React Split Pane v3: The Classic Layout Splitter Gets a Modern Makeover
If you have been building React apps for a while, chances are you have crossed paths with React Split Pane. For years it was the go-to library for resizable split layouts -- code editors, file browsers, dashboard panels, you name it. But that original v0.1.x codebase was showing its age: class components, no TypeScript, limited to exactly two panes, and no accessibility story to speak of.
That all changed with react-split-pane v3. Released in late 2025 and actively maintained into 2026, version 3 is a complete ground-up rewrite. Same package name, entirely new internals. Let us walk through what is new, what broke, and how to upgrade.
Everything Old Is New Again
The headline features of v3 read like a wishlist from the community:
- Hooks-based architecture replacing class components entirely
- Full TypeScript support with types included out of the box
- React 17, 18, and 19 compatibility so you are covered no matter which version you are on
- Accessibility built in with keyboard navigation, ARIA attributes, and screen reader support
- Multi-pane layouts supporting two or more panes in a single
SplitPane - Zero runtime dependencies at under 5KB gzipped
- Snap points for snapping dividers to predefined positions
- Built-in persistence hook for saving layouts to localStorage or sessionStorage
- CSS custom properties for easy theming, including dark mode
That is a lot of ground to cover. Let us start with the basics.
Getting It Installed
The package name has not changed, so installation is the same as before:
npm install react-split-pane
# or
yarn add react-split-pane
You will need React 17 or later and Node.js 20 or later. IE11 support has been dropped.
Your First v3 Split
A Simple Side-by-Side
The API has changed significantly from v0.1.x. Children are now wrapped in explicit Pane components, and size constraints live on each Pane rather than on the parent:
import { SplitPane, Pane } from "react-split-pane";
import "react-split-pane/styles.css";
const App = () => {
return (
<SplitPane direction="horizontal">
<Pane minSize={200} defaultSize={300}>
<nav>Sidebar</nav>
</Pane>
<Pane>
<main>Main Content</main>
</Pane>
</SplitPane>
);
};
Note the optional stylesheet import. Version 3 ships a default CSS file with sensible divider styling and dark mode support via prefers-color-scheme. You can skip it and style everything yourself if you prefer.
More Than Two Panes
One of the most requested features was multi-pane support. In v0.1.x, you had to nest SplitPane components to get more than two panels. Now you just add more Pane children:
import { SplitPane, Pane } from "react-split-pane";
import "react-split-pane/styles.css";
const ThreeColumnLayout = () => {
return (
<SplitPane direction="horizontal">
<Pane minSize={150} defaultSize={200}>
<aside>File Tree</aside>
</Pane>
<Pane minSize={300}>
<section>Editor</section>
</Pane>
<Pane minSize={200} defaultSize={250}>
<aside>Preview</aside>
</Pane>
</SplitPane>
);
};
Each divider between panes is independently draggable. Three panes, two dividers, no nesting required.
Listening to Resize Events
The callback API is cleaner in v3. The onResize handler receives an array of all pane sizes, and as of v3.2.0, onResizeStart and onResizeEnd include the pointerType so you can distinguish between mouse, touch, and pen input:
import { SplitPane, Pane } from "react-split-pane";
const ResizeAwareLayout = () => {
return (
<SplitPane
direction="horizontal"
onResize={(sizes) => {
console.log("Current sizes:", sizes);
}}
onResizeEnd={({ pointerType }) => {
console.log(`Resize finished via ${pointerType}`);
}}
>
<Pane defaultSize="40%">
<div>Left</div>
</Pane>
<Pane>
<div>Right</div>
</Pane>
</SplitPane>
);
};
Power Features
Snap Points for Precise Layouts
Snap points let you define positions where the divider "sticks" as the user drags. This is great for layouts where you want a collapsed sidebar state and a fully open state with nothing awkward in between:
import { SplitPane, Pane } from "react-split-pane";
const SnappingSidebar = () => {
return (
<SplitPane
direction="horizontal"
snapPoints={[60, 250]}
snapTolerance={20}
>
<Pane minSize={60} defaultSize={250}>
<nav>Sidebar</nav>
</Pane>
<Pane>
<main>Content</main>
</Pane>
</SplitPane>
);
};
Drag the divider near 60px and it snaps to a collapsed icon-only sidebar. Drag it near 250px and it snaps to the fully expanded state. The snapTolerance controls how close you need to get before the snap kicks in.
Remembering Layouts with usePersistence
In the old version, persisting pane sizes meant wiring up localStorage yourself. Version 3 ships a dedicated hook:
import { SplitPane, Pane } from "react-split-pane";
import { usePersistence } from "react-split-pane/persistence";
const PersistentLayout = () => {
const { sizes, onResize } = usePersistence({
key: "editor-layout",
storage: localStorage,
debounce: 300,
});
return (
<SplitPane direction="horizontal" onResize={onResize}>
<Pane defaultSize={sizes?.[0] ?? 300}>
<nav>Sidebar</nav>
</Pane>
<Pane>
<main>Editor</main>
</Pane>
</SplitPane>
);
};
The hook handles serialization, debounced writes, and restoring sizes on mount. Switch localStorage to sessionStorage if you only want the layout remembered for the current tab.
Custom Dividers and CSS Theming
The divider prop accepts a custom React component, giving you full control over the drag handle appearance. And even without a custom component, the CSS custom properties make quick styling easy:
import { SplitPane, Pane } from "react-split-pane";
import "react-split-pane/styles.css";
const ThemedSplit = () => {
return (
<SplitPane
direction="horizontal"
style={{
"--split-pane-divider-size": "6px",
"--split-pane-divider-color": "#e2e8f0",
"--split-pane-divider-color-hover": "#3b82f6",
"--split-pane-focus-color": "#3b82f6",
} as React.CSSProperties}
>
<Pane defaultSize="50%">
<div>Left</div>
</Pane>
<Pane>
<div>Right</div>
</Pane>
</SplitPane>
);
};
Keyboard Navigation
Accessibility was a first-class concern in v3. Every divider is keyboard-accessible out of the box:
- Arrow keys resize by 10px per press
- Shift + Arrow resizes by 50px for faster adjustment
- Home / End collapse or expand to the minimum or maximum size
- Tab moves focus between dividers
No extra props needed. It just works.
Migrating from v0.1.x
The API changes are significant but mechanical. Here is a quick reference:
| v0.1.x | v3 |
|---|---|
split="vertical" |
direction="horizontal" |
split="horizontal" |
direction="vertical" |
| Bare children | Wrap in <Pane> |
minSize on SplitPane |
minSize on individual <Pane> |
defaultSize on SplitPane |
defaultSize on individual <Pane> |
onDragFinished |
onResizeEnd |
resizerStyle prop |
CSS custom properties or divider prop |
The naming swap between split and direction is the most confusing part. In v0.1.x, split="vertical" meant the divider was vertical (panes side by side). In v3, direction="horizontal" means the panes are arranged horizontally, which is the same visual result. Same layout, different naming convention.
One gotcha to watch for: SplitPane uses width: 100% and height: 100%, so its parent container must have explicit dimensions. For vertical direction splits, make sure the parent has an explicit height like height: 100vh. This was true in v0.1.x too, but it trips up a lot of newcomers.
Wrapping Up
React Split Pane v3 is a serious upgrade. The library went from a battle-tested but aging class component to a modern, accessible, TypeScript-native toolkit that supports multi-pane layouts, snap points, built-in persistence, and clean CSS theming. The migration from v0.1.x requires updating your component tree, but the new API is more explicit and more capable.
If you are already using react-split-pane in a project, v3 is worth the upgrade for the accessibility improvements and React 19 support alone. If you are starting fresh and evaluating options, react-split-pane now competes on features while keeping a simpler, more opinionated API than headless alternatives. At under 5KB with zero dependencies, it earns its place in the toolkit.