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.
Related components
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. |