Resizable

Resizable panel layouts with accessible handles for dashboards.

Overview

Resizable wraps react-resizable-panels with Vinyaas styling for sidebars, split editors, and stacked dashboard panes. Prefer percentage strings for sizes ("30%").

Use orientation="horizontal" for side-by-side panels and orientation="vertical" for stacked panels. Nest groups when a pane needs its own axis — for example an explorer beside an editor/terminal split.

Handles stay visually thin with an expanded hit target. Single-axis cursors follow separator orientation; when horizontal and vertical handles cross, the library shows a two-dimensional resize cursor. Install with vinyaas add resizable or via vinyaas add --catalog dashboard.

Preview

Sidebar
Main

Customize

Orientation
Variant
With Handle

Installation

Add the component with the CLI. It copies the files in and installs anything they depend on.

npx vinyaas add resizable

Usage

import { ResizableHandle, ResizablePanel, ResizablePanelGroup, } from "@/components/ui/resizable"; export function Workspace() { return ( <ResizablePanelGroup orientation="horizontal" className="min-h-[240px] rounded-md border"> <ResizablePanel defaultSize="30%" minSize="20%"> <div className="p-4">Sidebar</div> </ResizablePanel> <ResizableHandle withHandle aria-label="Resize sidebar" /> <ResizablePanel defaultSize="70%"> <div className="p-4">Main</div> </ResizablePanel> </ResizablePanelGroup> ); }

Examples

Horizontal split

Sidebar + content. Horizontal resize uses a padded 2×3 secondary grip; drag or focus and use arrow keys.

Sidebar
Content

Vertical split

Editor above terminal. Vertical resize uses the same 2×3 grip rotated 90°.

Editor
Terminal

Separate blocks

variant="blocks" turns each panel into its own bordered block with a small gutter between them. The gutter is the drag target; add withHandle to mark it with a three-dot grip, which rotates for vertical groups.

Inbox
Message
Reply

Dashboard

Sidebar navigation with a main column: header card, metrics, and scrollable content. Uses Card, Badge, Button, Separator, and Scroll Area.

Workspace

Live

Dashboard

Resize the sidebar. Arrow keys work when the handle is focused.

Active projects

18

Open issues

42
Nested groups are optional here — the main column is a single panel filled with Vinyaas primitives.

In practice

Prefer nested groups when a pane needs its own axis. Label every handle and set minSize so panels stay usable. IDE and editor shells usually omit withHandle — keep the divider thin.

Explorer


  • app/
  • page.tsx
  • layout.tsx
  • components/
  • ui/
  • resizable.tsx
resizable.tsxEdited

export function ResizableHandle() {
  // Focus + drag resize boundary
}

Terminal


$ pnpm add react-resizable-panels

$ vinyaas add resizable

✓ Installed resizable

API

Other attributes for the underlying element are passed through.

NameTypeDefaultDescription
orientation"horizontal" | "vertical""horizontal"ResizablePanelGroup layout axis. Matches ARIA orientation conventions.
defaultSize / minSize / maxSizenumber | string—Panel size constraints from react-resizable-panels. Prefer percentage strings such as "30%".
variant"default" | "blocks""default"ResizablePanelGroup: default splits one surface with a thin line. blocks renders each panel as its own bordered block with a small gutter, which is the drag target.
withHandlebooleanfalseResizableHandle: show a visible grip affordance — the dot grip in a default group, the three-dot grip in the blocks gutter.
aria-labelstring—ResizableHandle: accessible name for the separator when panels are unlabeled.
disabledboolean—Group or Handle: disables resize interaction for that surface.
disableCursorbooleanfalseGroup: turn off library cursor management. Leave false so crossing handles can show a 2D resize cursor.

Accessibility

Handles render as ARIA separators from react-resizable-panels. Focus a handle and use arrow keys to resize. There is no keyboard trap around the handle.

  • Provide an aria-label on each ResizableHandle when neighboring panels are not otherwise named.
  • Focused handles show a visible focus-visible ring.
  • disabled marks the handle aria-disabled and dims pointer interaction.
  • Hit targets stay larger than the visible bar via a centered ::after region; the library also enforces a minimum resize target size for fine and coarse pointers.
  • Leave disableCursor unset so crossing horizontal and vertical handles can announce a 2D resize affordance.
  • Non-essential motion respects prefers-reduced-motion via motion-reduce:transition-none.