Unified UI

Resizable

Resizable split panels with drag handle for building adjustable layouts. Supports horizontal and vertical directions with optional grip handle.

Basic

Resizable split panels with a drag handle for building adjustable layouts. Supports horizontal and vertical directions, default sizes, and an optional grip handle indicator.

Panel A
Panel B

Installation

Install the component via the CLI in one command.

npx @work-rjkashyap/unified-ui add resizable
pnpm dlx @work-rjkashyap/unified-ui add resizable
npx @work-rjkashyap/unified-ui add resizable
bunx @work-rjkashyap/unified-ui add resizable

If you haven't initialized your project yet, run npx @work-rjkashyap/unified-ui init first. See the CLI docs for details.

Or install the full package

Use this approach if you prefer to install the entire design system as a dependency instead of copying individual components.

npm install @work-rjkashyap/unified-ui
pnpm add @work-rjkashyap/unified-ui
yarn add @work-rjkashyap/unified-ui
bun add @work-rjkashyap/unified-ui

Anatomy

import {
	ResizablePanelGroup,
	ResizablePanel,
	ResizableHandle,
} from "@work-rjkashyap/unified-ui";

Basic Usage

Wrap panels and handles inside a ResizablePanelGroup. Each ResizablePanel fills available space, and ResizableHandle renders between them as a drag separator.

<ResizablePanelGroup direction="horizontal">
  <ResizablePanel>
    <div>Left</div>
  </ResizablePanel>
  <ResizableHandle />
  <ResizablePanel>
    <div>Right</div>
  </ResizablePanel>
</ResizablePanelGroup>

Horizontal Split

The default direction is "horizontal", which places panels side by side.

Sidebar
Content

Vertical Split

Set direction="vertical" to stack panels on top of each other with a horizontal drag handle.

Top
Bottom

With Grip Handle

Pass withHandle to ResizableHandle to show a visual grip indicator on the drag handle. This makes the handle more discoverable.

Left
Right

Three Panels

You can nest any number of panels and handles:

Nav
Content
Details

Panel Size Constraints

Use minSize and maxSize (as percentages) to constrain how small or large a panel can be resized:

<ResizablePanelGroup direction="horizontal">
  <ResizablePanel defaultSize={30} minSize={20} maxSize={40}>
    <div>Sidebar (min 20%, max 40%)</div>
  </ResizablePanel>
  <ResizableHandle withHandle />
  <ResizablePanel>
    <div>Main content (fills remaining space)</div>
  </ResizablePanel>
</ResizablePanelGroup>

Nested Resizable Panels

Nest ResizablePanelGroup inside a panel for complex layouts:

<ResizablePanelGroup direction="horizontal">
  <ResizablePanel defaultSize={30}>
    <div>Sidebar</div>
  </ResizablePanel>
  <ResizableHandle withHandle />
  <ResizablePanel defaultSize={70}>
    <ResizablePanelGroup direction="vertical">
      <ResizablePanel defaultSize={60}>
        <div>Editor</div>
      </ResizablePanel>
      <ResizableHandle withHandle />
      <ResizablePanel defaultSize={40}>
        <div>Terminal</div>
      </ResizablePanel>
    </ResizablePanelGroup>
  </ResizablePanel>
</ResizablePanelGroup>

IDE-Style Layout

A common real-world pattern is a three-zone IDE layout:

<div className="h-screen">
  <ResizablePanelGroup direction="horizontal">
    {/* File explorer */}
    <ResizablePanel defaultSize={20} minSize={15} maxSize={30}>
      <nav>File Explorer</nav>
    </ResizablePanel>
    <ResizableHandle withHandle />

    {/* Editor + terminal */}
    <ResizablePanel defaultSize={55}>
      <ResizablePanelGroup direction="vertical">
        <ResizablePanel defaultSize={70}>
          <div>Code Editor</div>
        </ResizablePanel>
        <ResizableHandle withHandle />
        <ResizablePanel defaultSize={30} minSize={10}>
          <div>Terminal</div>
        </ResizablePanel>
      </ResizablePanelGroup>
    </ResizablePanel>
    <ResizableHandle withHandle />

    {/* Sidebar */}
    <ResizablePanel defaultSize={25} minSize={15} maxSize={35}>
      <aside>Properties Panel</aside>
    </ResizablePanel>
  </ResizablePanelGroup>
</div>

Props

ResizablePanelGroup

PropTypeDefaultDescription
direction"horizontal" | "vertical""horizontal"Direction of the panel split.
defaultSizesnumber[]Default sizes (percentages) for all panels.
classNamestringAdditional CSS classes on the group container.
childrenReactNodePanel and handle children.

All standard <div> HTML attributes are also forwarded.

ResizablePanel

PropTypeDefaultDescription
defaultSizenumberInitial size as a percentage (e.g. 50 = 50%).
minSizenumber10Minimum size as a percentage.
maxSizenumber90Maximum size as a percentage.
classNamestringAdditional CSS classes on the panel.
childrenReactNodePanel content.

All standard <div> HTML attributes are also forwarded.

ResizableHandle

PropTypeDefaultDescription
withHandlebooleanfalseShow a visible grip icon on the handle.
classNamestringAdditional CSS classes on the handle.

All standard <div> HTML attributes are also forwarded.

Motion

  • Grip handle indicator — Subtle opacity and scale animation on hover via whileHover (Framer Motion).
  • Animation respects prefers-reduced-motion via useReducedMotion().

Keyboard Navigation

KeyAction
TabFocus the drag handle.
ArrowLeftDecrease left panel / increase right panel (horizontal mode).
ArrowRightIncrease left panel / decrease right panel (horizontal mode).
ArrowUpDecrease top panel / increase bottom panel (vertical mode).
ArrowDownIncrease top panel / decrease bottom panel (vertical mode).

Accessibility

  • The drag handle renders as role="separator" with aria-orientation set to the perpendicular axis.
  • aria-valuenow, aria-valuemin, and aria-valuemax are set on the handle for assistive technology.
  • The handle is keyboard-focusable (tabIndex={0}) with a visible focus ring.
  • The cursor changes to col-resize (horizontal) or row-resize (vertical) for visual affordance.
  • An invisible hit area (after:: pseudo-element) makes the thin handle easier to grab with a mouse or touch.

Design Tokens

TokenUsage
--borderHandle background color, grip border
--primaryHandle hover highlight color
--ringFocus ring color
--muted-foregroundGrip icon color
--duration-fastTransition speed

On this page