Skip to content
Protocore Design Systemv1.6.9

/// Layout

ResizablePanels

Two or more panels sharing an axis, split by hairline handles that drag with the pointer and resize with the arrow keys.

import { ResizablePanels } from "@protocore/pds";
View as Markdown

Horizontal split

Compose ResizablePanels.Root with Panels and a Handle between each pair. Give panels a defaultSize (percentage) plus minSize / maxSize bounds; drag the hairline seam or focus it and press the arrow keys.

SOURCES

infra/vpc.tf

infra/s3.tf

services/api

INSPECTOR

Drag the hairline seam, or focus it with Tab and press the arrow keys to resize.

Vertical split

Set direction="vertical" to stack panels top-to-bottom — the handle becomes a horizontal seam and Arrow Up / Down drive it.

QUERY

select count(*) from sessions

where region = 'eu-central-1';

RESULTS

count ...... 12,804

took ....... 42ms

Controlled sizes

Own the split by passing sizes and onSizesChange. Nothing is persisted for you — read the array out and store it wherever you like (URL, localStorage, a layout preset).

sizes

[50, 50]

Requests

Latency

How sizing works

Sizes are percentages that always conserve their pair's combined space: dragging a handle grows one neighbour and shrinks the other by the same amount, clamped so neither crosses its minSize / maxSize. Panels are laid out by flex grow-factor, so the split stays proportional as the container resizes. There is no persistenceResizablePanels is a controlled primitive; wire onSizesChange to your own store if you want the layout to survive a reload.

Usage

Do

  • Put a `Handle` between every adjacent pair of `Panel`s, in source order.
  • Give each panel a sensible `minSize` so content never collapses to nothing.
  • Pass an `aria-label` to each `Handle` describing what it resizes.
  • Persist the layout yourself from `onSizesChange` when it should outlive the session.

Don't

  • Don't nest interactive resize handles inside a scrolling region that traps the pointer.
  • Don't rely on the component to remember sizes — it deliberately keeps no state across mounts.
  • Don't omit the `Handle` and expect a seam; panels without a handle between them can't be resized.
  • Don't set `minSize` + `maxSize` sums that leave a pair no room to move.

Accessibility

KeysAction
TabMove focus to a resize handle.
Arrow Left / RightResize a horizontal split by one step (grow / shrink the leading panel).
Arrow Up / DownResize a vertical split by one step.
HomeCollapse the leading panel to its minimum.
EndGrow the leading panel to its maximum.
  • Each handle is a `role="separator"` with `aria-orientation` (perpendicular to the split axis), `aria-valuenow` / `aria-valuemin` / `aria-valuemax` reflecting the leading panel's size, and `aria-controls` pointing at that panel.
  • The arrow-key step is configurable via `keyboardStep` (default 10 percentage points).
  • The 1px seam widens to a 6px hit area and inks to the accent on hover, focus, and drag.

ResizablePanels.Root props

PropTypeDefaultDescription
classNamestring
defaultSizesnumber[]Initial sizes in uncontrolled mode; falls back to each panel's `defaultSize`.
directionenumhorizontalSplit axis.
keyboardStepnumber10Percentage points an arrow-key press moves the focused handle.
onSizesChange((sizes: number[]) => void)Fires with the next sizes array whenever a handle moves.
sizesnumber[]Controlled sizes (percentages, one per panel).
styleCSSProperties

ResizablePanels.Panel props

PropTypeDefaultDescription
__indexnumber0@internal Ordinal assigned by `Root`; do not set manually.
classNamestring
defaultSizenumberInitial size in percentage points. Missing panels share the remainder equally.
maxSizenumber100Largest size this panel may grow to, in percentage points.
minSizenumber0Smallest size this panel may shrink to, in percentage points.
styleCSSProperties

ResizablePanels.Handle props

PropTypeDefaultDescription
__indexnumber0@internal Gap ordinal assigned by `Root`; do not set manually.
aria-labelstring"Resize panels"Accessible name for the separator.
classNamestring
styleCSSProperties

Related

  • SidebarA left navigation rail of grouped items — mono uppercase group labels over sans links, with an accent rule on the active item.
  • AppShellThe application frame — an optional env strip and top bar over a sidebar + main body row, with an optional footer and a responsive mobile drawer.
  • PanelA dense bordered console surface with an optional labelled header row and hairline-divided sub-sections.
  • GridA CSS grid with either a responsive auto-fit track or a fixed column count, and a token-scale gap.