ResizablePanels
A fluid center column with optional, collapsible, user-resizable side panels.
Use ResizablePanels for desktop application shells, editors, inboxes, and workspaces where side content should resize without squashing the primary column. The primitive controls layout and resizing only. Consumers retain ownership of each panel's open state and collapse controls.
Projects
Details
- Status
- In progress
- Owner
- Grace Hopper
July release
The center column remains fluid while either side panel changes.
Installation
The copied source is available in the registry JSON. The canonical implementation lives in packages/react/src/components/resizable-panels.tsx.
Usage
const [leftOpen, setLeftOpen] = useState(true);
const [leftWidth, setLeftWidth] = usePanelWidth(
"workspace-left-panel",
275,
240,
520,
);
<ResizablePanels centerFloor={320}>
<SidePanel
side="left"
open={leftOpen}
width={leftWidth}
min={240}
max={520}
onResize={setLeftWidth}
>
<ProjectNavigation />
</SidePanel>
<ResizeHandle side="left" />
<main>{children}</main>
</ResizablePanels>Place SidePanel and ResizeHandle elements directly inside ResizablePanels. Other direct children are grouped into the fluid center track. A handle is rendered only while its matching side panel is open.
API reference
| Prop | Type | Default | Description |
|---|---|---|---|
| ResizablePanels.centerFloor | number | - | Minimum center-track width in pixels. |
| SidePanel.side | "left" | "right" | - | Pins the fixed-width panel content to an outer edge. |
| SidePanel.open | boolean | - | Expands or collapses the panel track. The consumer owns this state. |
| SidePanel.width | number | - | Controlled panel width in pixels. |
| SidePanel.min / max | number / number | - | Bounds resizing. The group tightens the effective maximum to preserve centerFloor. |
| SidePanel.onResize | (width: number) => void | - | Receives clamped pointer and keyboard resize values. |
| ResizeHandle.side | "left" | "right" | - | Connects the separator to the matching SidePanel. |
| usePanelWidth | (key, default, min, max) => [width, setWidth] | - | Clamps and persists a controlled width in localStorage. |
Behavior
- Opening and closing animates the grid tracks with the panel motion tokens. The fixed-width content stays pinned to the outer edge and is revealed instead of compressed.
- Pointer dragging is clamped to the panel's effective range. Dragging does not collapse a panel.
- While dragging,
body.resizingdisables track transitions, selection, and non-resize cursors so the boundary follows the pointer directly. - Applications should close one or both panels when the viewport cannot accommodate their minimum widths plus centerFloor.
prefers-reduced-motionsets the panel duration to 0ms and removes its easing.
Accessibility
ResizeHandle is a focusable vertical separator with aria-valuenow, aria-valuemin, aria-valuemax, and a pixel-based aria-valuetext. Arrow Right expands a left panel and contracts a right panel. Arrow Left performs the inverse. Toggle buttons that control open should expose their state with aria-pressed or aria-expanded.