react-split-pane: invisible panes, unbounded limits, and a check script that rewrites files
React split-pane component
At a glance
- What is it?
- A TypeScript split pane component for React with hooks, keyboard navigation, ARIA attributes and three published entry points, described as under 5KB gzipped. Its documentation is unusually candid about one failure mode, a parent without explicit dimensions, while the pane size limits default to zero and infinity, and the aggregate check script runs the formatter in write mode.
- Who is it for?
- react-split-pane is a reasonable choice for a resizable two or three pane layout where you want keyboard and screen reader support without writing it, and the accessibility claims are backed by a documented key map rather than a badge. Four things to know.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Activity is slowing. The repository last received commits 6 months ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 9, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Invisible panes come from a parent with no height
One note appears directly under the quick start, and it is the failure everyone hits.
The component sets its own width and height to 100% and measures its container with a ResizeObserver. That means the parent has to have explicit dimensions, or the panes have nothing to fill. For a vertical split the height is the part that goes missing.
The documentation devotes a section to it, naming invisible panes as the common issue and the most frequent cause as a missing height on the parent container. The example shows the two versions side by side, one where the wrapper is a bare div and one where the wrapper carries an explicit viewport height.
Three solutions are offered. Set an explicit height on the parent, which the file recommends for most cases. Use absolute positioning with an inset of zero. Or use flexbox with a growing child. The third example is cut off partway through its height declaration, so the exact value is not shown.
The aggregate check script reformats your source
The scripts are individually sensible: a typecheck that runs the compiler with no output, a lint over the source directory, a format that writes, a format check that does not, and a test run.
The composite script is the problem. It chains the typecheck, then the lint, then the formatter, then the tests, and the formatter it chains is the write-mode one, not the check-mode one. So running the full suite on a clean checkout is not a read-only operation: it rewrites src and examples in place, and any formatting difference becomes part of your next commit.
The non-mutating formatter check exists as a separate script and is never invoked by the composite one. That is the script to wire into a pre-commit hook if you want the suite to fail rather than fix.
Publishing is guarded separately, with a prepublish hook that runs the tests and then the build.
Three published entry points, each built twice
The manifest is explicit about what consumers get. The package is marked as an ES module, and three subpaths are exported: the root, a keyboard module, and a persistence module. A fourth export is the stylesheet, shipped from the repository root rather than the build directory. Each of the three code entries resolves to a declaration file, an ES module build and a CommonJS build.
Alongside that map there are still top-level fields for the main entry, the ES module entry and the type declarations, which older resolvers read and modern ones ignore in favour of the map.
Two details are worth noticing. The persistence hook and the keyboard helpers are separate entry points rather than part of the root, which means they are separate modules with their own build output and can be imported independently. And the published file list is only the build directory and the stylesheet, so source, examples and configuration stay out of the package.
The fifty pixel keyboard step is not in the props table
The keyboard section lists six behaviours. Arrow keys resize by the step value in pixels, ten by default. Shift with an arrow resizes by a larger step, fifty pixels by default. Home minimizes the left or top pane, End maximizes it, and Escape restores the pane sizes to their initial state. Tab moves between dividers.
The props table confirms only one of those numbers. There is a step prop with a default of ten, and no second prop for the larger increment. So the fifty pixel value is documented as behaviour you get, and not as something the public API lets you change.
That is a small gap, and it only matters if you want coarse keyboard movement to be faster or slower than fifty pixels. The larger step is not configurable through any prop the table lists.
usePersistence can return an array too short to index
Sizes can be persisted through a hook imported from the persistence subpath, which saves and restores them to localStorage or sessionStorage. It takes three options: a storage key that is required, a storage backend defaulting to localStorage, and a debounce delay in milliseconds defaulting to 300.
The example is where the detail is. It reads the first two entries out of the returned array and falls back to a literal size when the first is missing:
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>
);
}So the documented usage defends against a restored array being empty or shorter than the pane count, which is what a stale key or a changed layout produces. The fallback is applied to the first pane only, so a layout with three panes restored from a two pane key still has to handle the third itself.
Panes are unbounded until you say otherwise
The pane props table gives two limits and two sizes. The initial size in uncontrolled mode defaults to fifty percent. The controlled size has no default at all. The minimum defaults to zero and the maximum defaults to infinity.
Which means a pane with no constraints can be dragged to nothing, and a layout built from the documented defaults inherits that. The quick start does set a minimum of 200 pixels and an initial size of 300 pixels on its first pane, so the example is better behaved than the defaults.
There is also a mismatch to be aware of between the two size props. They accept either a string or a number, and the documented defaults are expressed as strings, a percentage and the words infinity. A number is therefore not interchangeable with a percentage, and the maximum default is a value rather than an unset flag, which means a layout can check it but cannot treat it as absent.
Six examples in the tree, none for the hook or the keyboard
The examples directory holds six named programs: basic, controlled, nested, percentage, snap points and styled, plus the entry HTML, the mount script, a stylesheet and its own Vite config. A pair of scripts run them, one serving and one building.
The set covers the layout modes well and the advanced features unevenly. There is no example for persistence and none for the keyboard handling, even though both are documented features with their own entry points. Snap points get a file; the hook that writes to localStorage does not.
Two other documents sit at the top of the repository alongside the readme, a changelog, a migration guide and a Tailwind guide. The migration guide is the interesting one, since the readme is titled for version 3 while the default branch is master and the most recent tags are v3.0.5 from January 2026, v3.1.0 and v3.2.0 from February 2026, the last two a day apart.
Editorial conclusion
react-split-pane is a reasonable choice for a resizable two or three pane layout where you want keyboard and screen reader support without writing it, and the accessibility claims are backed by a documented key map rather than a badge. Four things to know. Size your container before anything else, because the component fills its parent and a parent with no height produces nothing visible. Panes are unbounded unless you set the limits, since the minimum defaults to zero and the maximum to infinity. The fifty pixel keyboard step is documented in the keyboard section but absent from the props table, so it cannot be configured from the props you can see. And when you run the aggregate checks locally, expect them to reformat your source rather than only report on it, because that script chains the write-mode formatter.
Frequently asked questions
How do I install react-split-pane?
Three commands are given: npm install react-split-pane, yarn add react-split-pane, or pnpm add react-split-pane. The published version is 3.2.0, and the package ships ES module and CommonJS builds plus its own type declarations.
Why are my react-split-pane panes invisible?
The component sets its width and height to 100% and measures its container with a ResizeObserver, so the parent element needs explicit dimensions. A missing height is the most common cause, especially for vertical splits, and the recommended fix is a defined height such as height: 100vh on the parent.
How do I make react-split-pane remember pane sizes?
Use the usePersistence hook, imported from react-split-pane/persistence, which saves and restores sizes to localStorage or sessionStorage. It takes a required key, a storage backend that defaults to localStorage, and a debounce delay in milliseconds that defaults to 300.
What keyboard shortcuts does react-split-pane support?
Arrow keys resize by the step value in pixels, ten by default, and shift with an arrow resizes by a larger step of fifty pixels. Home minimizes and End maximizes the left or top pane, Escape restores the initial sizes, and Tab moves between dividers.
How do I style the divider in react-split-pane?
Pass a component to the divider prop that spreads the incoming props and merges its own style over props.style, or use the dividerClassName and dividerStyle props to apply a CSS class and inline styles without writing a component.
Does react-split-pane work with TypeScript?
It ships its own declarations. Each entry in the exports map has a types condition alongside its import and require conditions, and a typecheck script runs the compiler with no output. The build emits declaration files as a separate step after the bundler runs.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/tomkp-react-split-pane)