Morpha UI

Resizable

Split-pane layouts with draggable dividers.

Overview

Split panes built on react-resizable-panels (v4): pointer and keyboard resizing, nested groups, collapsible panels and layout persistence come from the primitive. The divider is an outline-variant hairline with an invisible 8px grab zone straddling it — the seam stays quiet, the target stays comfortable. It picks up outline on hover and fills primary while it is dragged or focused, and withHandle puts the MD3 drag-handle pill on the seam.

One
Two
Three
$ npx morpha-ui add resizable

Composition

<ResizablePanelGroup orientation="horizontal">
  <ResizablePanel defaultSize="50%" />
  <ResizableHandle withHandle />
  <ResizablePanel defaultSize="50%" />
</ResizablePanelGroup>

Sizes are percentages when given as strings ("50%", "50"), pixels when given as numbers, and any CSS unit otherwise ("16rem", "50vh"). ResizablePanel applies its className to an inner element, so you can lay out the pane's content directly on it.

The group sets height: 100% on itself inline, so a height class on ResizablePanelGroup has no effect — it fills its container instead. Put the height (and the border and rounding) on a wrapper around the group, as every example below does.

Examples

Vertical

orientation="vertical" stacks the panes and turns the divider — and its pill — with them.

Header
Content

Handle

withHandle renders the drag-handle pill: a 4×32px outline-variant grip that goes primary on drag and on keyboard focus. Use it whenever the divider is a primary affordance rather than a passive seam.

Sidebar
Content
Console

Collapsible

A collapsible panel snaps shut when it is dragged below its minSize, down to collapsedSize. panelRef exposes the imperative API — collapse(), expand(), resize(), isCollapsed() — for driving it from a button.

Sidebar
ContentDrag the divider below 20% to collapse

Layout

Groups nest, and each panel carries its own minSize / maxSize, so a three-pane app shell can never squeeze a pane shut by accident.

InboxStarredSentArchive
Ada LovelaceAnalytical engine notes
Grace HopperCompiler draft
Alan TuringOn computable numbers
Analytical engine notes

Drag either divider to rebalance the three panes. Each one keeps its own minimum width, so the reading pane never gets squeezed shut.

RTL

The group follows the document direction: under dir="rtl" the panes and the drag axis mirror, with no extra configuration.

القائمة
المحتوى

Persisting the layout

onLayoutChanged fires once a drag settles — it is the callback to persist — and defaultLayout takes the stored map of panel id to percentage back. Give every panel a stable id so the layout can be matched up again.

<ResizablePanelGroup
  orientation="horizontal"
  defaultLayout={savedLayout}
  onLayoutChanged={(layout) => {
    localStorage.setItem('layout', JSON.stringify(layout))
  }}
>
  <ResizablePanel id="sidebar" defaultSize="30%" />
  <ResizableHandle withHandle />
  <ResizablePanel id="content" defaultSize="70%" />
</ResizablePanelGroup>

Installation

Morpha UI components are distributed via the Morpha CLI: npx morpha-ui add resizable. In the meantime, since this is the base shadcn component, you can also add it directly:

npx shadcn@latest add resizable

API Reference

ResizablePanelGroup

Prop

Type

ResizablePanel

Prop

Type

ResizableHandle

Prop

Type

Accessibility

  • Dividers implement the WAI-ARIA window-splitter pattern: focus with Tab, resize with the Arrow keys, Home/End to collapse and expand.
  • Keyboard focus draws the kit's secondary ring on the divider and fills the pill with primary.
  • The grab zone is 8px wide regardless of the 1px hairline, and the primitive widens the hit target further on coarse (touch) pointers.
  • Double-clicking a divider restores the neighbouring panels to their default sizes; pass disableDoubleClick to opt out.

On this page