Light

Resizable

Accessible resizable panel groups and layouts with keyboard support.

Spec · from metadata

When to use

  • Split-pane layouts where users need to resize sections (e.g., editor + preview)
  • Adjustable sidebar and main content layouts
  • IDE-style interfaces with resizable panels

When not to use

  • For fixed column layouts — use CSS Grid instead
  • For collapsible sections — use Accordion or Collapsible instead
  • For responsive layouts that only change at breakpoints — use responsive CSS instead

Anti-patterns

Avoid<ResizablePanelGroup><ResizablePanel>A</ResizablePanel><ResizablePanel>B</ResizablePanel></ResizablePanelGroup>
Prefer<ResizablePanelGroup direction="horizontal"><ResizablePanel defaultSize={50}>A</ResizablePanel><ResizableHandle /><ResizablePanel defaultSize={50}>B</ResizablePanel></ResizablePanelGroup>

A ResizableHandle is required between panels for resize interaction, and direction must be specified

Avoid<ResizablePanel defaultSize={30}><ResizablePanel defaultSize={70}>
Prefer<ResizablePanel defaultSize={30}>...<ResizableHandle /><ResizablePanel defaultSize={70}>

Panel sizes must sum to 100 and a handle must separate them

Accessibility

  • Required ARIAaria-orientation (set by react-resizable-panels on handle)
  • Screen readerHandles act as separators; screen readers announce orientation. Consider adding aria-label to panels for context.

Token bindings

TokenCategoryUsage
bg-bordercolorHandle track and grip icon container background
ring-ringcolorHandle focus ring color

Import

import {
  ResizablePanelGroup,
  ResizablePanel,
  ResizableHandle,
} from "@timelycare/helix-ui"

Props

interface ResizablePanelGroupProps {
  direction: "horizontal" | "vertical"
  className?: string
  children: React.ReactNode
}

interface ResizablePanelProps {
  defaultSize?: number      // Percentage (0-100)
  minSize?: number          // Minimum percentage
  maxSize?: number          // Maximum percentage
  collapsible?: boolean
  className?: string
  children: React.ReactNode
}

interface ResizableHandleProps {
  withHandle?: boolean      // Show grip icon
  className?: string
}

Structure

PartTailwind
PanelGroupflex bg-background border border-border rounded-lg
Panelflex-1 overflow-hidden
Handle (line)w-px bg-border (vertical) / h-px bg-border (horizontal)
Handle gripw-3 h-4 bg-border border border-border rounded-xs
Grip iconsize-2.5 text-foreground (GripVertical)

Direction

DirectionLayoutHandle
horizontalSide-by-side panelsVertical divider with grip
verticalStacked panelsHorizontal divider with grip

States

StateImplementation
DefaultHandle grip visible with bg-border
HoverCursor changes to col-resize or row-resize
DraggingActive resize, panels update width/height
Focusfocus-visible:ring-2 focus-visible:ring-ring on handle

Common Patterns

Two Panels Horizontal

<ResizablePanelGroup direction="horizontal" className="rounded-lg border">
  <ResizablePanel defaultSize={50}>
    <div className="flex h-full items-center justify-center p-6">
      <span className="font-semibold">One</span>
    </div>
  </ResizablePanel>
  <ResizableHandle withHandle />
  <ResizablePanel defaultSize={50}>
    <div className="flex h-full items-center justify-center p-6">
      <span className="font-semibold">Two</span>
    </div>
  </ResizablePanel>
</ResizablePanelGroup>

Three Panels with Nesting

<ResizablePanelGroup direction="horizontal" className="rounded-lg border">
  <ResizablePanel defaultSize={25}>
    <div className="flex h-full items-center justify-center p-6">
      <span className="font-semibold">Sidebar</span>
    </div>
  </ResizablePanel>
  <ResizableHandle withHandle />
  <ResizablePanel defaultSize={75}>
    <ResizablePanelGroup direction="vertical">
      <ResizablePanel defaultSize={70}>
        <div className="flex h-full items-center justify-center p-6">
          <span className="font-semibold">Content</span>
        </div>
      </ResizablePanel>
      <ResizableHandle withHandle />
      <ResizablePanel defaultSize={30}>
        <div className="flex h-full items-center justify-center p-6">
          <span className="font-semibold">Footer</span>
        </div>
      </ResizablePanel>
    </ResizablePanelGroup>
  </ResizablePanel>
</ResizablePanelGroup>

Collapsible Sidebar

<ResizablePanelGroup direction="horizontal" className="rounded-lg border">
  <ResizablePanel defaultSize={20} minSize={15} maxSize={30} collapsible>
    <div className="p-4">Sidebar</div>
  </ResizablePanel>
  <ResizableHandle withHandle />
  <ResizablePanel defaultSize={80}>
    <div className="p-4">Main Content</div>
  </ResizablePanel>
</ResizablePanelGroup>

Accessibility

  • Built on react-resizable-panels
  • Keyboard: Arrow keys to resize when handle is focused
  • Handle is focusable with visible focus indicator
  • Screen reader: announces panel sizes
  • Add aria-label on panels for context

Gotchas

ProblemSolution
Panels collapse unexpectedlySet minSize prop on panels
No visible handleAdd withHandle prop to ResizableHandle
Content overflows panelAdd overflow-hidden or overflow-auto to panel
Height not filling containerSet explicit height on PanelGroup or parent

See Also


Last updated: February 9, 2026