Skip to content
gnosticaignostic-ui
Esc
↑↓navigate↵open⌘Jpreview
On this page

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>
Open preview in a new tab

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>
Open preview in a new tab

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>
Open preview in a new tab

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>
Open preview in a new tab

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>
Open preview in a new tab

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.

Use Scroll Area inside a bounded panel and Sidebar for a navigation shell.

Was this page helpful?