Resizable
ProSplit a surface into panels the user can drag to resize. No Radix primitive ships this, so the handle is hand-rolled: it carries role=separator, is arrow-key operable, and reports through aria-valuenow.
Installation#
Usage#
The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
Anatomy#
import {
ResizablePanelGroup,
ResizablePanel,
ResizableHandle,
} from "@/components/ui/resizable"
<ResizablePanelGroup>
<ResizablePanel>...</ResizablePanel>
<ResizableHandle />
<ResizablePanel>...</ResizablePanel>
</ResizablePanelGroup>Horizontal#
The default direction lays panels side-by-side. Give each panel a defaultSize weight (conventionally summing to 100), then drag the divider to redistribute the space between the two panels it sits between.
Vertical#
Set direction="vertical" to stack panels and drag a horizontal divider. The handle, cursor, and keyboard axis all flip automatically.
With handle#
Add withHandle to render a minimal, fully-rounded grip pill: a clear "drag me" affordance for wide gutters. Without it the divider is a hairline that highlights on hover and focus.
Custom handle#
Pass children to a ResizableHandle to swap the visual entirely (an icon nub, a brand chip, your own shape) while keeping all the drag, keyboard, and ARIA behavior. The two examples below show a dotted chip and a bold brand pill.
Min & max sizes#
Clamp how far a panel can be dragged with minSize and maxSize. A number (or "30%") is a proportional weight; a pixel string like "240px" is a hard bound pinned as a real CSS min/max-width, so it holds at any group size: drag it, then shrink your window and the sidebar below keeps its 240px–360px range instead of scaling away.
Collapsible#
Mark a panel collapsible and it snaps shut once dragged past half its minSize, settling at collapsedSize (default 0): the pattern for a sidebar that tucks away. Drag back out to restore it.
Nested dashboard#
Groups nest: put a vertical ResizablePanelGroup inside a panel of a horizontal one to build a real dashboard split. Each group resizes independently.
API reference#
ResizablePanelGroup
The split container: compose panels and handles as direct children, alternating panel, handle, panel. Give it a height via className (panels fill the box). Forwards all native <div> props; every part accepts className, merged last.
| Prop | Type | Default | Description |
|---|---|---|---|
| direction | "horizontal" | "vertical" | "horizontal" | Lay panels side by side or stacked. The handle, cursor and keyboard axis flip with it. |
| onLayout | (sizes: number[]) => void | - | Called with the panel weights (in DOM order) whenever a drag or key press changes the layout. |
| Data attribute | Description |
|---|---|
| data-direction | "horizontal" or "vertical", mirroring direction. |
ResizablePanel
One region of the split, sized by flex-grow weight. Forwards all native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| defaultSize | number | - | Starting weight, conventionally a percentage with all panels summing to 100. Omit it on every panel for an equal split. |
| minSize | number | string | - | Smallest drag size. A number (or "30%") is a proportional weight; a pixel string like "240px" is a hard bound pinned as real CSS, so it holds as the window resizes. |
| maxSize | number | string | - | Largest drag size, as a weight or a pixel string like "480px". |
| collapsible | boolean | - | Snaps the panel shut once dragged past half its minSize; drag back out to restore it. |
| collapsedSize | number | string | 0 | Size the panel settles at when collapsed, as a weight or a pixel string. |
| asChild | boolean | - | Render as the child element (Radix Slot) instead of a <div>. |
| Data attribute | Description |
|---|---|
| data-min-size | The minSize value, read by the handle during a drag. |
| data-max-size | The maxSize value. |
| data-collapsible | Present when collapsible. |
| data-collapsed-size | The collapsedSize value. |
ResizableHandle
The draggable divider between the two panels beside it, with role="separator" and live aria-valuenow. Arrow keys nudge 16px (Shift 48px), Home and End jump to the previous panel's min and max. Forwards all native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| withHandle | boolean | - | Shows the minimal rounded grip pill; without it the divider is a hairline that highlights on hover and focus. |
| children | React.ReactNode | - | Custom handle content centered on the divider. Replaces the withHandle pill while keeping the drag, keyboard and ARIA behavior. |
| disabled | boolean | - | Disables dragging and keyboard resizing, and takes the handle out of the tab order. |
| Data attribute | Description |
|---|---|
| data-dragging | "true" while a pointer drag is in progress. Style custom children with group-data-[dragging=true]/handle:. |
| data-disabled | "true" when disabled. |