Components
- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Breadcrumb
- Bubble
- Button
- Button Group
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Collapsible
- Combobox
- Command
- Context Menu
- Date Picker
- Dialog
- Drawer
- Dropdown Menu
- Empty
- Field
- Hover Card
- Input
- Input OTP
- Input Group
- Item
- Kbd
- Label
- Marker
- Menubar
- Message
- Message Scroller
- Native Select
- Navigation Menu
- Pagination
- Popover
- Progress
- Radio Group
- Resizable
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Spinner
- Switch
- Table
- Tabs
- Textarea
- Toggle
- Toggle Group
- Tooltip
- New Component
Elements
Charts
Icons
Sidebar
Content
Nav
Editor
Preview
Top panel
Bottom panel
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable";
export const ResizableDemo = () => (
<div className="flex flex-col gap-6">
{/* Horizontal */}
<ResizablePanelGroup orientation="horizontal" className="min-h-40 rounded-lg border">
<ResizablePanel defaultSize={30}><div className="flex h-full items-center justify-center p-4"><span className="text-muted-foreground text-sm">Sidebar</span></div></ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize={70}><div className="flex h-full items-center justify-center p-4"><span className="text-muted-foreground text-sm">Content</span></div></ResizablePanel>
</ResizablePanelGroup>
{/* Three panels */}
<ResizablePanelGroup orientation="horizontal" className="min-h-40 rounded-lg border">
<ResizablePanel defaultSize={20} minSize={15}><div className="flex h-full items-center justify-center p-4"><span className="text-muted-foreground text-sm">Nav</span></div></ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize={55}><div className="flex h-full items-center justify-center p-4"><span className="text-muted-foreground text-sm">Editor</span></div></ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize={25} minSize={15}><div className="flex h-full items-center justify-center p-4"><span className="text-muted-foreground text-sm">Preview</span></div></ResizablePanel>
</ResizablePanelGroup>
{/* Vertical */}
<ResizablePanelGroup orientation="vertical" className="min-h-56 rounded-lg border">
<ResizablePanel defaultSize={60}><div className="flex h-full items-center justify-center p-4"><span className="text-muted-foreground text-sm">Top panel</span></div></ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize={40}><div className="flex h-full items-center justify-center p-4"><span className="text-muted-foreground text-sm">Bottom panel</span></div></ResizablePanel>
</ResizablePanelGroup>
</div>
);Horizontal
Two side-by-side panels separated by a draggable handle.
<ResizablePanelGroup direction="horizontal" className="rounded-lg border">
<ResizablePanel defaultSize={50}>
<div className="p-4">Left panel</div>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel defaultSize={50}>
<div className="p-4">Right panel</div>
</ResizablePanel>
</ResizablePanelGroup>Vertical
Stack panels top-to-bottom with direction="vertical".
<ResizablePanelGroup direction="vertical" className="rounded-lg border">
<ResizablePanel defaultSize={50}>
<div className="p-4">Top panel</div>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel defaultSize={50}>
<div className="p-4">Bottom panel</div>
</ResizablePanel>
</ResizablePanelGroup>Three panels
Add more panels and handles for multi-pane layouts.
<ResizablePanelGroup direction="horizontal" className="rounded-lg border">
<ResizablePanel defaultSize={25} minSize={15}>
<div className="p-4">Sidebar</div>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel defaultSize={50}>
<div className="p-4">Main</div>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel defaultSize={25} minSize={15}>
<div className="p-4">Inspector</div>
</ResizablePanel>
</ResizablePanelGroup>With handle grip
Add withHandle to the ResizableHandle to show a visible drag grip icon.
<ResizableHandle withHandle />Controlled size
Use defaultSize, minSize, and maxSize (all in percent) to constrain each panel.
<ResizablePanel
defaultSize={30}
minSize={20}
maxSize={60}
collapsible
collapsedSize={5}
>
...
</ResizablePanel>Props
ResizablePanelGroup
| Prop | Type | Default | Description |
|---|---|---|---|
direction | "horizontal" | "vertical" | — | Required. Split direction |
onLayout | (sizes: number[]) => void | — | Called whenever panel sizes change |
className | string | — | Additional CSS classes |
ResizablePanel
| Prop | Type | Default | Description |
|---|---|---|---|
defaultSize | number | — | Initial size as a percentage of the group |
minSize | number | — | Minimum size percentage |
maxSize | number | — | Maximum size percentage |
collapsible | boolean | false | Allow the panel to collapse to collapsedSize |
collapsedSize | number | 0 | Size percentage when collapsed |
className | string | — | Additional CSS classes |
ResizableHandle
| Prop | Type | Default | Description |
|---|---|---|---|
withHandle | boolean | false | Show a drag grip icon on the handle |
className | string | — | Additional CSS classes |
Usage
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@/components/ui/resizable";
<ResizablePanelGroup direction="horizontal" className="h-full rounded-lg border">
<ResizablePanel defaultSize={20} minSize={15} collapsible>
<nav className="p-2">Sidebar nav</nav>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize={80}>
<main className="p-4">Page content</main>
</ResizablePanel>
</ResizablePanelGroup>Specs
# Mint — Resizable Specs
## Component
- name: resizable
- file: registry/mint/resizable.tsx
- installs to: components/ui/resizable.tsx
- dependencies: react-resizable-panels, lucide-react
- registryDependencies: none
## What is it
Thin styled wrappers around react-resizable-panels that add consistent handle
styling to draggable split-panel layouts. Use it for IDE-style workspaces,
sidebar/content splits, and multi-pane dashboards. Do not use when panels have
fixed sizes — a simple CSS grid or flex layout is more appropriate.
## Visual identity
Minimal and unobtrusive: the separator is a 1px --border line, a soft,
translucent green line at 60% opacity, that expands its hit area via a
pseudo-element without visual noise. The optional grip pill (border
+ --border fill, rounded-xs) signals draggability while staying compact and light.
## Design tokens
--border: oklch(0.88 0.04 155 / 60%) / oklch(0.32 0.05 155 / 40%) — separator line and grip pill fill/border
--ring: oklch(0.5 0.14 155) / oklch(0.62 0.15 155) — green focus ring on the handle
## Sub-components
- ResizablePanelGroup → flex container that wraps all panels; sets orientation via aria-[orientation]
- ResizablePanel → individual resizable pane; accepts size constraints and collapsible behaviour
- ResizableHandle → 1px separator with extended hit area; withHandle renders a visible grip pill
## External dependencies
- react-resizable-panels: keyboard-accessible drag-resize engine (Panel, PanelGroup, PanelResizeHandle)
- lucide-react: provides GripVerticalIcon for the withHandle grip pill
## States
- focus-visible: 1px --ring ring with 1px offset on the handle separator
- aria-[orientation=horizontal]: handle rotates to horizontal layout (h-px, full-width after pseudo-element, grip div rotates 90deg)
## Conventions
- All size values (defaultSize, minSize, maxSize, collapsedSize) are percentages (0–100), not pixels
- Always set minSize to prevent a panel from collapsing to zero unintentionally
- Use collapsible + collapsedSize for sidebar patterns that can be fully hidden
- Persist layout across sessions with onLayout + localStorage
- withHandle improves discoverability; omit it for minimal or data-dense UIs
- Sub-components expose data-slot="resizable-panel-group", data-slot="resizable-panel", and data-slot="resizable-handle" for CSS targeting
- Keyboard resize (from react-resizable-panels): Arrow keys = 1%, Shift+Arrow = 10%, Home/End = min/maxInstallation
$ npx shadcn add \https://gin-design.vercel.app/resizable