Resizable

Panels separated by draggable handles — resize by mouse, touch or keyboard.

One
Two
import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from '@/components/ui/resizable'

export default function ResizableDemo() {
  return (
    <ResizablePanelGroup orientation="horizontal" className="h-[200px] max-w-md rounded-lg border border-border">
      <ResizablePanel defaultSize="50%" minSize="20%">
        <div className="flex h-full items-center justify-center p-6">
          <span className="font-semibold">One</span>
        </div>
      </ResizablePanel>
      <ResizableHandle withHandle aria-label="Resize panels" />
      <ResizablePanel defaultSize="50%" minSize="20%">
        <div className="flex h-full items-center justify-center p-6">
          <span className="font-semibold">Two</span>
        </div>
      </ResizablePanel>
    </ResizablePanelGroup>
  )
}

Installation

pnpm dlx shadcn@latest add https://kagadmodyaa.vercel.app/r/resizable.json

Needs a React + Tailwind project set up with shadcn. Copies the component source straight into your project, so you can edit it.

Resizable imports cn() from lib/utils.ts — add it too if you don't have it yet:

import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

Usage

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

<ResizablePanelGroup orientation="horizontal" className="h-[200px] rounded-lg border">
  <ResizablePanel defaultSize="50%" minSize="20%">One</ResizablePanel>
  <ResizableHandle withHandle aria-label="Resize panels" />
  <ResizablePanel defaultSize="50%">Two</ResizablePanel>
</ResizablePanelGroup>

// Sizes: strings are percentages ("30%"), numbers are pixels (240).
// The group needs a height (or width, for a vertical one) — e.g. h-[200px].

Examples

Vertical

Header
Content
import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from '@/components/ui/resizable'

export default function ResizableVertical() {
  return (
    <ResizablePanelGroup orientation="vertical" className="h-[280px] max-w-md rounded-lg border border-border">
      <ResizablePanel defaultSize="30%" minSize="15%">
        <div className="flex h-full items-center justify-center p-6">
          <span className="font-semibold">Header</span>
        </div>
      </ResizablePanel>
      <ResizableHandle aria-label="Resize header" />
      <ResizablePanel defaultSize="70%" minSize="15%">
        <div className="flex h-full items-center justify-center p-6">
          <span className="font-semibold">Content</span>
        </div>
      </ResizablePanel>
    </ResizablePanelGroup>
  )
}

Nested groups

Sidebar
Editor
Terminal
import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from '@/components/ui/resizable'

export default function ResizableNested() {
  return (
    <ResizablePanelGroup orientation="horizontal" className="h-[320px] max-w-lg rounded-lg border border-border">
      <ResizablePanel defaultSize="30%" minSize="15%" maxSize="50%">
        <div className="flex h-full items-center justify-center p-6">
          <span className="font-semibold">Sidebar</span>
        </div>
      </ResizablePanel>
      <ResizableHandle withHandle aria-label="Resize sidebar" />
      <ResizablePanel defaultSize="70%">
        <ResizablePanelGroup orientation="vertical">
          <ResizablePanel defaultSize="40%" minSize="15%">
            <div className="flex h-full items-center justify-center p-6">
              <span className="font-semibold">Editor</span>
            </div>
          </ResizablePanel>
          <ResizableHandle withHandle aria-label="Resize editor" />
          <ResizablePanel defaultSize="60%" minSize="15%">
            <div className="flex h-full items-center justify-center p-6">
              <span className="font-semibold">Terminal</span>
            </div>
          </ResizablePanel>
        </ResizablePanelGroup>
      </ResizablePanel>
    </ResizablePanelGroup>
  )
}

API Reference

NameTypeDefaultDescription
orientation'horizontal' | 'vertical''horizontal'Set on ResizablePanelGroup. Horizontal puts panels side by side; vertical stacks them. Groups can be nested.
defaultSizenumber | string—Initial size of a ResizablePanel. Write percentages as strings ("30%") and pixels as numbers (240). Panels without one share the rest equally.
minSize / maxSizenumber | string'0%' / '100%'Limits for a panel, in the same units as defaultSize (also "20rem", "50vw").
collapsible / collapsedSizeboolean / number | stringfalse / 0%Lets a panel snap shut when dragged below minSize.
onLayoutChange / onLayoutChanged(layout: Record<string, number>) => void—Set on the group. The first fires while dragging, the second once the pointer is released — use it to save the layout.
defaultLayoutRecord<string, number>—Restore a saved layout on the group (keyed by panel id).
ResizableHandle withHandlebooleanfalseAdds a small grip in the middle of the handle.
ResizableHandle aria-labelstring—Names the handle for screen readers, e.g. "Resize sidebar".
disabledbooleanfalseOn a group, panel or handle: stops it from being resized.
Keyboard——Focus a handle with Tab. The arrow keys along its axis move it by 5%, Home and End send it to the limits, Enter collapses or restores a collapsible panel, and F6 jumps between handles.