GitHub

@gfazioli/mantine-split-pane

A Mantine component that manages split panes allows users to divide and resize content areas within a layout efficiently

Installation

yarn add @gfazioli/mantine-split-pane

After installation import package styles at the root of your application:

import '@gfazioli/mantine-split-pane/styles.css';

Usage

The main component for creating a Split pane is Split, which accepts two or more children, Split.Pane. Below a very basic example with the main props:

Pane 1

Pane 2

Orientation
Size
Radius
Spacing
Opacity
Color
Hover color
Knob size
Knob opacity
Knob color
Knob hover color
import { Split } from '@gfazioli/mantine-split-pane';
import { Paper } from '@mantine/core';

function Demo() {
  return (
    <Split>
      <Split.Pane>
        <Paper withBorder w="100%" mih="100%">
          <h1>Pane 1</h1>
        </Paper>
      </Split.Pane>

      <Split.Pane>
        <Paper withBorder>
          <h1>Pane 2</h1>
        </Paper>
      </Split.Pane>
    </Split>
  );
}

By setting properties on the main component Split, these will be applied to all child Split.Pane components. However, you can also set specific properties for each Split.Pane component, as shown below.

Pane

The Split.Pane component is a child component of Split and allows setting some props for panel management.

Pane 1

Pane 2

Size
Radius
Spacing
Opacity
Color
Hover color
Knob size
Knob opacity
Knob color
Knob hover color
import { Split } from '@gfazioli/mantine-split-pane';
import { Paper } from '@mantine/core';

function Demo() {
  return (
    <Split>
      <Split.Pane>
        <Paper withBorder w="100%" mih="100%">
          <h1>Pane 1</h1>
        </Paper>
      </Split.Pane>

      <Split.Pane>
        <Paper withBorder w="100%" mih="100%">
          <h1>Pane 2</h1>
        </Paper>
      </Split.Pane>
    </Split>
  );
}

Multiple Panes

You can add as many panes as you want, and you can also set the minWidth and maxWidth props for each pane alogn with other props.

Pane 1

Pane 2

Pane 3

initialWidth={280} maxWidth={300}

Pane 4

import { Split } from '@gfazioli/mantine-split-pane';
import { Code, Paper } from '@mantine/core';

function Demo() {
  return (
    <Split>
      <Split.Pane>
        <Paper withBorder w="100%" mih="100%">
          <h1>Pane 1</h1>
        </Paper>
      </Split.Pane>

      <Split.Pane>
        <Paper withBorder w="100%" mih="100%">
          <h1>Pane 2</h1>
        </Paper>
      </Split.Pane>

      <Split.Pane color="red" initialWidth={280} maxWidth={300}>
        <Paper withBorder w="100%" mih="100%">
          <h1>Pane 3</h1>
          <Code>initialWidth={280} maxWidth={300}</Code>
        </Paper>
      </Split.Pane>

      <Split.Pane>
        <Paper withBorder w="100%" mih="100%">
          <h1>Pane 4</h1>
        </Paper>
      </Split.Pane>
    </Split>
  );
}

Note well

Obviously, you cannot set the minWidth and maxWidth from the Split component, as these props are specific to each individual Split.Pane. As well as you cannot set the orientation of the Split.Pane component, as this is specific to the Split component.

The below code won't work:

<Split>
  <Split.Pane orientation="vertical">
    <h1>Pane 1</h1>
  </Split.Pane>
  <Split.Pane>
    <h1>Pane 2</h1>
  </Split.Pane>
</Split>

The below code won't work:

<Split maxWidth={300}>
  <Split.Pane>
    <h1>Pane 1</h1>
  </Split.Pane>
  <Split.Pane>
    <h1>Pane 2</h1>
  </Split.Pane>
</Split>

Inline

By default, each Split is displayed using flex, but in case you want to display the panels inline, you can use the inline prop.

P 1

P 2

P 1

P 2

Orientation
import { Split } from '@gfazioli/mantine-split-pane';
import { Paper } from '@mantine/core';

function Demo() {
  return (
    <>
      <Split>
        <Split.Pane>
          <Paper withBorder>
            <h1>Pane 1</h1>
          </Paper>
        </Split.Pane>

        <Split.Pane>
          <Paper withBorder>
            <h1>Pane 2</h1>
          </Paper>
        </Split.Pane>
      </Split>
      <Split>
        <Split.Pane>
          <Paper withBorder>
            <h1>Pane 1</h1>
          </Paper>
        </Split.Pane>

        <Split.Pane>
          <Paper withBorder>
            <h1>Pane 2</h1>
          </Paper>
        </Split.Pane>
      </Split>
    </>
  );
}

Initial and mix/max sizes

As mentioned above, you can set the initial size of the panes using the initialWidth and initialHeight props. Obviously, the initialWidth will be used for vertical panes, and the initialHeight for horizontal panes.

Min and Max sizes

