Resizable
Split panes the user can resize or collapse.
Installation
pnpm dlx shadcn@latest add @paneui/resizableUsage
Composition
Examples
Sizes
Sizes are percentages of the group, or pixel strings like "240px" for defaultSize, minSize, maxSize and collapsedSize (converted with the group's current size). Panels without defaultSize share what is left.
Persistence
autoSaveId stores sizes and collapsed state in localStorage["resizable:<id>"] when a drag, key press or collapse settles. The saved layout is used only while the set of panel ids is unchanged.
Keyboard
The divider is a focusable separator. Arrow keys move it 2% (10% with Shift), Enter collapses or expands the collapsible neighbour, double click resets every panel to its default.
Motion
Panels follow the pointer exactly while dragging; collapse and expand ease flex-grow over 200ms, instant with reduced motion. The primary guide on the divider appears after a 150ms hover.
ResizablePanelGroup
Flex container that owns the sizes. Also accepts every prop of div.
- direction= "horizontal"
- "horizontal" | "vertical"
- Lay panels out in a row or a column.
- autoSaveId
- string
- Persist the layout under this key.
- onLayout
- (sizes: number[]) => void
- Called with percentages whenever the layout settles.
- handleRef
- Ref<ResizablePanelGroupHandle>
- Imperative handle with
collapse(id),expand(id),toggle(id),isCollapsed(id),getSizes(),setSizes(sizes)andreset().
| Prop | Type | Default | Description |
|---|---|---|---|
| direction | "horizontal" | "vertical" | "horizontal" | Lay panels out in a row or a column. |
| autoSaveId | string | — | Persist the layout under this key. |
| onLayout | (sizes: number[]) => void | — | Called with percentages whenever the layout settles. |
| handleRef | Ref<ResizablePanelGroupHandle> | — | Imperative handle with collapse(id), expand(id), toggle(id), isCollapsed(id), getSizes(), setSizes(sizes) and reset(). |
- data-dragging
- Present while a divider is dragged.
| Data attribute | Description |
|---|---|
| data-dragging | Present while a divider is dragged. |
ResizablePanel
Also accepts every prop of div.
- id
- string
- Stable id for the imperative API and saved layouts.
- defaultSize
- number | `${number}px`
- Initial size.
- minSize= 0
- number | `${number}px`
- Smallest size while dragging.
- maxSize= 100
- number | `${number}px`
- Largest size while dragging.
- collapsible= false
- boolean
- Snap to
collapsedSizewhen dragged past half ofminSize. - collapsedSize= 0
- number | `${number}px`
- Size when collapsed.
- onCollapsedChange
- (collapsed: boolean) => void
- Called when the panel collapses or expands.
| Prop | Type | Default | Description |
|---|---|---|---|
| id | string | — | Stable id for the imperative API and saved layouts. |
| defaultSize | number | `${number}px` | — | Initial size. |
| minSize | number | `${number}px` | 0 | Smallest size while dragging. |
| maxSize | number | `${number}px` | 100 | Largest size while dragging. |
| collapsible | boolean | false | Snap to collapsedSize when dragged past half of minSize. |
| collapsedSize | number | `${number}px` | 0 | Size when collapsed. |
| onCollapsedChange | (collapsed: boolean) => void | — | Called when the panel collapses or expands. |
- data-collapsed
- Present while collapsed.
| Data attribute | Description |
|---|---|
| data-collapsed | Present while collapsed. |
ResizableHandle
Also accepts every prop of div.
- variant= "line"
- "line" | "hidden"
hiddendrops the resting hairline; the guide still shows on hover and drag.- withHandle= false
- boolean
- Show a grip in the middle.
- keyboardStep= 2
- number
- Percent per arrow key press.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "line" | "hidden" | "line" | hidden drops the resting hairline; the guide still shows on hover and drag. |
| withHandle | boolean | false | Show a grip in the middle. |
| keyboardStep | number | 2 | Percent per arrow key press. |
- data-active
- Present while this divider is dragged.
| Data attribute | Description |
|---|---|
| data-active | Present while this divider is dragged. |