build69
build69  /  Components  /  Layout & Surfaces  /  Resizable

Resizable

v1.0.7NewUpdated 16 Oct 2026

Resizable split-pane layout built on react-resizable-panels v4. Compose ResizablePanelGroup → ResizablePanel + ResizableHandle for master-detail workspaces, collapsible sidebars, and IDE-like layouts. Handles are keyboar

Preview

Live · 9 stories
DefaultOpen ↗
▸Why this is recommendedresizesplit-panepanelslayout+4

The default, out-of-the-box Resizable.

Recommended for+14
  • ✓Master-detail workspaces (invoice list + preview)
  • ✓Collapsible app sidebars
  • ✓Dashboards with user-adjustable panes
  • ✓POS / editor split views
Tags+6
resizesplit-panepanelslayoutdragseparatorworkspacedefault
Features+4
  • Horizontal & vertical groups
  • Keyboard resize (Arrows step, Home/End snap, Enter toggles collapsible neighbor)
  • Full role="separator" ARIA (aria-valuenow/min/max, aria-controls, orientation)
  • min / max size constraints in px, %, rem, vh…
  • Collapsible panels with collapsedSize snap
  • Layout persistence via autoSaveId (localStorage or custom storage, SSR-safe)
  • Nested groups
  • Grip pill visual via withHandle + hover/active/focus token states
  • Imperative API: useResizablePanelRef (collapse/expand/resize), useResizableGroupRef (get/setLayout)
  • Double-click a handle to reset the panel to its default size (disableDoubleClick turns it off per handle)
  • i18n: labels prop on ResizableHandle overrides its one built-in string, the separator's accessible name (default "Resize panel"); merged over DEFAULT_RESIZABLE_HANDLE_LABELS, and an explicit aria-label / aria-labelledby still wins
  • Dragging keeps working across an iframe: while a separator is active the group neutralises pointer events on any iframe, object or embed inside it. Without that the pointer crosses into a cross-origin frame (an embedded editor, a map, a PDF viewer), the parent document stops seeing pointermove and the handle freezes mid-drag (fb_711744e3)
Not recommended for−2
  • ✕Static page layout (use CSS grid/flex)
  • ✕Single-column mobile screens
  • ✕Simple show/hide panels (use Collapsible or Sheet)

Install

Ask your agent, or call the MCP tool directly. The resolved bundle is written into your project.

get_registry_item({ name: "Resizable" })

Usage

import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from '@/components/surfaces/resizable';

<ResizablePanelGroup direction="horizontal" autoSaveId="invoice-workspace" className="h-full rounded-lg border">
  <ResizablePanel id="list" defaultSize="35%" minSize="20%">{invoiceList}</ResizablePanel>
  <ResizableHandle withHandle />
  <ResizablePanel id="preview" minSize="30%">{invoicePreview}</ResizablePanel>
</ResizablePanelGroup>

Tick variants in the left menu — each adds a tab with that variant’s real story source, the same code the preview above renders.

Props

PropTypeRequired
direction

Group layout axis; defaults to horizontal

'horizontal' | 'vertical'—
autoSaveId

Persist group layout under this key (give each Panel a stable id)

string—
storage

Custom getItem/setItem store for persistence; defaults to localStorage

LayoutStorage—
defaultSize

Panel: initial size (number = px, "35" / "35%" = percent, any CSS unit string)

number | string—
minSize

Panel: minimum size

number | string—
maxSize

Panel: maximum size

number | string—
collapsible

Panel: snap to collapsedSize when dragged below minSize

boolean—
collapsedSize

Panel: size while collapsed; defaults to 0%

number | string—
withHandle

Handle: render the grip pill visual

boolean—
disabled

Handle/Panel: prevent resizing

boolean—
disableDoubleClick

Handle: stop a double-click on this separator resetting its panel to the default size (default false, so the reset gesture is on). Forwarded straight to the react-resizable-panels separator. Turn it on where a double-click inside the layout already means something else and an accidental one would throw away a layout the user arranged

boolean—
labels

Handle: override the built-in UI string (i18n). One key, `resizeHandle`, the accessible name of the focusable role="separator" (default "Resize panel"); exported as DEFAULT_RESIZABLE_HANDLE_LABELS. Merged over that default, and an explicit aria-label wins over it — an aria-labelledby wins too and suppresses the default outright, so the handle never carries both

Partial<ResizableHandleLabels>—

Related