You can set the minimum and maximum size of the panes using the minWidth and maxWidth props. Obviously, the minWidth and maxWidth will be used for vertical panes, and the minHeight and maxHeight for horizontal panes.

Pane 1

Pane 2

import { Split } from '@gfazioli/mantine-split-pane';
import { Button, Group, Paper, Stack } from '@mantine/core';
import { useState } from 'react';

function Demo() {
  const [mode, setMode] = useState<'horizontal' | 'vertical'>('vertical');

  return (
    <Stack>
      <Split orientation={mode}>
        <Split.Pane initialWidth={200} initialHeight={300} maxWidth={300} maxHeight={350}>
          <Paper withBorder w="100%" mih="100%">
            <h1>Pane 1</h1>
          </Paper>
        </Split.Pane>

        <Split.Pane>
          <Paper withBorder w="100%" mih="100%">
            <h1>Pane 2</h1>
          </Paper>
        </Split.Pane>
      </Split>
      <Group>
        <Button onClick={() => setMode((c) => (c === 'horizontal' ? 'vertical' : 'horizontal'))}>
          Change mode
        </Button>
      </Group>
    </Stack>
  );
}

Reset with Double Click

You can reset the size of the panes by double-clicking on the separator.

Resize and then
Double Click →

Pane 2

import { Split } from '@gfazioli/mantine-split-pane';
import { Button, Group, Paper, Stack } from '@mantine/core';
import { useState } from 'react';

function Demo() {
  return (
    <Stack>
      <Split>
        <Split.Pane initialWidth={200}>
          <Paper withBorder w="100%" mih="100%">
            <h3>
              Resize and then
              <br />
              Double Click →
            </h3>
          </Paper>
        </Split.Pane>

        <Split.Pane>
          <Paper withBorder w="100%" mih="100%">
            <h1>Pane 2</h1>
          </Paper>
        </Split.Pane>
      </Split>
    </Stack>
  );
}

Custom Double Click

You can also manage double click to swap two different dimensions in rotation. In the example below, double click changes the dimension by alternating the values of the initialWidth property.

Swap
Double Click →

Pane 2

import { Split } from '@gfazioli/mantine-split-pane';
import { Button, Group, Paper, Stack } from '@mantine/core';
import { useState } from 'react';

function Demo() {
  const [initialWidth, setInitialWidth] = useState(200);

  const handleDoubleClick = () => {
    setInitialWidth(initialWidth === 200 ? 100 : 200);
  };

  return (
    <Stack>
      <Split>
        <Split.Pane initialWidth={initialWidth} onDoubleClick={handleDoubleClick}>
          <Paper withBorder w="100%" mih="100%">
            <h3>
              Swap
              <br />
              Double Click →
            </h3>
          </Paper>
        </Split.Pane>

        <Split.Pane>
          <Paper withBorder w="100%" mih="100%">
            <h2>Pane 2</h2>
          </Paper>
        </Split.Pane>
      </Split>
    </Stack>
  );
}

Pane grow

The grow property is only accessible for Split.Pane and enables the panel size to expand and occupy the available space. It is especially handy for establishing a dynamic layout or, as illustrated below, for crafting a layout with two centered panels.

P 1

P 2

import { Split } from '@gfazioli/mantine-split-pane';
import { Paper } from '@mantine/core';

function Demo() {
  return (
    <Split>
      <Split.Pane initialWidth="50%">
        <Paper withBorder w="100%" h="100%">
          <h1>P 1</h1>
        </Paper>
      </Split.Pane>

      <Split.Pane>
        <Paper withBorder w="100%" h="100%">
          <h1>P 2</h1>
        </Paper>
      </Split.Pane>
    </Split>
  );
}

Nested Split

Of course, you can also nest Split components to create more complex layouts.

Pane 1

Pane 2

Pane 3

import { Split } from '@gfazioli/mantine-split-pane';
import { Paper } from '@mantine/core';

function Demo() {
  return (
    <Split>
      <Split.Pane initialWidth={300}>
        <Paper withBorder w="100%" mih="100%">
          <h1>Pane 1</h1>
        </Paper>
      </Split.Pane>

      <Split.Pane>
        <Split orientation="horizontal">
          <Split.Pane>
            <Paper withBorder w="100%" mih="100%">
              <h1>Pane 2</h1>
            </Paper>
          </Split.Pane>

          <Split.Pane>
            <Paper withBorder w="100%" mih="100%">
              <h1>Pane 3</h1>
            </Paper>
          </Split.Pane>
        </Split>
      </Split.Pane>
    </Split>
  );
}

Nested Split with different props

Now you can appreciate the flexibility of the Split component, which allows you to create complex layouts with different props for each Split component.

Pane 1

Pane 2

Pane 3

import { Split } from '@gfazioli/mantine-split-pane';
import { Paper } from '@mantine/core';

