Skip to content

Resizable Split Pane

A split view with pointer capture, pixel minimums resolved against real width, and a separator you can actually tab to.

Prominimalbrutalist
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.

Preview

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

46
140
2
No runtime dependencies
"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
  // ---------------------------------------------------------------
}
Notes

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.

Related

Pairs well with

Matched on shared tags and category — the entries most likely to be used alongside this one.

A search icon that smoothly expands into a full text input on click.

searchinputexpand
Featured
New

A status pill that resizes itself fluidly around whatever content it holds.

islandnotchlayout
Featured
NewPro

A tile field that leans toward the pointer and brightens with proximity.

gridmagneticpointer