SplitViews
A lightweight, framework-agnostic split-pane library for resizable layouts.
Zero dependencies. Modern Pointer Events. CSS-variable driven sizing.
- Modern: Uses Pointer Events +
setPointerCapture(no global listeners) - Performant: Batched DOM writes, CSS variables for sizes, minimal reflows
- Composable: Works with nested splits, no framework required
- Accessible: ARIA roles, orientations, keyboard focusable gutters
Or include it via jsDelivr CDN (UMD):
Quick Start
HTML:
<div>Left Panediv>
<div>Right Panediv>
div>
JavaScript:
const split = SplitViews({
root: "#editor",
direction: "horizontal", // 'vertical' or 'horizontal' (default)
gutterSize: 8,
sizes: [30, 70], // percentages; defaults to equal split
minSize: [120, 200], // px per pane or single px applied to all
snapOffset: 8, // px tolerance before snapping to min
onDrag: (sizes) => {
console.log("resizing...", sizes);
},
onDragEnd: (sizes) => {
console.log("final sizes:", sizes);
},
});
Options
| Option | Type | Default | Description |
|---|---|---|---|
root |
HTMLElement | string |
required | Container element or selector. Children become panes. |
direction |
'horizontal' | 'vertical' |
'horizontal' |
Split direction. |
gutterSize |
number |
10 |
Gutter thickness in px. |
gutterClassName |
string |
'split-gutter' |
Class applied to each gutter. |
minSize |
number | number[] |
0 |
Per-pane minimum size in px (array) or single px for all. |
sizes |
number[] |
equal split | Initial sizes in percentages. |
snapOffset |
number |
0 |
Extra px tolerance before snapping to min. |
onDrag |
(sizes:number[])=>void |
-- | Called on every drag frame. |
onDragEnd |
(sizes:number[])=>void |
-- | Called when dragging stops. |
API
destroy(): Removes gutters and resets stylessetSizes(sizes: number[]): Programmatically set pane sizesgetSizes(): number[]: Get current pane sizes
Styling
The library sets:
display: flexandflex-directiononrootflex-basisper pane (via CSS variables)- Basic
cursorandtouch-actionon gutters
You control the look of gutters:
contain: layout size style; /* hint for performance */
}
.split-gutter {
background: transparent;
position: relative;
}
/* Visible hairline */
.split-gutter::before {
content: "";
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.1);
}
.split-gutter:hover::before {
background: rgba(0, 0, 0, 0.2);
}
CSS variables you can use:
--pane-: applied as-size flex-basisfor each pane--split-gutter-size: gutter thickness--split-cursor: cursor type (col-resize/row-resize)
Accessibility
Gutters automatically have:
role="separator"aria-orientation="vertical"(for horizontal split) or"horizontal"(for vertical split)tabIndex="0"so they can be focused
Nested Splits
You can nest multiple instances:
const inner = SplitViews({
root: '#root [data-split-pane="1"]',
direction: "vertical",
});
Each instance manages only its direct children.
Performance Notes
- Uses
setPointerCapture- no global listeners. - DOM writes batched with
requestAnimationFrame. - CSS variables used for sizes - fast style recalculation.
- Avoid heavy work in
onDrag; debounce if needed. - Clean
destroy()ensures no leaks.
Browser Support
- Chromium, Firefox, Safari (modern versions with Pointer Events).
- No IE support (Pointer Events required).
License
MIT (c) 2025