Resizable
The Resizable component splits a space into panels that users resize by dragging the handle between them.
When to Use
- for two views that share a screen, when users decide how much room each one gets, such as a list of clients next to their invoices
- as a nested group, to split one panel again in the other direction
When Not to Use
- for a split that never changes, as a handle is one more tab stop (lay out the two areas in a grid with a Separator between them)
- for an app’s navigation panel (use a Sidebar, which has its own trigger to collapse it)
- for a panel that slides over the page (use a Sheet)
- to scroll content that is longer than its frame (use a Scroll Area)
Anatomy
| Element | Usage |
|---|---|
| Group | Required |
| Panel | Required |
| Grip | Optional |
| Handle | Required for the keyboard |
| Focus outline | Keyboard focus only |
Panels and handles are direct children of their group. className on ResizablePanel is applied to a div inside the panel, so it can’t affect the panel’s own flex styles. That div has overflow: auto, so content that doesn’t fit a panel is clipped and scrolls.
States
If you set disabled on a handle, it is removed from the tab order and has aria-disabled. If you set disabled on the group, every handle is disabled.
Behavior
When users drag a handle, the panels on both sides are resized as the pointer moves. When users double-click a handle, the panel before it returns to its defaultSize.
Size
The group has h-full w-full, so it fills its container. Give the container a height, as the group has none of its own. If panels have no defaultSize, they share the space equally.
In defaultSize, minSize, and maxSize, a number is read as pixels and a bare string as a percentage.
| Value | Reading |
|---|---|
"40%" or "40" | 40% of the group |
{240} or "240px" | 240px |
"20rem", "50vh" | A length in em, rem, vh, or vw |
By default, minSize is 0%, so users can drag a panel shut. Set minSize to the size at which the panel’s content still fits.
minSize so that a panel’s content still fits at its narrowest.minSize is 0%, users can drag the panel down to a sliver, and its content is cut off.Saved Layout
A group doesn’t save its layout, so after a reload, panels return to their defaultSize. To save a layout between page loads, use the useDefaultLayout hook of react-resizable-panels.
Accessibility
The react-resizable-panels library renders each handle as a separator that can receive focus. The handle reports the size of the panel before it as a value from 0 to 100.
Keyboard
| Key | Result |
|---|---|
Tab | Moves focus to a handle; each handle is one tab stop |
ArrowLeft, ArrowRight | In a horizontal group, move the handle by 5% of the group’s width |
ArrowUp, ArrowDown | In a vertical group, move the handle by 5% of the group’s height |
Home | Shrinks the panel before the handle to its minSize, or collapses a collapsible panel |
End | Grows the panel before the handle to its maxSize |
Enter | Collapses a collapsible panel before the handle, or restores it |
F6, Shift+F6 | Move focus to the next or the previous handle of the same group, and wrap at its ends |
Requirements
A handle has no name. It needs an aria-label so that screen readers can announce it, as WCAG 2.2 SC 4.1.2 Name, Role, Value
requires. The Window Splitter pattern of the ARIA
Authoring Practices Guide names the handle after the panel it resizes, such as “Clients”.
Two neighboring panels need a ResizableHandle between them so that keyboard users can resize them. If there is no
handle, users can still drag the edge between the panels, but no key resizes them, which WCAG 2.2 SC 2.1.1 Keyboard doesn’t
allow.
When users resize a panel with a pointer, the only way is to drag the handle. Users who can’t drag need a way to do the same with single clicks, as WCAG 2.2 SC 2.5.7 Dragging Movements requires, and the keyboard doesn’t count as one. If a task depends on the split, add Buttons that call collapse, expand, or resize on a panel’s panelRef.
Installation
npx shadcn@latest add @summit/resizableUsage
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/components/ui/resizable';
<div className="h-64 rounded-xl border">
<ResizablePanelGroup>
<ResizablePanel defaultSize="40%">Clients</ResizablePanel>
<ResizableHandle aria-label="Clients" />
<ResizablePanel defaultSize="60%">Invoices</ResizablePanel>
</ResizablePanelGroup>
</div>;With a Grip
To show a grip in the middle of the line, set withHandle on ResizableHandle.
<ResizableHandle withHandle aria-label="Clients" />Vertical
To stack the panels, set orientation="vertical" on the group. The handle is then horizontal.
<ResizablePanelGroup orientation="vertical">
<ResizablePanel defaultSize="35%">Invoices</ResizablePanel>
<ResizableHandle aria-label="Invoices" />
<ResizablePanel defaultSize="65%">Payments</ResizablePanel>
</ResizablePanelGroup>Limit a Panel
To limit how far users can resize a panel, set minSize and maxSize on it.
<ResizablePanel defaultSize="40%" minSize="30%" maxSize="70%">
Clients
</ResizablePanel>Collapse a Panel
To allow a panel to collapse, set collapsible with a minSize. When users drag the panel past the halfway point between minSize and collapsedSize, it collapses. By default, collapsedSize is 0%.
<ResizablePanel defaultSize="40%" minSize="20%" collapsible>
Clients
</ResizablePanel>Nest a Group
A panel can contain another group. To split in the other direction, set orientation on the inner group.
<ResizablePanelGroup>
<ResizablePanel defaultSize="40%">Clients</ResizablePanel>
<ResizableHandle aria-label="Clients" />
<ResizablePanel defaultSize="60%">
<ResizablePanelGroup orientation="vertical">
<ResizablePanel defaultSize="35%">Invoices</ResizablePanel>
<ResizableHandle aria-label="Invoices" />
<ResizablePanel defaultSize="65%">Payments</ResizablePanel>
</ResizablePanelGroup>
</ResizablePanel>
</ResizablePanelGroup>API Reference
ResizableHandle adds one prop.
| Prop | Type | Default | Description |
|---|---|---|---|
withHandle | boolean | None | Shows a grip in the middle of the line |
Other props are passed to react-resizable-panels, where ResizablePanelGroup is Group, ResizablePanel is Panel, and ResizableHandle is Separator. See the react-resizable-panels documentation for orientation, defaultSize, minSize, maxSize, collapsible, panelRef, resizeTargetMinimumSize, and useDefaultLayout.