function Demo() {
  return (
    <Split>
      <Split.Pane initialWidth={300}>
        <h1>Pane 1</h1>
      </Split.Pane>

      <Split.Pane>
        <Split mode="horizontal">
          <Split.Pane color="red.3" size="md">
            <h1>Pane 2</h1>
          </Split.Pane>

          <Split.Pane>
            <h1>Pane 3</h1>
          </Split.Pane>
        </Split>
      </Split.Pane>
    </Split>
  );
}

Events

The Split.Pane component emits some events that you can use to manage the layout of your application. You may want to use the onResizeStart, onResize, and onResizeEnd events to manage the layout of your application.

Pane 1

Pane 2

Start: falseEnd: Resizing:
Orientation
import { Split } from '@gfazioli/mantine-split-pane';

function Demo() {
  return (
    <Stack>
      <Split>
        <Split.Pane
          onResizeStart={() => {
            setStart(true);
            setEnd(false);
          }}
          onResizeEnd={() => {
            setStart(false);
            setEnd(true);
          }}
          onResizing={(w: string, h: string) => setResizing(`w=${w}, h=${h}`)}
        >
          <Paper withBorder w="100%" mih="100%">
            <h1>Pane 1</h1>
          </Paper>
        </Split.Pane>

        <Split.Pane>
          <Paper withBorder w="100%" mih="100%">
            <h1>Pane 2</h1>
          </Paper>
        </Split.Pane>
      </Split>

      <Code>Start: {start ? 'true' : 'false'}</Code>
      <Code>End: {end ? 'true' : 'false'}</Code>
      <Code>Resizing: {resizing}</Code>
    </Stack>
}

Store the layout

You can store the layout of the panes and restore it later using the browser localStorage or any other storage system. Try to change the layout and refresh the page to see the layout restored.

Pane 1

Pane 2

import { Split } from '@gfazioli/mantine-split-pane';
import { Paper } from '@mantine/core';
import { useLocalStorage } from '@mantine/hooks';

function Demo() {
  const [width, setWidth] = useLocalStorage({
    key: 'split-width',
    defaultValue: 'auto',
  });

  return (
    <Split>
      <Split.Pane initialWidth={width} onResizeEnd={({ width }) => setWidth(width)}>
        <Paper withBorder w="100%" mih="100%">
          <h1>Pane 1</h1>
        </Paper>
      </Split.Pane>

      <Split.Pane>
        <Paper withBorder w="100%" mih="100%">
          <h1>Pane 2</h1>
        </Paper>
      </Split.Pane>
    </Split>
  );
}

Accessibility

You may also use the Keyboard to navigate through the panes. Use the Tab key to navigate through the resizer and the Arrow keys to resize the panes. You may use ◀︎ ▶︎ for horizontal panes and for vertical panes.

By default, the Arrow keys will resize the panes by 8 pixel, but you can change this value using the step prop. Keeping the SHIFT key pressed while using the Arrow keys will resize the panes by 64 pixel, but you can change this value using the shiftStep prop.

Pane 1

Pane 2

Pane 1

Pane 2

Step
Shift step
import { Split } from '@gfazioli/mantine-split-pane';
import { Paper } from '@mantine/core';

function Demo() {
  return (
    <Split step={8} shiftStep={64}>
      <Split.Pane>
        <Paper withBorder w="100%" mih="100%">
          <h1>Pane 1</h1>
        </Paper>
      </Split.Pane>

      <Split.Pane>
        <Paper withBorder w="100%" mih="100%">
          <h1>Pane 2</h1>
        </Paper>
      </Split.Pane>

      <Split.Pane>
        <Split orientation="horizontal">
          <Split.Pane step={1}>
            <Paper withBorder w="100%" mih="100%">
              <h1>Pane 1</h1>
            </Paper>
          </Split.Pane>

          <Split.Pane>
            <Paper withBorder w="100%" mih="100%">
              <h1>Pane 2</h1>
            </Paper>
          </Split.Pane>
        </Split>
      </Split.Pane>
    </Split>
  );
}

Note well

The step and shiftStep props are accessible both for the Split and Split.Pane components. Obviously, the step and shiftStep props of the Split.Pane component will override the step and shiftStep props of the Split component.

...
<Split step={16} shiftStep={128}>
  <Split.Pane step={32} shiftStep={256}>
    <h1>Pane 1</h1>
  </Split.Pane>
  <Split.Pane>
    <h1>Pane 2</h1>
  </Split.Pane>
  <Split.Pane>
    <h1>Pane 3</h1>
  </Split.Pane>
</Split>
...

In the above example, the resizer between the Pane 1 and Pane 2 will resize the panes by 32 pixel, and the resizer between the Pane 2 and Pane 3 will resize the panes by 16 pixel. The SHIFT key will resize the panes by 256 pixel between the Pane 1 and Pane 2 and by 128 pixel between the Pane 2 and Pane 3.