Modern, accessible, TypeScript-first split pane component for React.
⨠Features
- đĒ Hooks-based - Built with modern React patterns
- đ TypeScript - Full type safety out of the box
- âŋ Accessible - Keyboard navigation, ARIA attributes, screen reader support
- đą Touch-friendly - Full mobile/tablet support
- đ¯ Flexible - Controlled/uncontrolled modes, nested layouts, 2+ panes
- đĒļ Lightweight - < 5KB gzipped
- ⥠Performant - RAF-throttled resize, optimized renders
- đ¨ Customizable - Full styling control
Installation
npm install react-split-pane # or yarn add react-split-pane # or pnpm add react-split-pane
Quick Start
import { SplitPane, Pane } from 'react-split-pane'; function App() { return ( <SplitPane direction="horizontal"> <Pane minSize="200px" defaultSize="300px"> <Sidebar /> </Pane> <Pane> <MainContent /> </Pane> </SplitPane> ); }
Note: SplitPane requires its container to have explicit dimensions. The component uses
width: 100%andheight: 100%, so the parent element must have a defined size. For vertical splits, ensure the parent has an explicit height (e.g.,height: 100vh). See Container Sizing for details.
Basic Usage
Horizontal Split (Side-by-Side)
<SplitPane direction="horizontal"> <Pane defaultSize="25%"> <LeftPanel /> </Pane> <Pane> <RightPanel /> </Pane> </SplitPane>
Vertical Split (Top-Bottom)
<SplitPane direction="vertical"> <Pane defaultSize="100px"> <Header /> </Pane> <Pane> <Content /> </Pane> </SplitPane>
Controlled Mode
function App() { const [sizes, setSizes] = useState([300, 500]); return ( <SplitPane onResize={setSizes}> <Pane size={sizes[0]} minSize="200px"> <Sidebar /> </Pane> <Pane size={sizes[1]}> <Main /> </Pane> </SplitPane> ); }
Nested Layouts
<SplitPane direction="vertical"> <Pane defaultSize="60px"> <Header /> </Pane> <SplitPane direction="horizontal"> <Pane defaultSize="250px" minSize="150px"> <Sidebar /> </Pane> <SplitPane direction="vertical"> <Pane> <Editor /> </Pane> <Pane defaultSize="200px"> <Console /> </Pane> </SplitPane> </SplitPane> </SplitPane>
Advanced Features
Persistence
The usePersistence hook saves and restores pane sizes to localStorage (or sessionStorage):
import { usePersistence } from 'react-split-pane/persistence'; function App() { const [sizes, setSizes] = usePersistence({ key: 'my-layout' }); return ( <SplitPane onResize={setSizes}> <Pane size={sizes[0] || 300}> <Sidebar /> </Pane> <Pane size={sizes[1]}> <Main /> </Pane> </SplitPane> ); }
usePersistence Options
| Option | Type | Default | Description |
|---|---|---|---|
key |
string |
Required | Storage key for persisting sizes |
storage |
Storage |
localStorage |
Storage backend (localStorage or sessionStorage) |
debounce |
number |
300 |
Debounce delay in ms before saving |
// Use sessionStorage instead of localStorage const [sizes, setSizes] = usePersistence({ key: 'my-layout', storage: sessionStorage, debounce: 500, });
Snap Points
<SplitPane snapPoints={[200, 400, 600]} snapTolerance={20} > {/* panes */} </SplitPane>
Custom Divider
function CustomDivider(props) { return ( <div {...props} style={{ ...props.style, background: 'blue' }}> <GripIcon /> </div> ); } <SplitPane divider={CustomDivider}> {/* panes */} </SplitPane>
Keyboard Navigation
The divider is fully keyboard accessible:
- Arrow Keys: Resize by
steppixels (default: 10px) - Shift + Arrow: Resize by larger step (default: 50px)
- Home: Minimize left/top pane
- End: Maximize left/top pane
- Escape: Restore pane sizes to initial state
- Tab: Navigate between dividers
API Reference
SplitPane Props
| Prop | Type | Default | Description |
|---|---|---|---|
direction |
'horizontal' | 'vertical' |
'horizontal' |
Layout direction |
resizable |
boolean |
true |
Whether panes can be resized |
snapPoints |
number[] |
[] |
Snap points in pixels |
snapTolerance |
number |
10 |
Snap tolerance in pixels |
step |
number |
10 |
Keyboard resize step |
onResizeStart |
(event) => void |
- | Called when resize starts |
onResize |
(sizes, event) => void |
- | Called during resize |
onResizeEnd |
(sizes, event) => void |
- | Called when resize ends |
className |
string |
- | CSS class name |
style |
CSSProperties |
- | Inline styles |
divider |
ComponentType |
- | Custom divider component |
dividerClassName |
string |
- | Divider class name |
dividerStyle |
CSSProperties |
- | Divider inline styles |
Pane Props
| Prop | Type | Default | Description |
|---|---|---|---|
defaultSize |
string | number |
'50%' |
Initial size (uncontrolled) |
size |
string | number |
- | Controlled size |
minSize |
string | number |
0 |
Minimum size |
maxSize |
string | number |
Infinity |
Maximum size |
className |
string |
- | CSS class name |
style |
CSSProperties |
- | Inline styles |
Container Sizing
SplitPane uses width: 100% and height: 100% and measures its container via ResizeObserver. The parent container must have explicit dimensions for panes to render correctly.
Common Issue: Invisible Panes
If your pane content doesn't appear, the most common cause is a missing height on the parent container. This is especially true for vertical splits:
// â Won't work - parent has no height function App() { return ( <div> <SplitPane direction="vertical"> <Pane><div>Top</div></Pane> <Pane><div>Bottom</div></Pane> </SplitPane> </div> ); } // â Works - parent has explicit height function App() { return ( <div style={{ height: '100vh' }}> <SplitPane direction="vertical"> <Pane><div>Top</div></Pane> <Pane><div>Bottom</div></Pane> </SplitPane> </div> ); }
Solutions
-
Set explicit height on parent (recommended for most cases):
.container { height: 100vh; }
-
Use absolute positioning:
.container { position: absolute; inset: 0; }
-
Use flexbox with flex-grow:
.parent { display: flex; flex-direction: column; height: 100vh; } .container { flex: 1; }
Styling
Default Stylesheet
Import the optional default styles with CSS custom properties:
import 'react-split-pane/styles.css';
Customize via CSS variables:
.my-split-pane { --split-pane-divider-size: 8px; --split-pane-divider-color: #e0e0e0; --split-pane-divider-color-hover: #b0b0b0; --split-pane-focus-color: #2196f3; }
The default styles include dark mode support via prefers-color-scheme.
Basic Styles
.split-pane { height: 100vh; } .split-pane-divider { background: #e0e0e0; transition: background 0.2s; } .split-pane-divider:hover { background: #b0b0b0; } .split-pane-divider:focus { outline: 2px solid #2196f3; outline-offset: -2px; }
Expanded Hover Area
This classic pattern creates a thin visible divider with a larger grabbable area that reveals on hover:
.split-pane-divider { background: #000; opacity: 0.2; z-index: 1; box-sizing: border-box; background-clip: padding-box; } .split-pane-divider:hover { transition: all 0.2s ease; } .split-pane-divider.horizontal { width: 11px; margin: 0 -5px; border-left: 5px solid rgba(255, 255, 255, 0); border-right: 5px solid rgba(255, 255, 255, 0); cursor: col-resize; } .split-pane-divider.horizontal:hover { border-left: 5px solid rgba(0, 0, 0, 0.5); border-right: 5px solid rgba(0, 0, 0, 0.5); } .split-pane-divider.vertical { height: 11px; margin: -5px 0; border-top: 5px solid rgba(255, 255, 255, 0); border-bottom: 5px solid rgba(255, 255, 255, 0); cursor: row-resize; } .split-pane-divider.vertical:hover { border-top: 5px solid rgba(0, 0, 0, 0.5); border-bottom: 5px solid rgba(0, 0, 0, 0.5); }
Minimal Divider
A subtle single-pixel divider:
.split-pane-divider.horizontal { width: 1px; margin: 0; background: linear-gradient(to right, transparent, #ccc, transparent); } .split-pane-divider.vertical { height: 1px; margin: 0; background: linear-gradient(to bottom, transparent, #ccc, transparent); }
Tailwind CSS & shadcn/ui
React Split Pane works seamlessly with Tailwind CSS and shadcn/ui. See TAILWIND.md for detailed integration examples including custom dividers and CSS variable overrides.
Migration from v0.1.x or v2.x
See MIGRATION.md for detailed migration guide.
Quick changes:
// v0.1.x <SplitPane split="vertical" minSize={50} defaultSize={100}> <div>Pane 1</div> <div>Pane 2</div> </SplitPane> // v3 <SplitPane direction="horizontal"> <Pane minSize="50px" defaultSize="100px"> <div>Pane 1</div> </Pane> <Pane> <div>Pane 2</div> </Pane> </SplitPane>
Browser Support
- Chrome/Edge (latest 2 versions)
- Firefox (latest 2 versions)
- Safari (latest 2 versions)
- Mobile browsers (iOS Safari, Chrome Android)
Note: IE11 is not supported. Use v0.1.x for IE11 compatibility.
Contributing
Contributions are welcome! Please see CONTRIBUTING.md.
License
MIT Š tomkp