Resizable
A row or column of panels resized by dragging the handles between them — or, just as fully, with the arrow keys.
Sidebar
Content
Drag the divider, or focus it and press ← / →.
Usage#
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "neelam-ui";
<ResizablePanelGroup className="h-64">
<ResizablePanel defaultSize={30} minSize={15}>Sidebar</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel>Content</ResizablePanel>
</ResizablePanelGroup>Compose them alternating: panel, handle, panel, handle, panel. A handle resizes only its immediate previous and next sibling.
Sizing#
defaultSize, minSize, and maxSize are percentages of the group's main
axis. Omit defaultSize and a panel fills whatever is left over, split evenly
with any other unsized panels.
A vertical group needs an explicit height
Panels resolve their percentage sizes against the group's main-axis size, and
a flex container with no definite height cannot give them one. Set h-80,
h-screen, or similar on a direction="vertical" group. Width is not the
same problem — the group defaults to w-full, which is almost always what is
wanted.
Adjacency, not registration#
A handle reads minSize and maxSize straight back off its neighbours' own
data-* attributes rather than through context. A handle only ever needs to
know about its two immediate siblings, never the whole group — the same
approach Carousel takes reading its slides from
the DOM instead of an id-registration system.
Resizing by keyboard#
This is not a mouse-only control. ResizableHandle is a WAI-ARIA window
splitter: focusable, with role="separator", and fully operable from the
keyboard. Dragging is one input method, not the only one.
Try it above
Tab to the divider in the demo and press the arrow keys. If a resizable layout in your own app cannot do this, it is not finished.
Keyboard#
| Key | Behaviour |
|---|---|
| Tab | Moves focus to the handle. Each handle is its own tab stop. |
| ←→ | Shrinks or grows the previous panel, in a horizontal group. |
| ↑↓ | The same, in a vertical group. |
| Home | Collapses the previous panel to its minimum. |
| End | Expands the previous panel to its maximum. |
Accessibility#
- The handle is
role="separator"witharia-orientation, the WAI-ARIA window splitter pattern. aria-valuenowis required the moment aseparatorbecomes focusable, and is kept current as the panel resizes — along witharia-valueminandaria-valuemax, which reflect the neighbours' real bounds rather than a fixed 0–100.- The handle is a genuine tab stop (
tabIndex={0}), which is what makes the keyboard path work at all. - The grip icon (
withHandle) is decorative; the handle's own role and values carry the meaning.
API reference#
ResizablePanelGroup#
| Prop | Type | Default |
|---|---|---|
direction`"horizontal"` (panels side by side, the default) or `"vertical"` (stacked). | enum | horizontal |
ResizablePanel#
| Prop | Type | Default |
|---|---|---|
defaultSizeInitial size, as a percentage of the group's main axis. Omit to fill the space left over by sized siblings, split evenly with any other unsized panels. | number | — |
maxSizeMaximum size, as a percentage. Defaults to `100`. | number | 100 |
minSizeMinimum size, as a percentage. Defaults to `0`. | number | 0 |
ResizableHandle#
| Prop | Type | Default |
|---|---|---|
withHandleShows a small grip icon centered on the handle. Defaults to `false`. | boolean | false |