Skip to content

Behaviors — Splitter

Two resizable panels separated by a draggable handle. Drag the divider, or focus it and use the arrow keys (Home/End jump to the min/max):

Primary
Secondary
<div
and-splitter="horizontal"
and-splitter-min="15"
and-splitter-max="85"
style="height: 8rem;"
>
<div and-splitter-panel="primary">Primary</div>
<div and-splitter-handle style="width: 6px;"></div>
<div and-splitter-panel="secondary">Secondary</div>
</div>

The behavior applies its own flex sizing, cursor, and role="separator" — you only style the surfaces (backgrounds, the handle’s width/color). The container needs a height for horizontal, since panels fill it. and-splitter requires all three children ([and-splitter-panel="primary"], [and-splitter-handle], [and-splitter-panel="secondary"]) to already be present — the behavior doesn’t generate them.

AttributeWhereValuesDefault
and-splittercontainer'horizontal' | 'vertical''horizontal'
and-splitter-mincontainernumber (%)0
and-splitter-maxcontainernumber (%)100
and-splitter-stepcontainernumber (% per arrow key)1
and-splitter-default-positioncontainernumber (%)50
and-splitter-panel="primary"child
and-splitter-handlechild
and-splitter-panel="secondary"child

Dispatched on the container, all bubbles: true:

EventDetailFires
and-splitter-changeSplitterState ({ position, orientation, isDragging }), also composed: trueEvery position update, drag or keyboard
and-splitter-dragstartPointer/touch drag begins
and-splitter-dragendPointer/touch drag ends
import { createSplitter } from '@andersseen/behaviors/splitter';
const splitter = createSplitter(document.getElementById('panels'), {
orientation: 'horizontal',
minSize: 15,
maxSize: 85,
defaultPosition: 30,
});

createSplitter(container, options?) throws if container doesn’t already contain the three children above — it has nothing to size or attach listeners to otherwise.

OptionTypeDefault
orientation'horizontal' | 'vertical''horizontal'
minSizenumber (%)0
maxSizenumber (%)100
stepnumber (% per arrow key)1
defaultPositionnumber (%)50

DEFAULT_SPLITTER_OPTIONS is exported alongside the factory.

MemberSignatureNotes
container / controllerThe container element and the underlying SplitterController state machine.
setPosition(position)(percent: number) => voidMoves the divider programmatically (clamped to minSize/maxSize).
updateOptions(options)(options: Partial<SplitterOptions>) => voidPatch any option — e.g. swap orientation after a breakpoint change.
destroy()() => voidRemoves listeners and every class/attribute the factory added.

The handle gets role="separator", aria-orientation, aria-valuemin/max/now (kept in sync on every position change), and keyboard resize via the arrow keys matching the current orientation plus Home/End for the min/max. If you don’t set aria-label/aria-labelledby on the handle yourself, the behavior adds a default aria-label="Resize panels".

Recipes → Persisting a splitter’s position