Resizable — spec¶
Synthesized from shadcn-ui
resizable. Resizable panel groups for IDE-style layouts. Distinct fromSplitter(covered separately) — Resizable here is the shadcn primitive.
When to use¶
- IDE-style two-pane layouts (sidebar | editor).
- Three-pane layouts (sidebar | editor | preview).
- Vertical splits (top pane / bottom pane — terminal below editor).
- Any UI where users want to manually adjust pane sizes.
When NOT to use: - Simple two-column layouts that don't need user-resize (use Grid). - Mobile (drag handles awkward on touch).
Anatomy¶
┌────────┬─────────────────────────┐
│ Side │ │
│ panel ║ Main editor pane │
│ ║ │
│ ║ │
└────────┴─────────────────────────┘
↑ draggable handle (vertical bar)
API¶
<Resizable.Group direction="horizontal">
<Resizable.Panel defaultSize={25} minSize={15}>
<Sidebar />
</Resizable.Panel>
<Resizable.Handle />
<Resizable.Panel defaultSize={75}>
<Resizable.Group direction="vertical">
<Resizable.Panel defaultSize={70}>
<Editor />
</Resizable.Panel>
<Resizable.Handle />
<Resizable.Panel defaultSize={30} minSize={20} collapsible>
<Terminal />
</Resizable.Panel>
</Resizable.Group>
</Resizable.Panel>
</Resizable.Group>
Composition¶
| Part | Purpose |
|---|---|
Resizable.Group |
Wrapper; direction = "horizontal" or "vertical" |
Resizable.Panel |
Single resizable pane |
Resizable.Handle |
Draggable divider |
| Panel prop | Type | Description |
|---|---|---|
defaultSize |
number |
Initial size as percentage |
minSize |
number |
Minimum size % |
maxSize |
number |
Maximum size % |
collapsible |
boolean |
Allow collapsing to 0 |
collapsedSize |
number |
Size when collapsed (default 0) |
onResize |
(size) => void |
Callback during drag |
onCollapse / onExpand |
function | State callbacks |
States¶
| State | Visual |
|---|---|
| Default | Handles visible (subtle) |
| Handle hover | Highlights (cursor: col-resize / row-resize) |
| Handle dragging | Active visual; user dragging |
| Panel collapsed | Width 0; chevron to expand |
| Panel min reached | Handle stops at min boundary |
Tokens consumed¶
--resizable-handle-bg
--resizable-handle-bg-hover
--resizable-handle-bg-active
--resizable-handle-thickness (typically 4-8px)
--motion-fast
Accessibility¶
- Handle:
role="separator" aria-orientation="vertical|horizontal" aria-valuenow={size} aria-valuemin={minSize} aria-valuemax={maxSize}. - Keyboard:
- Tab to focus handle.
- Arrow keys (Left/Right or Up/Down) move handle by 5%.
- Home / End: snap to min / max.
- Enter (when collapsible): toggle collapse.
- Don't make handle thinner than 4px — hard to grab.
Layout persistence¶
Save panel sizes to localStorage so they persist across sessions:
const [sizes, setSizes] = useLocalStorage("layout-sizes", [25, 75]);
<Resizable.Group
direction="horizontal"
onLayout={(newSizes) => setSizes(newSizes)}
>
...
</Resizable.Group>
Code example — IDE shell¶
function IDEShell() {
return (
<Resizable.Group direction="horizontal" className="h-screen">
<Resizable.Panel defaultSize={20} minSize={15} collapsible>
<FileExplorer />
</Resizable.Panel>
<Resizable.Handle />
<Resizable.Panel defaultSize={60}>
<Resizable.Group direction="vertical">
<Resizable.Panel defaultSize={70}>
<Editor />
</Resizable.Panel>
<Resizable.Handle />
<Resizable.Panel defaultSize={30} minSize={10}>
<Terminal />
</Resizable.Panel>
</Resizable.Group>
</Resizable.Panel>
<Resizable.Handle />
<Resizable.Panel defaultSize={20} minSize={15} collapsible>
<Outline />
</Resizable.Panel>
</Resizable.Group>
);
}
Don't¶
- Don't ship without min/max sizes — users drag to 0 and lose content.
- Don't make handles invisible — affordance must be visible.
- Don't auto-resize on window resize without preserving user adjustments.
- Don't use Resizable on mobile — touch drag conflicts with scroll.
References¶
- shadcn-ui:
resizable— wrapsreact-resizable-panels - VS Code, Linear, Cursor — IDE layout pattern