Resizable
Astro panels with pointer and keyboard size controls.
---
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@/components/ui/resizable"
---
<ResizablePanelGroup class="h-48 w-full max-w-2xl rounded-lg border">
<ResizablePanel defaultSize={50} minSize={20}>
<div class="flex h-full flex-col items-center justify-center gap-2 p-6">
<span class="font-semibold">Panel A</span>
<span class="text-muted-foreground text-sm">
Drag the handle to resize.
</span>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize={50} minSize={20} class="bg-muted">
<div class="flex h-full flex-col items-center justify-center gap-2 p-6">
<span class="font-semibold">Panel B</span>
<span class="text-muted-foreground text-sm">
Arrow keys resize the focused handle.
</span>
</div>
</ResizablePanel>
</ResizablePanelGroup>
Installation
bunx --bun shadcn@latest add @gnosticai/resizable
Usage
---
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@/components/ui/resizable"
---
<ResizablePanelGroup class="h-48 w-full rounded-lg border">
<ResizablePanel defaultSize={50} minSize={20}>
<div class="flex h-full items-center justify-center p-6">One</div>
</ResizablePanel>
<ResizableHandle />
<ResizablePanel defaultSize={50} minSize={20}>
<div class="flex h-full items-center justify-center p-6">Two</div>
</ResizablePanel>
</ResizablePanelGroup>
Composition
Place one ResizableHandle between each pair of panels. Sizes are percentages of the group. Give the group a height.
ResizablePanelGroup
├── ResizablePanel
├── ResizableHandle
└── ResizablePanel
Examples
Vertical
Set direction="vertical" to stack the panels. The handle moves up and down.
---
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@/components/ui/resizable"
---
<ResizablePanelGroup
direction="vertical"
class="h-64 w-full max-w-md rounded-lg border"
>
<ResizablePanel defaultSize={40} minSize={20}>
<div class="flex h-full flex-col items-center justify-center gap-2 p-6">
<span class="font-semibold">Panel A</span>
<span class="text-muted-foreground text-sm">
Drag the handle to resize.
</span>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize={60} minSize={20} class="bg-muted">
<div class="flex h-full flex-col items-center justify-center gap-2 p-6">
<span class="font-semibold">Panel B</span>
<span class="text-muted-foreground text-sm">
Up and down arrow keys resize this group.
</span>
</div>
</ResizablePanel>
</ResizablePanelGroup>
Custom handle
Add a custom handle by passing a direct child to ResizableHandle.
---
import GripVerticalIcon from "@lucide/astro/icons/grip-vertical"
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@/components/ui/resizable"
---
<ResizablePanelGroup class="h-48 w-full max-w-2xl rounded-lg border">
<ResizablePanel defaultSize={50} minSize={20}>
<div class="flex h-full flex-col items-center justify-center gap-2 p-6">
<span class="font-semibold">Panel A</span>
<span class="text-muted-foreground text-sm">
Drag the custom handle to resize.
</span>
</div>
</ResizablePanel>
<ResizableHandle>
<GripVerticalIcon class="h-6 w-3 bg-border z-10 flex shrink-0 rounded-lg text-muted-foreground/72" />
</ResizableHandle>
<ResizablePanel defaultSize={50} minSize={20} class="bg-muted">
<div class="flex h-full flex-col items-center justify-center gap-2 p-6">
<span class="font-semibold">Panel B</span>
<span class="text-muted-foreground text-sm">
Arrow keys resize the focused panel.
</span>
</div>
</ResizablePanel>
</ResizablePanelGroup>
Nested groups
A panel can contain another group. Use withHandle to show a grip on the separator.
---
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@/components/ui/resizable"
---
<ResizablePanelGroup class="h-64 w-full max-w-2xl rounded-lg border">
<ResizablePanel defaultSize={50} minSize={20}>
<div class="flex h-full items-center justify-center p-6">
<span class="font-semibold">One</span>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize={50} minSize={20}>
<ResizablePanelGroup direction="vertical" class="h-full">
<ResizablePanel defaultSize={30} minSize={15}>
<div class="flex h-full items-center justify-center p-6">
<span class="font-semibold">Two</span>
</div>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel defaultSize={70} minSize={15} class="bg-muted">
<div class="flex h-full items-center justify-center p-6">
<span class="font-semibold">Three</span>
</div>
</ResizablePanel>
</ResizablePanelGroup>
</ResizablePanel>
</ResizablePanelGroup>
Collapsible panel
Set collapsible on a panel to let it collapse past minSize. Focus the handle and press Enter to collapse and expand that panel. Arrow keys resize by 10 percent. Home and End move to the limits.
---
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@/components/ui/resizable"
---
<ResizablePanelGroup class="h-48 w-full max-w-2xl rounded-lg border">
<ResizablePanel
defaultSize={25}
minSize={15}
collapsible
collapsedSize={0}
class="bg-muted"
>
<div class="flex h-full items-center justify-center p-4">
<span class="text-sm font-semibold">Sidebar</span>
</div>
</ResizablePanel>
<ResizableHandle withHandle aria-label="Resize sidebar" />
<ResizablePanel defaultSize={75} minSize={30}>
<div class="flex h-full flex-col items-center justify-center gap-2 p-6">
<span class="font-semibold">Content</span>
<span class="text-muted-foreground text-center text-sm">
Focus the handle and press Enter to collapse the sidebar.
</span>
</div>
</ResizablePanel>
</ResizablePanelGroup>
API Reference
ResizablePanelGroup, ResizablePanel, and ResizableHandle accept native div attributes.
ResizablePanelGroup
| Prop | Type | Default | Purpose |
|---|---|---|---|
direction |
horizontal, vertical |
horizontal |
Layout axis. |
keyboardResizeBy |
number |
10 |
Percent moved for each arrow key press. |
ResizablePanel
| Prop | Type | Default | Purpose |
|---|---|---|---|
defaultSize |
number |
even split | Initial size as a percentage. |
minSize |
number |
0 |
Minimum size as a percentage. |
maxSize |
number |
100 |
Maximum size as a percentage. |
collapsible |
boolean |
false |
The panel can collapse. |
collapsedSize |
number |
0 |
Size as a percentage when collapsed. Must not exceed minSize. |
ResizableHandle
| Prop | Type | Default | Purpose |
|---|---|---|---|
withHandle |
boolean |
false |
Show a grip in the center of the handle. |
The group emits resizable:change on the root element. event.detail.layout is the panel sizes in percent. The component does not store the layout.
Read the installed source for the component source. See the data-slot docs for keyboard behavior and the controller API.
When to use Resizable
Compose panels and resize handles inside a ResizablePanelGroup. Choose an orientation and useful size bounds so neither panel becomes unusable. Keep the panel content independent of its starting size.
Keyboard and behavior
The existing data-slot controller handles pointer and keyboard resizing. Give each handle a useful label and test its arrow-key behavior. A panel that contains a form must still expose all controls at its minimum size.
Theme and layout
The handle uses shared border and focus colors. Check content overflow after a resize and at narrow viewport widths. Some layouts are clearer when panels stack instead of remaining resizable on a phone.
Related choices
Use Scroll Area inside a bounded panel and Sidebar for a navigation shell.