Resizable

Split panes the user can resize or collapse.

Filesapp/page.tsxapp/layout.tsxpackages/ui/src/lib/utils.ts
Editor
Terminal

Installation

pnpm dlx shadcn@latest add @paneui/resizable

Usage

Composition

Examples

Collapsible

Use for sidebars that can be dragged shut or toggled from a button.

With Handle

Use where the divider is easy to miss.

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.

API Reference

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) and reset().
PropTypeDefaultDescription
direction"horizontal" | "vertical""horizontal"Lay panels out in a row or a column.
autoSaveIdstring—Persist the layout under this key.
onLayout(sizes: number[]) => void—Called with percentages whenever the layout settles.
handleRefRef<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 attributeDescription
data-draggingPresent 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 collapsedSize when dragged past half of minSize.
collapsedSize= 0
number | `${number}px`
Size when collapsed.
onCollapsedChange
(collapsed: boolean) => void
Called when the panel collapses or expands.
PropTypeDefaultDescription
idstring—Stable id for the imperative API and saved layouts.
defaultSizenumber | `${number}px`—Initial size.
minSizenumber | `${number}px`0Smallest size while dragging.
maxSizenumber | `${number}px`100Largest size while dragging.
collapsiblebooleanfalseSnap to collapsedSize when dragged past half of minSize.
collapsedSizenumber | `${number}px`0Size when collapsed.
onCollapsedChange(collapsed: boolean) => void—Called when the panel collapses or expands.
data-collapsed
Present while collapsed.
Data attributeDescription
data-collapsedPresent while collapsed.

ResizableHandle

Also accepts every prop of div.

variant= "line"
"line" | "hidden"
hidden drops 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.
PropTypeDefaultDescription
variant"line" | "hidden""line"hidden drops the resting hairline; the guide still shows on hover and drag.
withHandlebooleanfalseShow a grip in the middle.
keyboardStepnumber2Percent per arrow key press.
data-active
Present while this divider is dragged.
Data attributeDescription
data-activePresent while this divider is dragged.