SuperAPI AI Directory

react-resizable-panels-v3-to-v4

Frontend & websites

View repository

Updated 10-16 09:52

About this skill

react-resizable-panels v3 → v4 Migration Guide

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" → requires width
  • Vertical group (orientation="vertical"): the separator ishorizontal→ aria-orientation="horizontal" → requires height
  /* 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 order prop
  • onCollapse/onExpand → onResize callbacks
  • ref → panelRef/groupRef named props
  • autoSaveId → useDefaultLayout hook
  • 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>
  );
}