Bridge UI

Resizable

Panels the user resizes by dragging the handle or with the keyboard.

Introduction

Resizable splits an area into panels the user resizes by dragging the handle between them or with the keyboard. Resizable is the group, ResizablePanel is each panel, and ResizableHandle sits between two panels.

Sizes are percentages of the group. Panels without defaultSize share the space left. Panel order follows the DOM, so panels and handles can be wrapped or rendered conditionally.

When to use: Editor and IDE layouts, master–detail views, and any split view where the user decides how much room each part gets.

Compared to similar components:

  • Sidebar — App-shell navigation rail with a fixed width, collapse modes, and a mobile drawer. Use Resizable when the user drags the split.
  • Divider — A static separator. Use Resizable when the line between two areas should move.

Use the framework selector in the site header to switch between React and Vue.

Import

import { Resizable } from "@bridge-ui/vue/Components/Resizable";
import { ResizableHandle } from "@bridge-ui/vue/Components/ResizableHandle";
import { ResizablePanel } from "@bridge-ui/vue/Components/ResizablePanel";
import { Resizable } from "@bridge-ui/react/Components/Resizable";
import { ResizableHandle } from "@bridge-ui/react/Components/ResizableHandle";
import { ResizablePanel } from "@bridge-ui/react/Components/ResizablePanel";

Basic usage

Put a ResizableHandle between every two ResizablePanels. The group fills its parent (h-full w-full), so give the parent, or the group through className (Vue: class), a height.

Vertical

orientation="vertical" stacks the panels and turns the handles horizontal.

Grip

Each handle shows a grip by default. hideGrip leaves only the line. Replace the grip content with slots.grip (Vue: #grip) and restyle it with classes.

Size limits

minSize and maxSize bound a panel while dragging and with the keyboard. Once the panel shrinking next to the handle reaches its minSize, the drag goes on to shrink the panels beyond it.

Nested groups

A panel can hold another Resizable, for example a vertical split inside a horizontal one.

Collapsible panel

A collapsible panel collapses to collapsedSize (default 0) once it is dragged past half of minSize. Enter on the handle collapses it and expands it back to its last size. Bind collapsed / onCollapsedChange (Vue: v-model:collapsed) to collapse or expand it from outside. A non-zero collapsedSize keeps a strip visible, for example for an icon rail.

Saving the layout

onLayoutChange (Vue: @layout-change) receives every panel size, in order, after a drag, key press, or collapse. Store it and feed the saved sizes back through defaultSize to restore the layout. ResizablePanel also reports its own size through onResize (Vue: @resize).

Disabled

disabled on a handle locks only that handle. On the group, it locks every handle.

Right to left

dir="rtl" on the group flips the horizontal drag and arrow keys, so the panel on the right is the first one.

<Resizable dir="rtl" class="h-64">
  <ResizablePanel :default-size="25">Sidebar</ResizablePanel>
  <ResizableHandle />
  <ResizablePanel>Content</ResizablePanel>
</Resizable>
<Resizable dir="rtl" className="h-64">
  <ResizablePanel defaultSize={25}>Sidebar</ResizablePanel>
  <ResizableHandle />
  <ResizablePanel>Content</ResizablePanel>
</Resizable>

Defaults and theme

Set app-wide defaults through the registry: Resizable (disabled, keyboardStep, orientation), ResizablePanel (collapsedSize, collapsible, maxSize, minSize), and ResizableHandle (disabled, hideGrip). Resizable.tokens.orientation holds the root, panel, handle, and grip classes for each orientation. See Default props.

<BridgeUIProvider
  :components="{
    Resizable: { defaultProps: { keyboardStep: 5 } },
    ResizableHandle: { defaultProps: { hideGrip: true } },
  }"
>
  <App />
</BridgeUIProvider>
<BridgeUIProvider
  components={{
    Resizable: { defaultProps: { keyboardStep: 5 } },
    ResizableHandle: { defaultProps: { hideGrip: true } },
  }}
>
  <App />
</BridgeUIProvider>

Keyboard

  • Each handle is in the tab order.
  • Horizontal: ArrowLeft / ArrowRight. Vertical: ArrowUp / ArrowDown. dir="rtl" swaps the horizontal arrows.
  • Arrow keys move the handle by keyboardStep percent (default 10).
  • Home shrinks the panel before the handle to its minSize, or collapses it when collapsible. End grows it to its maxSize.
  • Enter collapses or expands a collapsible panel before the handle.

Accessibility

  • Each handle is a focusable role="separator" with aria-valuenow, aria-valuemin, and aria-valuemax for the panel before it, and aria-controls pointing at that panel.
  • aria-orientation is perpendicular to the group: vertical handles in a horizontal group, and the other way around.
  • The handle label is aria-label="Resize panels" and resolves through the i18n adapter. Pass your own aria-label to name what the handle resizes.
  • A disabled handle leaves the tab order and gets aria-disabled.
  • The grip is decorative (aria-hidden="true").

Anatomy

<div> <!-- Resizable root -->
  <div> <!-- ResizablePanel, data-part="panel" -->
  <div role="separator"> <!-- ResizableHandle, data-part="handle" -->
    <div> <!-- grip, aria-hidden -->
  <div> <!-- ResizablePanel -->
</div>

Target parts with classes. Resizable: root. ResizablePanel: root. ResizableHandle: root and grip; customProps.grip forwards extra props to the grip. Panels and handles expose data-orientation; panels add data-collapsed, and handles add data-dragging and data-disabled.

Sidebar, Divider, Card

API

Resizable

Prop Type Default Description
children ReactNode — Panels and handles. Use the default slot in Vue.
classes ResizableClasses — Classes for root.
disabled boolean false Lock every handle in the group.
keyboardStep number 10 Percent an arrow key moves a focused handle.
onLayoutChange (layout: number[]) => void — Called with every panel size (percent), in order, after a change. Vue: @layout-change.
orientation "horizontal" | "vertical" "horizontal" Axis the panels are laid out on.

ResizablePanel

Prop Type Default Description
children ReactNode — Panel content. Use the default slot in Vue.
classes ResizablePanelClasses — Classes for root.
collapsed boolean — Collapsed state. Needs collapsible. Vue: v-model:collapsed.
collapsedSize number 0 Size while collapsed, in percent.
collapsible boolean false Let the panel collapse once it is dragged past half of minSize.
defaultSize number — Initial size, in percent. Panels without one share the space left.
maxSize number 100 Largest size, in percent.
minSize number 0 Smallest size while expanded, in percent.
onCollapsedChange (collapsed: boolean) => void — Called when the panel collapses or expands. Vue: update:collapsed.
onResize (size: number) => void — Called when the panel size changes (percent). Vue: @resize.

ResizableHandle

Prop Type Default Description
classes ResizableHandleClasses — Classes for root and grip.
customProps ResizableHandleCustomProps — Extra props for grip.
disabled boolean false Lock this handle.
hideGrip boolean false Hide the grip and show only the line.
slots ResizableHandleSlots — grip content. Vue: #grip.