Resizable
Accessible resizable panel groups and layouts.
Resizable: side-by-side panels with a handle to adjust their size (VS Code style). The group is horizontal or vertical. Each Panel has a defaultSize (%) and optionally a minSize. Resizing is done by dragging the handle with a mouse, or with the arrow keys when the handle has the focus.
Installation
$ bext ui add resizable
Copy and paste the following into src/components/ui/resizable.tsx.
This component is interactive. Make sure the shared island is loaded (see Islands). It reacts to data-ui="resizable" hooks.
Usage
Anatomy
<ResizablePanelGroup direction="horizontal">
├── <ResizablePanel defaultSize={50} minSize={20}>left</ResizablePanel>
├── <ResizableHandle />
└── <ResizablePanel defaultSize={50}>right</ResizablePanel>API Reference
ResizablePanelGroup
| Prop | Type | Default | Description |
|---|---|---|---|
| direction | "horizontal" | "vertical" | "horizontal" | Direction of the split. |
| children | ReactNode | — | ResizablePanels separated by ResizableHandles. |
| className | string | — | Extra classes on the group (flex, h-full w-full overflow-hidden). |
ResizablePanel
| Prop | Type | Default | Description |
|---|---|---|---|
| defaultSize | number | 50 | Initial size in % (0-100), applied as a flex-grow ratio. |
| minSize | number | 10 | Minimum size in % while dragging. |
| children | ReactNode | — | Panel content. The panel scrolls when the content overflows. |
ResizableHandle
| Prop | Type | Default | Description |
|---|---|---|---|
| withHandle | boolean | false | Shows a small visual grip in the center of the handle. |
| ariaLabel | string | "Resize panels" | Accessible name of the separator. |
| direction | "horizontal" | "vertical" | "horizontal" | Direction of the parent group, used for the server-rendered aria-orientation. The island corrects it from the group, so this only matters before the island runs. |
| className | string | — | Extra classes on the 1px separator line. |
Data Attributes
Attributes you can target in custom CSS to override the style for each state.
| Attribute | On | Values | Description |
|---|---|---|---|
| data-ui="resizable" | PanelGroup root | — | Scope used by the island to measure the group while dragging. |
| data-direction | PanelGroup root | "horizontal" | "vertical" | Reflects the direction prop. The handle orientation and cursor follow it. |
| data-ui-rz-handle | ResizableHandle | — | A mousedown on it starts the drag between the previous and the next panel; the arrow keys resize the same two panels. |
| aria-valuenow / aria-valuemin / aria-valuemax | ResizableHandle | 0 to 100 | Size of the panel before the handle, in percent of the group, and its bounds. Set by the island and updated on every resize. |
| data-ui-rz-panel | ResizablePanel | data-min-size="…" | Marks a panel. data-min-size carries the minSize prop. |
Keyboard
| Key | Action |
|---|---|
| Tab | Focuses each handle. |
| Arrow Left / Right | Horizontal group: shrinks or grows the panel before the handle by 5% of the group. |
| Arrow Up / Down | Vertical group: same, on the vertical axis. |
| Home / End | Sets the panel before the handle to its minimum / maximum size. |
Accessibility
The handle is a focusable role="separator" (tabindex="0") named "Resize panels" (ariaLabel to change it). aria-orientation is perpendicular to the group: "vertical" for a horizontal group. The island sets aria-valuenow, aria-valuemin and aria-valuemax to the size of the panel before the handle, in percent of the group, and updates them after a key press or a drag. These three attributes are absent until the island has run.
The minimum is the minSize of the panel before the handle; the maximum leaves the minSize of the panel after it. There is no touch drag: on a touch screen the panels keep their default size, so make sure every panel is usable at that size.
Edit this component live in the bext playground. The PRISM + signals version is the bext-native idiom: fine-grained reactivity, no virtual DOM.
On This Page