Rime

Resizable

Panels split by draggable hairline handles, side by side or stacked, with keyboard resizing and minimum sizes.

ResizablePanelGroup holds ResizablePanel and ResizableHandle children, alternating. Sizes are percentages applied as flex-grow proportions, so handles keep their pixel width and nothing overflows. Each panel can set defaultSize and minSize; onLayout reports every size after a change, for saving the split.

Drag a handle with the pointer, or focus it and use the arrow keys (5%, Shift for 10%) and Home or End. withGrip draws a small grip on the rule. framed (default) puts the group on a glass panel.

Installation

npx shadcn@latest add KezLahd/rime/resizable

Also installs: base.

Usage

import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/components/ui";

<ResizablePanelGroup height={320}>
  <ResizablePanel defaultSize={30} minSize={20}>Projects</ResizablePanel>
  <ResizableHandle withGrip />
  <ResizablePanel>Details</ResizablePanel>
</ResizablePanelGroup>

Examples

Three panels

Drag a handle, or focus it and use the arrow keys.
Projects
Invoice INV-004213
Details

Vertical

Editor
Console

API

Generated from the component's TypeScript types and JSDoc (scripts/gen-ui-registry.mjs), so it always matches the source.

ResizablePanelGroup

ResizablePanelGroup props
PropTypeDefaultDescription
direction"horizontal" | "vertical""horizontal"horizontal = panels side by side (drag left and right). Default horizontal.
childrenRequiredReactNodeResizablePanel and ResizableHandle children, alternating.
onLayout(sizes: number[]) => voidCalled with every panel's size, in percent, after each change.
heightnumber | stringGroup height for vertical groups (or any fixed frame), e.g. 320.
framedbooleantrueGlass frame around the group with hairline handles. Default true.
classNamestring

ResizablePanel

ResizablePanel props
PropTypeDefaultDescription
defaultSizenumberStarting size in percent of the group. Unset panels share what is left.
minSizenumberSmallest size in percent. Default 10.
childrenReactNode
classNamestring

ResizableHandle

ResizableHandle props
PropTypeDefaultDescription
aria-labelstringAccessible name. Defaults to "Resize".
withGripbooleanShows a small grip on the rule.
classNamestring

Theme tokens

Per-component tokens are undeclared by default, so the default shown is the fallback the CSS uses. Set one at :root (or in theme.css) to restyle every instance. Live values are read from this page, so they follow the Rime Studio.

Resizable per-component tokens
TokenDefaultLive value
--resizable-min-height200px…
--resizable-radiusvar(--r-lg)…

System tokens it reads

Resizable system tokens
TokenDefaultLive value
--blur-panel-lighttokens.css…
--border-wtokens.css…
--brandtokens.css…
--durtokens.css…
--easetokens.css…
--glass-edgetokens.css…
--glass-fill-nestedtokens.css…
--linetokens.css…
--line-inputtokens.css…
--r-2xstokens.css…
--r-lgtokens.css…
--rgb-brandtokens.css…
--shadow-hairline-cardtokens.css…
--shadow-inputtokens.css…
--shadow-speculartokens.css…
--surface-solidtokens.css…

Accessibility

  • Each handle is a focusable role="separator" with aria-valuenow (the size of the panel before it), aria-valuemin, aria-valuemax and aria-controls; aria-orientation is perpendicular to the group.
  • Arrow keys resize in 5% steps (10% with Shift); Home and End jump to the limits.
  • Give each handle an aria-label that says what it resizes when there is more than one.

Do and don't

Do

  • Set minSize so no panel can be dragged shut by accident.
  • Save the split from onLayout so it survives a reload.

Don't

  • Don't use it for layouts people never need to change; fixed columns are simpler.

For AI agents

This page as markdown, rendered from the same registry entry. Point an agent at it (or add .md to this page's URL), or at /llms.txt for the index.

Open resizable.md