Overview
react-resizable-panels v4 is a major version upgrade. Core changes: component renaming (ARIA alignment), size unit system refactor (numbers change from percentages to pixels), removal of order prop, removal of onCollapse/onExpand. This skill covers all key paths of the migration.
Migration Steps Overview
Step 1: Installation
pnpm add react-resizable-panels@4
# 或
npm install react-resizable-panels@4
Step 2: Component Renaming
| v3 | v4 | Description |
|---|---|---|
PanelGroup |
Group |
Container component renaming |
PanelResizeHandle |
Separator |
Separator renaming, aligned with ARIA separator role |
Panel |
Panel |
Unchanged |
- import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
+ import { Panel, Group, Separator } from 'react-resizable-panels';
- <PanelGroup direction='horizontal'>
+ <Group orientation='horizontal'>
<Panel />
- <PanelResizeHandle />
+ <Separator />
<Panel />
- </PanelGroup>
+ </Group>
Step 3: Type Renaming
| v3 type | v4 type | Description |
|---|---|---|
PanelResizeHandleProps |
SeparatorProps |
Renamed along with the component |
PanelOnCollapse |
Removed | Use onResize instead |
PanelOnExpand |
Removed | Use onResize instead |
ImperativePanelGroupHandle |
GroupImperativeHandle |
Renamed |
ImperativePanelHandle |
PanelImperativeHandle |
Renamed |
(new) PanelSize |
{ asPercentage: number; inPixels: number } |
New type |
- import type { PanelResizeHandleProps } from 'react-resizable-panels';
+ import type { SeparatorProps } from 'react-resizable-panels';
- import type { PanelOnCollapse, PanelOnExpand } from 'react-resizable-panels';
+ // 已移除,改用 Panel 的 onResize 回调
Step 4: Props Changes
Group (formerly PanelGroup)
| v3 Prop | v4 Prop | Description |
|---|---|---|
direction |
orientation |
Renamed, aligned with the ARIA standard |
autoSaveId |
Removed | Use the useDefaultLayout hook instead |
ref={groupRef} |
groupRef={groupRef} |
ref changed to a named prop |
onLayout |
onLayoutChange / onLayoutChanged |
Renamed and split |
- <PanelGroup direction='horizontal' autoSaveId='my-layout' ref={groupRef}>
+ <Group orientation='horizontal' groupRef={groupRef}>
Panel
| v3 Prop | v4 Prop | Description |
|---|---|---|
order |
Removed | order is no longer needed for conditional rendering |
onCollapse |
Removed | Use onResize instead |
onExpand |
Removed | Use onResize instead |
ref={panelRef} |
panelRef={panelRef} |
ref changed to a named prop |
- <Panel id='left' order={1} onCollapse={handleCollapse} onExpand={handleExpand}>
+ <Panel id='left' onResize={(nextSize, id, prevSize) => {
+ if (prevSize) {
+ const wasCollapsed = prevSize.asPercentage === 0;
+ const isCollapsed = nextSize.asPercentage === 0;
+ if (isCollapsed && !wasCollapsed) handleCollapse();
+ if (!isCollapsed && wasCollapsed) handleExpand();
+ }
+ }}>
Step 5: Size Unit Conversion (Critical!)
v3: All size values are numbers, implicitly representing percentages. v4: Numbers represent pixels; percentages require explicit strings.
Supported units:px、%、em、rem、vh、vw
- <Panel defaultSize={30} minSize={20} maxSize={60}>
+ <Panel defaultSize='30%' minSize='20%' maxSize='60%'>
- <Panel collapsedSize={5} collapsible>
+ <Panel collapsedSize='5%' collapsible>
Warning: If you upgrade directly without changing size values, all numbers will change from "percentages" to "pixels", and the layout will be completely broken!
Quick Migration Rules: After all numeric values of defaultSize、minSize、maxSize、collapsedSize , add % and convert to a string.
Step 6: CSS Data Attribute Updates
v4 simplifies the data attribute system:
| v3 attribute | v4 attribute | Description |
|---|---|---|
data-panel-group-direction="horizontal" |
aria-orientation="vertical" |
Semantics flipped! v3=group direction, v4=separator direction |
data-panel-group-direction="vertical" |
aria-orientation="horizontal" |
Semantics flipped! horizontal separator in a vertical group |
data-resize-handle-state="hover" |
data-separator="hover" |
Simplified |
data-resize-handle-state="drag" |
data-separator="active" |
drag → active |
data-resize-handle-state="inactive" |
data-separator="inactive" |
Simplified |
data-resize-handle-active |
data-separator="active" |
Merged |
Separator's data-separator attribute values: inactive、hover、active、disabled
Key Pitfall: v3's data-panel-group-direction isthe group's direction, v4's aria-orientation isthe separator's own direction. The semantics are completely flipped!
- Horizontal group (
orientation="horizontal"): the separator isvertical→aria-orientation="vertical"→ requireswidth - Vertical group (
orientation="vertical"): the separator ishorizontal→aria-orientation="horizontal"→ requiresheight
/* CSS 选择器更新 — 注意语义翻转! */
- [data-panel-group-direction="horizontal"] { width: 1px; cursor: col-resize; }
+ [aria-orientation="vertical"] { width: 1px; cursor: col-resize; }
- [data-panel-group-direction="vertical"] { height: 1px; cursor: row-resize; }
+ [aria-orientation="horizontal"] { height: 1px; cursor: row-resize; }
- [data-resize-handle-state="hover"] { ... }
+ [data-separator="hover"] { ... }
- [data-resize-handle-state="drag"] { ... }
+ [data-separator="active"] { ... }
- [data-resize-handle-active] { ... }
+ [data-separator="active"] { ... }
/* Tailwind / 内联 data 属性 */
- className='data-[resize-handle-state=inactive]:opacity-20'
+ className='data-[separator=inactive]:opacity-20'
Step 7: Hook changes
- import { useRef } from 'react';
- const panelRef = useRef<ImperativePanelHandle>(null);
- const groupRef = useRef<ImperativePanelGroupHandle>(null);
+ import { usePanelRef, useGroupRef } from 'react-resizable-panels';
+ const panelRef = usePanelRef();
+ const groupRef = useGroupRef();
// autoSaveId 替代方案
+ import { useDefaultLayout } from 'react-resizable-panels';
+ const defaultLayout = useDefaultLayout({ groupId: 'my-layout', storage: localStorage });
Step 8: Other removals
// disableGlobalCursorStyles() 全局函数已移除
- import { disableGlobalCursorStyles } from 'react-resizable-panels';
- disableGlobalCursorStyles();
+ // 改为 Group 上的 prop
+ <Group disableCursor orientation='horizontal'>
Quick troubleshooting for common errors
| Symptom | Cause | Fix |
|---|---|---|
PanelGroup is not exported |
Component renamed | Replace with Group |
PanelResizeHandle is not exported |
Component renamed | Replace with Separator |
direction is not a valid prop |
prop renamed | Replace with orientation |
| Layout completely broken (panels extremely small/large) | Size unit changed | Add % suffix to numbers to convert to strings |
| CSS hover/drag styles not working | data attribute changed | Update CSS selectors |
order is not a valid prop |
prop removed | Delete directly order |
onCollapse is not a valid prop |
prop removed | Switch to onResize |
autoSaveId is not a valid prop |
prop removed | Switch to useDefaultLayout |
PanelResizeHandleProps not found |
Type renamed | Replace with SeparatorProps |
Migration checklist
- Install v4:
react-resizable-panels@4 -
PanelGroup→Group -
PanelResizeHandle→Separator -
direction→orientation - Add
%suffix to all size numbers to convert to strings - Remove all
orderprop -
onCollapse/onExpand→onResizecallbacks -
ref→panelRef/groupRefnamed props -
autoSaveId→useDefaultLayouthook - Update CSS:
data-panel-group-direction→aria-orientation - Update CSS:
data-resize-handle-state→data-separator - Update type imports:
PanelResizeHandleProps→SeparatorProps - Run typecheck to verify
- Visually verify layout and drag interactions
Complete migration example
Before (v3)
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
function MyLayout() {
return (
<PanelGroup direction='horizontal'>
<Panel defaultSize={30} minSize={10} order={1}>
<Sidebar />
</Panel>
<PanelResizeHandle className='w-1 bg-gray-300 hover:bg-blue-400' />
<Panel defaultSize={70} minSize={30} order={2}>
<Content />
</Panel>
</PanelGroup>
);
}
After (v4)
import { Panel, Group, Separator } from 'react-resizable-panels';
function MyLayout() {
return (
<Group orientation='horizontal'>
<Panel defaultSize='30%' minSize='10%'>
<Sidebar />
</Panel>
<Separator className='w-1 bg-gray-300 hover:bg-blue-400' />
<Panel defaultSize='70%' minSize='30%'>
<Content />
</Panel>
</Group>
);
}