Layout/Resizable

Resizable

Accessible resizable panel groups and layouts.

Themed

When to Use

Use Resizable when you need to:

  • Build IDE-style layouts with adjustable sidebar, editor, and terminal panes
  • Create split-view interfaces such as email clients or file managers
  • Allow users to customize panel proportions in dashboards
  • Implement collapsible navigation or detail panels
  • Persist user-preferred layout sizes across sessions

When Not to Use

  • Simple content separation without user adjustment - use Separator
  • Scrollable content regions - use Scroll Area
  • Expandable/collapsible content sections - use Accordion or Collapsible
  • Responsive grid layouts that reflow on breakpoints - use CSS Grid or Flexbox

Nested Panels

Horizontal panels with nested vertical panels for complex layouts.

One
Two
Three

Vertical

Vertically stacked resizable panels using direction='vertical'.

Header
Content

With Handle

Visible drag handle for better discoverability. Use withHandle prop on ResizableHandle.

Sidebar
Content

Collapsible Panel

Panels can collapse below their minimum size. Drag the sidebar fully to the left to collapse it.

Sidebar
Content

Persist Layout

Panel sizes are saved to localStorage via autoSaveId and restored on revisit.

Nav
Main

UX & Design Guidelines

Visual Hierarchy

Use withHandle on the ResizableHandle whenever the resize interaction is not obvious from surrounding UI. The default 1px divider works well for IDE-style layouts where users expect resizability, but a visible grip handle improves discoverability in general-purpose interfaces.

Spacing & Layout

Set minSize on each panel to prevent content from being crushed to an unusable width. A good starting point is 15-20% for sidebars and 30% for main content areas. Use collapsible instead of allowing a panel to shrink to near-zero, so the transition is clean.

Responsive Behavior

On mobile, consider switching from a horizontal to a vertical layout or replacing resizable panels with a collapsible drawer. The direction prop can be set dynamically based on viewport width. Ensure panels have enough minimum size to remain usable on smaller screens.

Color & Contrast

The resize handle uses bg-border for the divider line, ensuring it adapts to both light and dark modes. The visible grip icon inherits from the same token. Focus rings use ring-ring for WCAG-compliant visibility. Avoid adding custom colors to handles that break theme consistency.

Accessibility

Keyboard Navigation

  • Tab — Move focus to the resize handle
  • Arrow Left / Arrow Right — Resize horizontal panels in small increments
  • Arrow Up / Arrow Down — Resize vertical panels in small increments
  • Home — Collapse panel to its minimum (or collapsed) size
  • End — Expand panel to its maximum size
  • Enter — Toggle a collapsible panel between collapsed and expanded

Screen Reader Support

  • Resize handles are rendered as separator role elements with proper ARIA attributes
  • Panel size changes are announced via aria-valuenow, aria-valuemin, and aria-valuemax
  • Use semantic landmarks (nav, main, aside) inside panels for clear structure
  • Collapse/expand state is communicated to assistive technology automatically

Focus Management

Resize handles display a visible focus ring via focus-visible:ring-1 with ring-ring. The ring offset ensures the indicator is visible against both panel backgrounds. Focus remains on the handle during keyboard resizing for continuous adjustment.

Touch Targets

The handle has an invisible hit area expanded via after:w-1 pseudo-element, making it easier to grab on touch devices. The withHandle prop renders a 12x16px grip icon that provides an additional visual target. For mobile interfaces, consider using larger custom handle components.

ARIA Attributes

{/* Resizable panels with accessible labels */}
<ResizablePanelGroup direction="horizontal">
  <ResizablePanel defaultSize={25}>
    <nav aria-label="Sidebar navigation">
      {/* sidebar content */}
    </nav>
  </ResizablePanel>
  <ResizableHandle />
  <ResizablePanel defaultSize={75}>
    <main aria-label="Main content">
      {/* main content */}
    </main>
  </ResizablePanel>
</ResizablePanelGroup>

{/* Handle with visible grip indicator */}
<ResizableHandle withHandle />

{/* Collapsible panel with onCollapse callback */}
<ResizablePanel
  collapsible
  collapsedSize={0}
  minSize={15}
  onCollapse={() => console.log("Panel collapsed")}
  onExpand={() => console.log("Panel expanded")}
>
  {/* collapsible content */}
</ResizablePanel>

API Reference

The Resizable component is composed of three parts: ResizablePanelGroup, ResizablePanel, and ResizableHandle.

ResizablePanelGroup

Container that manages panel layout direction and coordinates resize behavior.

PropTypeDefaultDescription
direction"horizontal" | "vertical"Direction of the panel group layout.
autoSaveIdstringUnique ID for persisting panel sizes to localStorage.
onLayout(sizes: number[]) => voidCallback fired when panel sizes change.
classNamestringAdditional CSS classes to apply to the panel group.

ResizablePanel

Individual panel that can be resized within the group. Accepts all standard div attributes.

PropTypeDefaultDescription
defaultSizenumberDefault size as a percentage (0–100).
minSizenumber"10"Minimum size as a percentage.
maxSizenumberMaximum size as a percentage.
collapsibleboolean"false"Whether the panel can be collapsed below its minimum size.
collapsedSizenumber"0"Size of the panel when collapsed.
onCollapse() => voidCallback fired when the panel is collapsed.
onExpand() => voidCallback fired when the panel is expanded.

ResizableHandle

Draggable divider placed between panels. Renders as a separator role element.

PropTypeDefaultDescription
withHandleboolean"false"Show a visible drag handle indicator (grip icon).
disabledboolean"false"Disable resizing via this handle.
classNamestringAdditional CSS classes to apply to the handle.