Skip to content
WorkspaceResizableMenu

Resizable

Split panes with pointer and keyboard resizing, pixel or percent limits, collapsible panels and a hairline handle. No third-party dependency.

Nested horizontal and vertical splits with pixel minimums.

Installation

$ pnpm dlx shadcn@latest add https://chatcn.dev/r/resizable.json

Usage

tsx
import { ResizableGroup, ResizableHandle, ResizablePanel } from "@/components/ui/chatcn/resizable"

<ResizableGroup direction="horizontal" autoSaveId="workspace">
  <ResizablePanel id="chat" defaultSize={55} minSize="320px">
    <Conversation />
  </ResizablePanel>
  <ResizableHandle />
  <ResizablePanel id="artifact" defaultSize={45} minSize="360px" collapsible>
    <ArtifactPanel />
  </ResizablePanel>
</ResizableGroup>

Sizes are percentages of the group. Pass a string like "360px" for pixel limits; they are re-resolved whenever the group resizes, so a panel never drops below its pixel minimum on a narrow window. Panels can mount and unmount: a new panel takes its defaultSize, the others share the rest, and a panel that comes back returns at its last width.

Examples

Collapsible

A collapsible panel snaps shut once it is dragged past half its minimum. Enter on the handle toggles it, and panelRef gives you collapse(), expand() and resize() for a toolbar button.

A sidebar that collapses by drag, Enter or a button.

Props

ResizableGroup

PropTypeNotes
direction"horizontal" | "vertical"Defaults to horizontal
onLayout(sizes: number[]) => voidPercentages in DOM order, after every change
autoSaveIdstringPersists the layout in localStorage

ResizablePanel

PropTypeNotes
idstringStable id for persistence and aria-controls. Generated when omitted
defaultSizenumber | "Npx"Size on mount and on double-click reset
minSize, maxSizenumber | "Npx"Percent or pixels
collapsiblebooleanSnaps shut past half the minimum
collapsedSizenumber | "Npx"Defaults to 0
onCollapsedChange(collapsed: boolean) => void
panelRefRef<ResizablePanelHandle>collapse, expand, resize, isCollapsed, getSize

ResizableHandle

PropTypeNotes
withGripbooleanShows a small grip on the hairline
keyboardStepnumberPercent per arrow press, default 2. Shift moves five times as far
disabledboolean
aria-labelstringDefaults to Resize panels

Keyboard

KeyAction
← → (or ↑ ↓ when vertical)Resize by keyboardStep. Mirrored in RTL
Shift + arrowResize five times as far
Home, EndJump to the smallest or largest size the pair allows
EnterCollapse or restore the collapsible neighbor

Accessibility

  • The handle is a focusable separator with aria-valuenow, aria-valuemin, aria-valuemax and aria-controls, the APG window splitter pattern.
  • The range it reports already accounts for the panel across it, so screen readers hear the real limits.
  • The hit area is 9 px wide with a mouse and 24 px with a finger, while the visible line stays 1 px.

Motion

  • The hairline thickens to 3 px with a scale transform after a 100 ms hover intent, and at once while dragging or focused.
  • While dragging, one cursor applies everywhere and iframes stop catching the pointer. At a limit the cursor turns into a one-way arrow.
  • Resizing itself never animates: keyboard and drag changes are instant.