react-resplit
Resizable split pane layouts for React applications 🖖
- Compound component API that works with any styling method
- Built with modern CSS, a grid-based layout and custom properties
- Works with any amount of panes in a vertical or horizontal layout
- Built following the Window Splitter pattern for accessibility and keyboard controls
https://github.com/KenanYusuf/react-resplit/assets/9557798/d47ef278-bcb1-4c2b-99e6-7a9f99943f96
Example of a code editor built with react-resplit
Development
Run the development server:
yarn dev
The files for the development app can be found in src
, and the library files in lib
.
Usage
Install the package using your package manager of choice.
npm install react-resplit
Import Resplit
from react-resplit
and render the Root, Pane(s) and Splitter(s).
import { Resplit } from 'react-resplit';
function App() {
return (
<Resplit.Root direction="horizontal">
<Resplit.Pane order={0} initialSize="0.5fr">
Pane 1
</Resplit.Pane>
<Resplit.Splitter order={1} size="10px" />
<Resplit.Pane order={2} initialSize="0.5fr">
Pane 2
</Resplit.Pane>
</Resplit.Root>
);
}
Styling
The Root, Splitter and Pane elements are all unstyled by default apart from a few styles that are necessary for the layout - this is intentional so that the library remains flexible.
Resplit will apply the correct cursor based on the direction
provided to the hook.
As a basic example, you could provide a className
prop to the Splitter elements and style them as a solid 10px divider.
<Resplit.Splitter className="splitter" order={0} size="10px" />
.splitter {
width: 100%;
height: 100%;
background: #ccc;
}
Accessibility
Resplit has been implemented using guidence from the Window Splitter pattern.
In addition to built-in accessibility considerations, you should also ensure that splitters are correctly labelled.
If the primary pane has a visible label, the aria-labelledby
attribute can be used.
<Resplit.Pane order={0}>
<h2 id="pane-1-label">Pane 1</h2>
</Resplit.Pane>
<Resplit.Splitter order={1} aria-labelledby="pane-1-label" />
Alternatively, if the pane does not have a visible label, the aria-label
attribute can be used on the Splitter instead.
<Resplit.Splitter order={1} aria-label="Pane 1" />
API
All of the resplit components extend the React.HTMLAttributes<HTMLDivElement>
interface, so you can pass any valid HTML attribute to them.
Root (ResplitRootProps)
The root component of a resplit layout. Provides context to all child components.
Prop | Type | Default | Description |
---|
direction | "horizontal" | "vertical" | "horizontal" | Direction of the panes |
asChild | boolean | false | Merges props onto the immediate child |
children | ReactNode | | Child elements |
className | string | | Class name |
style | CSSProperties | | Style object |
Pane (ResplitPaneProps)
A pane is a container that can be resized.
Prop | Type | Default | Description |
---|
order | number | | Specifies the order of the resplit child (pane or splitter) in the DOM |
initialSize | ${number}fr | [available space]/[number of panes] | Set the initial size of the pane as a fractional unit (fr) |
minSize | ${number}fr | ${number}px | "0fr" | Set the minimum size of the pane as a fractional (fr) or pixel (px) unit |
collapsible | boolean | false | Whether the pane can be collapsed below its minimum size |
collapsedSize | ${number}fr | ${number}px | "0fr" | Set the collapsed size of the pane as a fractional (fr) or pixel (px) unit |
onResizeStart | () => void | | Callback function that is called when the pane starts being resized. |
onResize | (size: FrValue) => void | | Callback function that is called when the pane is actively being resized. |
onResizeEnd | (size: FrValue) => void | | Callback function that is called when the pane is actively being resized. |
asChild | boolean | false | Merges props onto the immediate child |
children | ReactNode | | Child elements |
className | string | | Class name |
style | CSSProperties | | Style object |
Splitter (ResplitSplitterProps)
A splitter is a draggable element that can be used to resize panes.
Name | Type | Default | Description |
---|
order | number | | Specifies the order of the resplit child (pane or splitter) in the DOM |
size | ${number}px | "10px" | Set the size of the splitter as a pixel unit |
asChild | boolean | false | Merges props onto the immediate child |
children | ReactNode | | Child elements |
className | string | | Class name |
style | CSSProperties | | Style object |
useResplitContext () => ResplitContextValue
The useResplitContext
hook provides access to the context of the nearest Resplit.Root
component.
See the methods below for more information on what is available.
setPaneSizes (paneSizes: FrValue[]) => void
Get the collapsed state of a pane.
Specify the size of each pane as a fractional unit (fr). The number of values should match the number of panes.
setPaneSizes(['0.6fr', '0.4fr']);
If your pane has an onResize
callback, it will be called with the new size.
isPaneCollapsed (order: number) => boolean
Get the collapsed state of a pane.
Note: The returned value will not update on every render and should be used in a callback e.g. used in combination with a pane's onResize
callback.
import { Resplit, useResplitContext, ResplitPaneProps, FrValue } from 'react-resplit';
function CustomPane(props: ResplitPaneProps) {
const { isPaneCollapsed } = useResplitContext();
const handleResize = (size: FrValue) => {
if (isPaneCollapsed(props.order)) {
}
};
return (
<Resplit.Pane
{...props}
initialSize="0.5fr"
collapsedSize="0.2fr"
collapsible
onResize={handleResize}
/>
);
}
function App() {
return (
<Resplit.Root>
<CustomPane order={0} />
<Resplit.Splitter order={1} />
<CustomPane order={2} />
</Resplit.Root>
);
}
isPaneMinSize (order: number) => boolean
Get the min size state of a pane.
Note: The returned value will not update on every render and should be used in a callback e.g. used in combination with a pane's onResize
callback.
import { Resplit, useResplitContext, ResplitPaneProps, FrValue } from 'react-resplit';
function CustomPane(props: ResplitPaneProps) {
const { isPaneMinSize } = useResplitContext();
const handleResize = (size: FrValue) => {
if (isPaneMinSize(props.order)) {
}
};
return <Resplit.Pane {...props} initialSize="0.5fr" minSize="0.2fr" onResize={handleResize} />;
}
function App() {
return (
<Resplit.Root>
<CustomPane order={0} />
<Resplit.Splitter order={1} />
<CustomPane order={2} />
</Resplit.Root>
);
}