react-split-pane

React split-pane component with hooks and TypeScript

react-split-pane downloads react-split-pane version react-split-pane license

react-split-paneSimilar Packages:

Npm Package Weekly Downloads Trend

3 Years
🌟 Show real-time usage chart on react-split-pane's README.md, just copy the code below.
## Usage Trend
[![Usage Trend of react-split-pane](https://npm-compare.com/img/npm-trend/THREE_YEARS/react-split-pane.png)](https://npm-compare.com/react-split-pane#timeRange=THREE_YEARS)

Cumulative GitHub Star Trend

🌟 Show GitHub stars trend chart on react-split-pane's README.md, just copy the code below.
## GitHub Stars Trend
[![GitHub Stars Trend of react-split-pane](https://npm-compare.com/img/github-trend/react-split-pane.png)](https://npm-compare.com/react-split-pane)

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
react-split-pane290,6913,377104 kB325 days agoMIT

README for react-split-pane

React Split Pane v3

Modern, accessible, TypeScript-first split pane component for React.

NPM version NPM license NPM downloads Bundle size

Live Examples

✨ 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% and height: 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

OptionTypeDefaultDescription
keystringRequiredStorage key for persisting sizes
storageStoragelocalStorageStorage backend (localStorage or sessionStorage)
debouncenumber300Debounce 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 step pixels (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

PropTypeDefaultDescription
direction'horizontal' | 'vertical''horizontal'Layout direction
resizablebooleantrueWhether panes can be resized
snapPointsnumber[][]Snap points in pixels
snapTolerancenumber10Snap tolerance in pixels
stepnumber10Keyboard resize step
onResizeStart(event) => void-Called when resize starts
onResize(sizes, event) => void-Called during resize
onResizeEnd(sizes, event) => void-Called when resize ends
classNamestring-CSS class name
styleCSSProperties-Inline styles
dividerComponentType-Custom divider component
dividerClassNamestring-Divider class name
dividerStyleCSSProperties-Divider inline styles

Pane Props

PropTypeDefaultDescription
defaultSizestring | number'50%'Initial size (uncontrolled)
sizestring | number-Controlled size
minSizestring | number0Minimum size
maxSizestring | numberInfinityMaximum size
classNamestring-CSS class name
styleCSSProperties-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

  1. Set explicit height on parent (recommended for most cases):

    .container { height: 100vh; }
    
  2. Use absolute positioning:

    .container { position: absolute; inset: 0; }
    
  3. 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