Marble UI
← Components

Resizable Panels / Splitter

Preview: Horizontal

Split view with draggable, keyboard-accessible separators (role="separator", arrow keys step 5%).

Import

import { ResizablePanels, ResizablePanel, ResizableHandle } from '@marbleui/ui'

Props

<ResizablePanels>

PropTypeDefaultDescription
children *requiredReactNode—
defaultSizes *requirednumber[]—Initial sizes in percent, one per panel (sum 100).
classNamestring—
minSizenumber10
onSizesChange(sizes: number[]) => void—
orientation'vertical' | 'horizontal''horizontal'

<ResizablePanel>

PropTypeDefaultDescription
children *requiredReactNode—
index *requirednumber—
classNamestring—

<ResizableHandle>

Separator between panel `index` and `index + 1`.

PropTypeDefaultDescription
index *requirednumber—
aria-labelstring—
withGripbooleantrue

Usage notes

Split view whose panes the user can resize by dragging or with the keyboard. For a fixed layout use plain flex or grid; for showing or hiding a pane use Drawer or a collapsible Sidebar.

<ResizablePanels defaultSizes={[30, 70]} className="h-64">
  <ResizablePanel index={0}>Sidebar</ResizablePanel>
  <ResizableHandle index={0} aria-label="Resize sidebar" />
  <ResizablePanel index={1}>Content</ResizablePanel>
</ResizablePanels>
  • defaultSizes are percentages, one per panel, and should sum to 100. Sizes are uncontrolled; read changes via onSizesChange. minSize defaults to 10.
  • A handle at index sits between panels index and index + 1. Handles are focusable separators; arrow keys resize by 5% (10% with Shift). Horizontal uses Left/Right, orientation="vertical" uses Up/Down.
  • Give each handle a distinct aria-label (default “Resize”) and give the container a height.
Example source · show
import { ResizableHandle, ResizablePanel, ResizablePanels } from './resizable'

const Pane = ({ label }: { label: string }) => <div className="grid h-full place-items-center text-sm text-fg-muted">{label}</div>

export const styles = {
  Horizontal: () => (
    <ResizablePanels defaultSizes={[30, 70]} className="h-64 w-[36rem] rounded-xl border border-border bg-surface">
      <ResizablePanel index={0}><Pane label="Sidebar" /></ResizablePanel>
      <ResizableHandle index={0} />
      <ResizablePanel index={1}><Pane label="Content" /></ResizablePanel>
    </ResizablePanels>
  ),
  'Three panes': () => (
    <ResizablePanels defaultSizes={[25, 50, 25]} className="h-64 w-[36rem] rounded-xl border border-border bg-surface">
      <ResizablePanel index={0}><Pane label="Files" /></ResizablePanel>
      <ResizableHandle index={0} withGrip={false} />
      <ResizablePanel index={1}><Pane label="Editor" /></ResizablePanel>
      <ResizableHandle index={1} withGrip={false} />
      <ResizablePanel index={2}><Pane label="Preview" /></ResizablePanel>
    </ResizablePanels>
  ),
  Vertical: () => (
    <ResizablePanels orientation="vertical" defaultSizes={[60, 40]} className="h-72 w-96 rounded-xl border border-border bg-surface">
      <ResizablePanel index={0}><Pane label="Editor" /></ResizablePanel>
      <ResizableHandle index={0} />
      <ResizablePanel index={1}><Pane label="Terminal" /></ResizablePanel>
    </ResizablePanels>
  ),
}

Source: packages/ui/src/resizable/resizable.tsx

Catalog

Category
layout
Priority
Core
Primitive
custom