Panels separated by handles you drag or move with the arrow keys.
Example
import {
ResizablePanelGroup, ResizablePanel, ResizableHandle, ResizableGrid, ResizableCell,
ResizableGridline,
} from "@elements/ui/resizable";
<ResizablePanelGroup orientation="horizontal" autoSaveId="mail">
<ResizablePanel defaultSize="25%" minSize="15%" collapsible>Folders</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel>Messages</ResizablePanel>
</ResizablePanelGroup>
<ResizablePanelGroup layout={state.layout}>...</ResizablePanelGroup>
<ResizablePanel collapsible collapsed={state.sideClosed}>...</ResizablePanel>
<ResizableGrid columns={3} rows={2}>
<ResizableCell column={0} row={0} columnSpan={2}>Header</ResizableCell>
<ResizableCell column={2} row={0} rowSpan={2}>Inspector</ResizableCell>
<ResizableCell column={0} row={1}>Files</ResizableCell>
<ResizableCell column={1} row={1}>Editor</ResizableCell>
<ResizableGridline type="row" row={1} columnSpan={2} />
<ResizableGridline type="column" column={1} row={1} />
<ResizableGridline type="column" column={2} />
</ResizableGrid>
Parts
| Part |
What it is |
ResizablePanelGroup |
The flex row or column. Owns the layout: each panel's percent of the free space. |
ResizablePanel |
A region with a size, limits and optional collapse. |
ResizableHandle |
The separator between two panels: drag it or focus it and use the keys. |
.resizable-grip |
The visible grip withHandle renders. |
ResizableGrid |
A CSS grid whose columns and rows both resize. Owns a layout per axis. |
ResizableCell |
Occupies one or more columns and rows. |
ResizableGridline |
Resizes the columns or rows on either side of one boundary. Where a column and a row gridline cross, a drag resizes both. |
Attributes
ResizablePanelGroup
| Attribute |
Type |
Default |
Description |
orientation |
"horizontal" | "vertical" |
"horizontal" |
Panels side by side, or stacked. |
layout |
number[] (two-way) |
[] |
Every panel's percent, summing to 100. Bind it to read the layout or set it; empty uses the panels' defaults. |
disabled |
boolean |
false |
No handle resizes. |
disableCursor |
boolean |
false |
Leave the page cursor alone while dragging. |
keyboardStep |
number |
10 |
Percent an arrow key moves a handle. |
autoSaveId |
string |
"" |
Remembers the layout in localStorage under this id. |
dir |
"ltr" | "rtl" | "" |
"" |
Reading direction; empty inherits. |
onLayoutChange |
(layout: number[]) => void |
() => {} |
Every change, including each pointer move. |
onLayoutChanged |
(layout: number[], meta: { isUserInteraction: boolean; requestedLayout: number[] }) => void |
() => {} |
Once a drag ends, a key moves a handle, or code sets the layout. requestedLayout is the layout before constraints; save that one. |
resizePreviewMode |
"panel" | "separator" |
"panel" |
separator: a drag moves only the separator, and the panels resize on release. |
ResizablePanel
| Attribute |
Type |
Default |
Description |
id |
string |
generated |
Keys the saved layout and the handle's aria-controls. |
defaultSize |
number | string |
even share |
Starting size. A number is px; a string takes px, %, rem, em, vh, vw; a bare string is percent. |
minSize |
number | string |
"0%" |
Smallest size. |
maxSize |
number | string |
"100%" |
Largest size. |
collapsible |
boolean |
false |
Pushed past its threshold below minSize, it collapses to collapsedSize. |
collapsedSize |
number | string |
"0%" |
Size while collapsed. |
collapsedThreshold |
number | string |
half of min minus collapsed |
How far past minSize it must go to collapse, and be pulled to expand. |
collapsed |
boolean (two-way) |
false |
Whether it is collapsed. Set it to collapse or expand; the group writes it back as the user drags. |
disabled |
boolean |
false |
Neither resized nor pushed by its neighbors. |
groupResizeBehavior |
"preserve-relative-size" | "preserve-pixel-size" |
"preserve-relative-size" |
When the group resizes, keep this panel's percent or its px. |
onResize |
(size: PanelSize, id: string, prevSize: PanelSize | undefined) => void |
() => {} |
Its size changed, as { asPercentage, inPixels }; prevSize is undefined on the first layout. |
onCollapse / onExpand |
() => void |
() => {} |
It collapsed or expanded. |
ResizableHandle
| Attribute |
Type |
Default |
Description |
withHandle |
boolean |
false |
Shows a grip on the hairline. |
disabled |
boolean |
false |
This handle does not resize. |
fixed |
boolean |
false |
APG fixed size splitter: no arrow keys or dragging; Enter or a click toggles the panel before it between its size and collapsed. |
disableDoubleClick |
boolean |
false |
A double click no longer resets the panel before it to its default size. |
label |
string |
"Resize" |
The separator's accessible name. Pass "" with attrs aria-labelledby to name it by the pane's visible label. |
preview |
"panel" | "separator" | "" |
"" |
Overrides the group's resizePreviewMode for this handle. |
onDragging |
(dragging: boolean) => void |
() => {} |
A pointer drag started or ended. |
ResizableGrid
| Attribute |
Type |
Default |
Description |
columns, rows |
number | TrackSpec[] |
2, 1 |
A track count, or one { id, defaultSize, minSize, maxSize, collapsible, collapsedSize, collapsedThreshold, disabled } per track, in the panel units. |
layout |
{ columns: number[]; rows: number[] } (two-way) |
{ columns: [], rows: [] } |
Every track's percent per axis. Its initial value is the default layout; bind it to read or set. |
disabled, disableCursor, keyboardStep, autoSaveId, dir |
as on the group |
false, false, 10, "", "" |
As on ResizablePanelGroup; disableCursor also drops the gridlines' resize cursors. |
onLayoutChange / onLayoutChanged |
(layout, meta?) => void |
() => {} |
Every change, and once a drag ends, a key moves a gridline or code sets the layout. |
ResizableCell
| Attribute |
Type |
Default |
Description |
column, row |
number |
0 |
The first column and row it occupies, from 0. |
columnSpan, rowSpan |
number |
1 |
How many tracks it spans. |
ResizableGridline
| Attribute |
Type |
Default |
Description |
type |
"column" | "row" |
"column" |
A column gridline is vertical and resizes columns; a row gridline resizes rows. |
column, row |
number |
0 |
The boundary it sits on (the leading edge of that track) and the first track of the other axis it runs beside. |
rowSpan / columnSpan |
number |
0 (all) |
How many tracks it runs beside. Never beside a cell that spans its boundary. |
withHandle, disabled, disableDoubleClick, label, onDragging |
as on the handle |
as on the handle |
As on ResizableHandle. |
all
| Attribute |
Type |
Default |
Description |
class, attrs |
string, Record |
"", {} |
Extra classes and attributes on the element. |
Slots
| Slot |
Part |
Content |
default |
ResizablePanelGroup |
Panels and handles, alternating. |
default |
ResizablePanel |
The panel's content; may hold another group. |
Keyboard
| Keys |
Action |
ArrowLeft / ArrowRight |
Horizontal group: moves the focused handle by keyboardStep. |
ArrowUp / ArrowDown |
Vertical group: moves the focused handle by keyboardStep. |
Home |
The panel before the handle to its smallest size, collapsed if it can. |
End |
The panel before the handle to its largest size. |
Enter |
Collapses the panel before the handle, or restores it. |
Arrows, Home, End, Enter (gridline) |
A column gridline takes Left and Right, a row gridline Up and Down; Home, End and Enter act on the track before it. |
F6 / Shift + F6 |
Moves focus through the window panes: from a handle to the pane after it (Shift: before it), from a pane to the next or previous pane. |
Data attributes
| Attribute |
Values |
On |
Description |
data-orientation |
"horizontal" | "vertical" |
root |
|
data-group, data-panel-group |
present |
root |
|
data-panel-size |
percent |
.resizable-panel |
|
data-collapsible |
present |
.resizable-panel |
|
data-collapsed |
present |
.resizable-panel |
While collapsed. |
data-state |
"inactive" | "hover" | "drag" |
.resizable-handle |
|
data-preview |
present |
.resizable-handle |
While a separator preview is shown (resizePreviewMode separator). |
data-dragging |
present |
.resizable-handle |
While dragged. |
data-disabled |
present |
root, .resizable-panel, .resizable-handle |
|
data-fixed |
present |
.resizable-handle |
A fixed size splitter. |
data-edge-hover, data-edge-dragging |
present |
root |
The pointer is on, or dragging, an edge where two panels meet without a separator. |
data-grid |
present |
.resizable-grid |
|
data-cursor |
"move" | "col-resize" | "row-resize" |
.resizable-grid |
The pointer is on a resize target; move where two axes meet. |
data-dragging |
present |
.resizable-grid |
While a pointer drag runs. |
data-cell, data-column, data-row, data-column-span, data-row-span |
present, index |
.resizable-cell |
|
data-type |
"column" | "row" |
.resizable-gridline |
|
data-state |
"inactive" | "hover" | "drag" |
.resizable-gridline |
|
Accessibility
- Each handle is a focusable separator (WAI-ARIA window splitter) with aria-orientation, aria-controls naming the panel before it, and aria-valuenow, aria-valuemin and aria-valuemax in percent.
- Arrow keys move it, Home and End go to the limits, Enter collapses and restores, F6 moves between the window panes. A fixed handle takes only Enter. Separators are optional: without one, the edge between two panels is the drag target.
- The focus ring is a box-shadow on the hairline; the hit area is wider than the line, wider still on touch screens.
- A grid's gridlines are separators too, with aria-controls naming the cells on their leading side. F6 moves through the cells.
See also