Resizable Split Pane
A split view with pointer capture, pixel minimums resolved against real width, and a separator you can actually tab to.
- Category
- Components
- Added
- 2026-09-21
- Deps
- none
- Updated
- 2026-09-21
This is a viberdy Pro component
The live preview, the props panel and the write-up are open to everyone. The full source, the AI prompt and CLI install are part of Pro.
Files
- index.tsx
- workbench.tsx
- registry.ts
- types.ts
- utils.ts
- theme.css
Detail
Drag the divider, or focus it and use the arrow keys. Home and End go to the bounds, Enter restores the default, double-click resets.
46% / 54%
Props
"use client";
import { useRef, useState, type ReactNode } from "react";
export function ResizableSplitPane({
first,
second,
initial = 46,
minPx = 140,
orientation = "horizontal",
step = 2,
onResize,
}: {
first?: ReactNode;
second?: ReactNode;
/** Starting size of the first pane, as a percentage. */
initial?: number;
/** Minimum size of EITHER pane, in pixels. */
// ---------------------------------------------------------------
// Full source available with a viberdy Pro subscription.
// https://viberdy.dev/pricing
// ---------------------------------------------------------------
}About this component
A split pane is four lines of flexbox and four bugs. The first is that a drag handled with document-level move listeners, or worse with listeners on the handle itself, dies the moment the pointer outruns a six-pixel target — which on any fast drag is immediately; pointer capture fixes it in one line and removes the need for document listeners at all, so there is nothing left to leak. The second is the clamp: minimum sizes are naturally written in pixels while the split itself is a percentage, and clamping the percentage against a fixed number lets a pane collapse under its minimum in a narrow container while refusing to reach it in a wide one, so the bound has to be recomputed from the measured size on every move. The third is that the separator is almost never a real control — here it carries the separator role with a current value, takes focus, moves on arrow keys, jumps to the extremes on Home and End and restores the default on Enter, because a split that can only be dragged is unusable by keyboard. And the fourth is invisible until content gets wide: flex items default to a minimum size of auto, so without an explicit zero minimum a wide child silently refuses to shrink and the panes stop matching the percentage they are told to use.
Curator’s note
Every resizer that only works if you drag slowly is missing setPointerCapture. It is one line.
Pairs well with
Matched on shared tags and category — the entries most likely to be used alongside this one.
Expandable Search Bar
componentsA search icon that smoothly expands into a full text input on click.
Morphing Island Notch
componentsA status pill that resizes itself fluidly around whatever content it holds.
Magnetic Tile Grid
backgroundsA tile field that leans toward the pointer and brightens with proximity.