Vize

Splitter

Accessible, unstyled resizable panel groups following the WAI-ARIA window splitter pattern.

Package @vizejs/ui/splitter
Maturity stable
Own the source vize lib pull splitter
Requires collection, context, controllable-state, id
Aliases resizable panels, split view, split pane, window splitter, panel group
Covers WAI-ARIA window splitter pattern, react-resizable-panels, Reka UI Splitter, Ark UI Splitter

Usage

import { SplitterGroup, SplitterHandle, SplitterPanel } from "@vizejs/ui/splitter";

Or copy the source into your project with vize lib pull splitter (see Source Distribution).

API

SplitterGroup

Source: src/families/layout/splitter/splitter-group.vue

Props

Prop Type Default Description
id string | null undefined Consumer-owned group id. null and undefined select a deterministic fallback.
layout SplitterLayout undefined Controlled panel sizes in percent (v-model:layout). undefined selects uncontrolled behavior, which also lets useSplitterPersistence load a stored layout after mount.
defaultLayout SplitterLayout undefined Initial sizes by panel index. Panels without an entry use their defaultSize.
orientation SplitterOrientation "horizontal" Layout axis. "horizontal" places panels side by side with vertical separators.
dir SplitterDirection undefined Reading direction used to map horizontal arrow keys and pointer movement. undefined inherits DirectionProvider/LocaleProvider, then "ltr".
disabled boolean false Disable pointer and keyboard resizing while keeping the current layout.
keyboardStep number 10 Percent moved by one arrow key press on a handle.

Events

Event Payload Description
update:layout [layout: SplitterLayout] Fired with the next layout whenever sizes change.
resize [layout: SplitterLayout, reason: SplitterResizeReason] Fired after a distinct layout change with the input that caused it.
resizeStart [handleIndex: number] Fired when a pointer drag starts on a handle.
resizeEnd [layout: SplitterLayout] Fired when a pointer drag ends.

Slots

Slot Slot props Description
default SplitterGroupSlotState SplitterPanel and SplitterHandle children. Receives the resolved layout and drag state.

Exposed

Member Type Description
element HTMLDivElement | null Rendered group element.
id string Group base id.
layout SplitterLayout Resolved layout.
setLayout (layout: SplitterLayout) => boolean Request a new layout. Invalid lengths are rejected and sizes are clamped to constraints.
reset () => boolean Restore defaultLayout or the panel defaults.

SplitterHandle

Source: src/families/layout/splitter/splitter-handle.vue

Props

Prop Type Default Description
id string | null undefined Consumer-owned separator id.
disabled boolean false Disable this handle while keeping the rest of the group resizable.
order number undefined Deterministic order for conditionally rendered handles.
ariaLabel string undefined Accessible name, for example "Resize sidebar".
ariaLabelledby string undefined Space-separated ids that label the separator.

Slots

Slot Slot props Description
default SplitterHandleSlotState Optional grip content. Receives the value, orientation, and drag state.

Exposed

Member Type Description
element HTMLDivElement | null Rendered separator element.
value number Size in percent of the panel before the handle.
focus (options?: FocusOptions) => void Move focus to the separator.

SplitterPanel

Source: src/families/layout/splitter/splitter-panel.vue

Props

Prop Type Default Description
id string | null undefined Consumer-owned panel id, also used by aria-controls on the adjacent handle.
defaultSize number undefined Initial size in percent. Give every panel a size for exact server rendering.
minSize number 0 Smallest expanded size in percent.
maxSize number 100 Largest size in percent.
collapsible boolean false Allow the panel to snap to collapsedSize when dragged past half of minSize.
collapsedSize number 0 Size in percent while collapsed.
order number undefined Deterministic order for conditionally rendered panels.

Events

Event Payload Description
collapse [] Fired when the panel snaps to its collapsed size.
expand [] Fired when a collapsed panel grows back to at least its minimum size.
resize [size: number, previous: number] Fired with the panel's new size whenever it changes.

Slots

Slot Slot props Description
default SplitterPanelSlotState Panel content. Receives the current size and collapsed state.

Exposed

Member Type Description
element HTMLDivElement | null Rendered panel element.
id string Panel id.
size number Current size in percent.
collapsed boolean Whether the panel is collapsed.
collapse () => boolean Collapse a collapsible panel, remembering its size.
expand () => boolean Restore a collapsed panel to its remembered or minimum size.
resize (size: number) => boolean Resize the panel toward size percent through its adjacent handle.

Behavior

Normative state x input -> outcome table for splitter-group.vue, splitter-panel.vue, and splitter-handle.vue (@vizejs/ui/splitter), plus the useSplitterPersistence hook and the pure layout helpers. Every row is proven by the named test in splitter.test.ts, splitter-layout.test.ts, or splitter-ssr.test.ts; compile-only guarantees live in splitter.types.test-d.ts.

The handle follows the WAI-ARIA APG window splitter pattern: a focusable role="separator" whose aria-valuenow is the size of the panel before it (its primary pane, named by aria-controls) and whose aria-orientation is perpendicular to the group axis. Sizes are percentages of the group and the only inline styles are the flex declarations that carry them.

ID State Input Outcome Evidence
S1 default render flex group, flex-grow sizes, separator value/min/max/controls, perpendicular orientation renders the APG window splitter contract with flex-carried sizes
S2 focused handle ArrowLeft/Right, Home, End resize by keyboardStep inside min/max; Home/End jump to the primary pane's bounds arrow keys resize by the keyboard step within constraints; Home and End jump
S3 vertical group / RTL ArrowUp/Down, ArrowLeft vertical groups use block arrows; RTL flips inline arrows vertical groups use ArrowUp/ArrowDown and RTL flips horizontal arrows
S4 collapsible primary pane Enter, Home, ArrowRight Enter collapses and restores the remembered size; collapsed panes grow straight to minSize collapsible panels snap closed past half their minimum and Enter toggles them
S5 pointer drag pointerdown, move, up sizes follow the pointer from the drag origin, clamp to constraints, emit start/end pointer drags resize from the drag origin and report start and end
S6 disabled group keys, pointer no resize, separator leaves the tab order and reports aria-disabled disabled groups and handles ignore keyboard and pointer input
S7 controlled layout resize, invalid layout update events fire while the parent owns sizes; wrong panel counts fall back to defaults controlled layouts wait for the parent and invalid layouts fall back to defaults
S8 exposed instances setLayout, collapse, expand, resize, reset typed imperative layout control with validation exposes group and panel controls for programmatic layout changes
S9 nested groups keys on the inner handle each group owns its own panels and handles nested groups resize independently
S10 useSplitterPersistence mount, resize, malformed storage defaults render first, the stored layout loads after mount, changes are written back useSplitterPersistence loads after mount and writes later layouts
S11 missing provider setup panels and handles fail closed with the shared context diagnostic compound parts require a matching group provider
S12 layout helpers validate, default, clamp, resize remainder sharing, clamping, cascading resize, and midpoint collapse snapping splitter-layout.test.ts
S13 SSR and hydration isolated render/mount byte-identical exact sizes, controlled layouts before registration, hydration without warnings splitter-ssr.test.ts

Notes

  • For exact server rendering give every panel a defaultSize, or pass defaultLayout/layout to the group; unsized panels share the remainder once all panels have registered.
  • Resizing only moves the boundary between the handle's neighbours, cascading through further panels when a neighbour reaches its minimum.