Resizable panels

Trade space between panes by dragging the divider between them.

split viewresizablepanes
Preview
Resizable panels preview
Files
Editor

When to use

  • Editor, mail, or dashboard layouts where people trade space between side-by-side panes.
  • Sidebars that should collapse by dragging or flicking toward the edge.
  • Layouts whose pane sizes should persist between visits via onLayoutChange.

When not to use

  • Use drawer for a panel that slides over the page.
  • Use tabs when only one pane needs to be visible at a time.
  • Avoid it for vertical splits, since it is horizontal only.

shadcn CLI

Also installs (1)

  • manicat-foundation
Manual install(+)
registry.jsonjson
{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "resizable-panels",
  "type": "registry:ui",
  "title": "Resizable panels",
  "description": "Trade space between panes by dragging the divider between them.",
  "categories": [
    "disclosure"
  ],
  "dependencies": [
    "lucide-react",
    "motion"
  ],
  "registryDependencies": [
    "https://manicat.viraza.net/r/manicat-foundation.json"
  ],
  "files": [
    {
      "path": "registry/components/resizable-panels/resizable-panels.tsx",
      "type": "registry:component",
      "target": "@components/manicat/resizable-panels/resizable-panels.tsx"
    },
    {
      "path": "registry/components/resizable-panels/resizable-panels.module.css",
      "type": "registry:component",
      "target": "@components/manicat/resizable-panels/resizable-panels.module.css"
    }
  ],
  "docs": "Docs and live preview: https://uiarc.dev/components/resizable-panels",
  "meta": {
    "tier": "free",
    "kind": "component",
    "docs": "https://manicat.viraza.net/components/resizable-panels",
    "markdown": "https://uiarc.dev/components/resizable-panels/markdown",
    "tags": [
      "split view",
      "resizable",
      "panes"
    ]
  }
}

Usage

Install the component, then copy this into your app. Change the content and the props to fit your screen.

example.tsxtsx
"use client";

import { ResizablePanel, ResizablePanels } from "@/components/manicat/resizable-panels/resizable-panels";

export function ResizablePanelsExample() {
  return (
    <div className="h-48 w-full max-w-2xl">
      <ResizablePanels label="Editor split" className="rounded-[var(--radius-panel)] border border-(color:--border)">
        <ResizablePanel id="files" label="Files" defaultSize={40}>
          <div className="p-4 text-sm text-(color:--text-muted)">Files</div>
        </ResizablePanel>
        <ResizablePanel id="editor" label="Editor" defaultSize={60}>
          <div className="p-4 text-sm text-(color:--text-muted)">Editor</div>
        </ResizablePanel>
      </ResizablePanels>
    </div>
  );
}

Examples

Real screens rather than isolated props. Switch to Code to copy a whole example into your project.

Editor split Two resizable panes, like a file tree next to an editor. Drag the divider or use the arrow keys.

Editor split Two resizable panes, like a file tree next to an editor. Drag the divider or use the arrow keys. preview
app/ · lib/ · site/
export default function Page() { … }

Keyboard interactions

KeysAction
ArrowLeft / ArrowRightMoves the focused divider 16px; with Shift, 64px. Past a limit, collapses a collapsible pane.
Home / EndMoves the divider to its minimum or maximum.
EnterCollapses or restores a collapsible neighbouring pane.
EscapeDuring a drag, returns the divider to where it started.

Accessibility

  • Each divider is a focusable role="separator" with aria-orientation, aria-controls, aria-valuenow, min, and max in percent.
  • aria-valuetext announces the share, or that a pane is hidden and Enter shows it.
  • Collapsed panes are inert. The group has role="group"; pass label to name it.

Motion

  • Dividers follow the pointer 1:1, rubber-band past limits, and spring to rest; flicks toward an edge collapse a pane. Double-click resets the layout.
  • Pane content fades as it is squeezed below its minimum and a restore tab pops in on a snappy spring.
  • Reduced motion jumps panes to their targets and fades the restore tab.

Responsive behavior

  • Panes do not stack on narrow screens; on mobile, render a single pane or switch to tabs yourself.
  • Dividers use pointer capture with touch-action none, so touch, pen, and mouse drags all work.
  • The group needs a definite height from its parent.

Performance

  • A ResizeObserver on the group refits pane widths when the container resizes.
  • Dragging sets widths directly and springs only on release or collapse; pane content is never re-rendered by the drag itself.

Notes for AI

  • Use for editor, mail, or dashboard layouts where people trade space between side-by-side panes. Horizontal only.
  • Give the parent a definite height and persist onLayoutChange shares to restore via defaultSize.