Mantine Split Pane

Undolog

@gfazioli/mantine-split-pane

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

Update to v2

Note well

This version of the package is a complete rewrite of the previous version. We have rewritten the component to address some limitations of the previous version, particularly with multiple panes. We have made the Split.Resizer component visible and usable so that it can be customized and managed more easily. The API has changed, and some props have been removed or modified.

↓ Follow the instructions below to update your code ↓

Breaking changes

  • Added the new Split.Resizer component to manage the resizer between the panes. The Split.Resizer component is now a child of the Split component and is used to manage the resizing of the panes.
  • The Split component structure has changed. The Split component now needs the Split.Pane component as as well as the Split.Resizer component.
  • The Split.Pane component props have changed. We have moved the resizer props to the Split.Resizer component. The Split.Pane component now only accepts props related to the pane itself.
  • The Split.Pane onDoubleClick prop has been removed. You can now use the onResetInitialSize event to manage the double click event. The onResetInitialSize event is triggered when the user double clicks on the resizer to reset the initial size of the pane.
  • You can still use the onDoubleClick prop to manage the double click event in the Split.Resizer component.
  • The step and shiftStep props are now accessible both for the Split and Split.Resizer components. Those props are not accessible for the Split.Pane component.

JSX structure v1

<Split>
  <Split.Pane>
    <h1>Pane 1</h1>
  </Split.Pane>

  <Split.Pane>
    <h1>Pane 2</h1>
  </Split.Pane>
</Split>

JSX structure v2

<Split>
  <Split.Pane>
    <h1>Pane 1</h1>
  </Split.Pane>

  <Split.Resizer />

  <Split.Pane>
    <h1>Pane 2</h1>
  </Split.Pane>
</Split>
  • In the Split.Pane component all props related to the resizer have been removed. Now you can use the Split.Resizer component to manage the resizer between the panes. Have a look at the Props section for more information.

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';

You can import styles within a layer @layer mantine-split-pane by importing @gfazioli/mantine-split-pane/styles.layer.css file.

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

Usage

The main component for creating a Split pane is Split, which accepts two or more children, Split.Pane. Between each Split.Pane you need to add a Split.Resizer component. The Split.Resizer component is used to manage the resizing of the panes.

Pane number 1

Pane number 2

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

function Demo() {
  const paperProps = { withBorder: true, w: '100%', h: '100%' };

  return (
    <Split >
      <Split.Pane>
        <Paper {...paperProps}>
          <Title>Pane number 1</Title>
        </Paper>
      </Split.Pane>

      <Split.Resizer />

      <Split.Pane>
        <Paper {...paperProps}>
          <Title>Pane number 2</Title>
        </Paper>
      </Split.Pane>
    </Split>
  );
}

Note well

In the example above, the Paper component's width and height are set to 100% to fill the available space. Below are examples without this setting.

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.

Pane 1a

Pane 2a

Pane 1b

Pane 2b

Orientation
import { Split, type SplitProps } from '@gfazioli/mantine-split-pane';
import { Paper, Title } from '@mantine/core';
function Demo() {
  return (
    <>
      <Split

      >
        <Split.Pane>
          <Paper withBorder>
            <Title>Pane 1a</Title>
          </Paper>
        </Split.Pane>

        <Split.Resizer />

        <Split.Pane>
          <Paper withBorder>
            <Title>Pane 2a</Title>
          </Paper>
        </Split.Pane>
      </Split>

      <Split

      >
        <Split.Pane>
          <Paper withBorder>
            <Title>Pane 1b</Title>
          </Paper>
        </Split.Pane>

        <Split.Resizer />

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

Split.Resizer component

Starting from version 2, the Split.Resizer component is a child component of Split and allows setting some props for the resizer management. It is the component that will be used to resize the panes.

You don't need to set the Split.Resizer properties for each resizer, as they inherit these properties from the Split component.

In the example below, the first instance shows the Split.Resizer component inheriting props from the Split component. In contrast, the second instance shows the Split.Resizer component with its own props.

Pane 1

Pane 2

Pane 3

Pane 1

Pane 2

Pane 2

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

function Demo() {
  return (
    <Stack>
      <Split color="red" size="xl">
        <Split.Pane>
          <Paper withBorder>
            <Title>Pane 1</Title>
          </Paper>
        </Split.Pane>

        <Split.Resizer />

        <Split.Pane>
          <Paper withBorder>
            <Title>Pane 2</Title>
          </Paper>
        </Split.Pane>

        <Split.Resizer />

        <Split.Pane>
          <Paper withBorder>
            <Title>Pane 3</Title>
          </Paper>
        </Split.Pane>
      </Split>

      <Split>
        <Split.Pane>
          <Paper withBorder>
            <Title>Pane 1</Title>
          </Paper>
        </Split.Pane>

        <Split.Resizer color="red" size="xl" />

        <Split.Pane>
          <Paper withBorder>
            <Title>Pane 2</Title>
          </Paper>
        </Split.Pane>

        <Split.Resizer color="green" size="md" />

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

onResizeStart, onResizing, and onResizeEnd events

The Split.Resizer provides the same events onResizeStart, onResizing, and onResizeEnd that are available in the Split.Pane. However, pay attention to the difference in the parameter found in onResizing and onResizeEnd. In the case of the Split.Resizer, the parameter is an object with the properties width and height for both panes; the one on the left (or top) beforePane and the one on the right (or bottom) afterPane.

export type SPLIT_PANE_RESIZE_SIZES = {
  beforePane: {
    width: number;
    height: number;
  };
  afterPane: {
    width: number;
    height: number;
  };
};

The events released are therefore:

export interface SplitPaneResizerProps {
  /** Event called when resizer is double clicked */
  onDoubleClick?: (
    event: React.MouseEvent<HTMLButtonElement>
  ) => void;

  /** Event called when pane size starts changing */
  onResizeStart?: () => void;

  /** Event called when pane size changes */
  onResizing?: (sizes: SPLIT_PANE_RESIZE_SIZES) => void;

  /** Event called when pane size changes */
  onResizeEnd?: (sizes: SPLIT_PANE_RESIZE_SIZES) => void;
}

Pane 1

Pane 2

Start: falseEnd (beforePanePane 1): w= h=End (afterPanePane 1): w= h=Resizing (beforePanePane 1): w= h=Resizing (afterPanePane 1): w= h=
Orientation
import { useState } from 'react';
import { Split, SPLIT_PANE_RESIZE_SIZES, SplitProps } from '@gfazioli/mantine-split-pane';
import { Code, Paper, Stack, Title } from '@mantine/core';

function Demo() {
  const [start, setStart] = useState(false);
  const [end, setEnd] = useState<SPLIT_PANE_RESIZE_SIZES>();
  const [resizing, setResizing] = useState<SPLIT_PANE_RESIZE_SIZES>();
  
  return (
    <Stack>
      <Split>
        <Split.Pane>
          <Paper withBorder w="100%" mih="100%">
            <Title>Pane 1</Title>
          </Paper>
        </Split.Pane>

        <Split.Resizer
          onResizeStart={() => {
            setStart(true);
          }}
          onResizeEnd={(sizes) => {
            setStart(false);
            setEnd(sizes);
          }}
          onResizing={setResizing}
        />

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

      <Stack gap={2}>
        <Code>Start: {start ? 'true' : 'false'}</Code>
        <Code>
          End (beforePanePane 1): w={end?.beforePane.width} h={end?.beforePane.height}
        </Code>
        <Code>
          End (afterPanePane 1): w={end?.afterPane.width} h={end?.afterPane.height}
        </Code>
        <Code>
          Resizing (beforePanePane 1): w={resizing?.beforePane.width} h=
          {resizing?.beforePane.height}
        </Code>
        <Code>
          Resizing (afterPanePane 1): w={resizing?.afterPane.width} h={resizing?.afterPane.height}
        </Code>
      </Stack>
    </Stack>
}

Split.Pane component

The Split.Pane component is a child component of Split and allows setting some props for panel management. It is the container which will be resized by the Split.Resizer component. It's very important because you can set the initial size of the pane, the minimum and maximum size of the pane, and the grow property.

initialWidth=200 | initialWidth=300

Pane 1

Pane 2

initialWidth=400, minWidth=100 | initialWidth=200, minWidth=50

Pane 1

Pane 2

initialWidth="33%", maxWidth="40%"

Pane 1

Pane 2

Pane 3

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

function Demo() {
  return (
    <Stack>
      <Code>
        initialWidth={200} | initialWidth={300}
      </Code>
      <Split>
        <Split.Pane initialWidth={200}>
          <Paper withBorder>
            <Title>Pane 1</Title>
          </Paper>
        </Split.Pane>

        <Split.Resizer />

        <Split.Pane initialWidth={300}>
          <Paper withBorder>
            <Title>Pane 2</Title>
          </Paper>
        </Split.Pane>
      </Split>

      <Code>
        initialWidth={400}, minWidth={100} | initialWidth={200}, minWidth={50}
      </Code>
      <Split>
        <Split.Pane initialWidth={400} minWidth={100}>
          <Paper withBorder>
            <Title>Pane 1</Title>
          </Paper>
        </Split.Pane>

        <Split.Resizer />

        <Split.Pane initialWidth={200} minWidth={50}>
          <Paper withBorder>
            <Title>Pane 2</Title>
          </Paper>
        </Split.Pane>
      </Split>

      <Code>initialWidth="33%", maxWidth="40%"</Code>
      <Split>
        <Split.Pane initialWidth="33%" maxWidth="40%">
          <Paper withBorder>
            <Title>Pane 1</Title>
          </Paper>
        </Split.Pane>

        <Split.Resizer />

        <Split.Pane initialWidth="33%" maxWidth="40%">
          <Paper withBorder>
            <Title>Pane 2</Title>
          </Paper>
        </Split.Pane>

        <Split.Resizer />

        <Split.Pane initialWidth="33%" maxWidth="40%">
          <Paper withBorder>
            <Title>Pane 3</Title>
          </Paper>
        </Split.Pane>
      </Split>
    </Stack>
  );
}

initialWidth, initialHeight

The initial size as well as the min and max size of the pane can be set using the initialWidth, initialHeight, minWidth, maxWidth, minHeight, and maxHeight props. You may use a numeric value or a string with a unit (px, %) for the size of the pane. The initialWidth and initialHeight props are used to set the initial size of the pane, while the minWidth, maxWidth, minHeight, and maxHeight props are used to set the minimum and maximum size of the pane.

Pane 1

Pane 2

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

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

  const paperProps = { withBorder: true, w: '100%', h: '100%',};

  return (
    <Stack>
      <Split orientation={mode}>
        <Split.Pane initialWidth={200} initialHeight={200}>
          <Paper {...paperProps}>
            <Title>Pane 1</Title>
          </Paper>
        </Split.Pane>

        <Split.Resizer />

        <Split.Pane initialWidth="40%" initialHeight={400}>
          <Paper {...paperProps}>
            <Title>Pane 2</Title>
          </Paper>
        </Split.Pane>
      </Split>
      <Group>
        <Button onClick={() => setMode((c) => (c === 'horizontal' ? 'vertical' : 'horizontal'))}>
          Change orientation
        </Button>
      </Group>
    </Stack>
  );
}

minWidth, maxWidth, minHeight, maxHeight

The minimum and maximum sizes of the panes can be set using the minWidth, maxWidth, minHeight, and maxHeight props. These props allow you to control the resizing behavior of the panes, ensuring they do not shrink or grow beyond specified limits.

Pane 1

initialWidth: 200initialHeight: 200minWidth: 180maxWidth: 300Width: pxHeight: px

Pane 2

initialWidth: 40%initialHeight: 400minHeight: 300maxHeight: 500Width: pxHeight: px
import { useState } from 'react';
import { Split } from '@gfazioli/mantine-split-pane';
import { Button, Group, Paper, Stack, Title } from '@mantine/core';

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

  const [firstPaneSize, setFirstPaneSize] = useState<SPLIT_PANE_SIZE>();
  const [secondPaneSize, setSecondPaneSize] = useState<SPLIT_PANE_SIZE>();

  const paperProps = { withBorder: true, w: '100%', h: '100%' };

  return (
    <Stack>
      <Split orientation={mode}>
        <Split.Pane
          initialWidth={200}
          initialHeight={200}
          minWidth={180}
          maxWidth={300}
          onResizing={setFirstPaneSize}
        >
          <Paper {...paperProps}>
            <Title>Pane 1</Title>
            <Stack gap={2}>
              <Code>initialWidth: 200</Code>
              <Code>initialHeight: 200</Code>
              <Code>minWidth: 180</Code>
              <Code>maxWidth: 300</Code>
              <Code>Width: {firstPaneSize?.width}px</Code>
              <Code>Height: {firstPaneSize?.height}px</Code>
            </Stack>
          </Paper>
        </Split.Pane>

        <Split.Resizer />

        <Split.Pane
          initialWidth="40%"
          initialHeight={400}
          minHeight={300}
          maxHeight={500}
          onResizing={setSecondPaneSize}
        >
          <Paper {...paperProps}>
            <Title>Pane 2</Title>
            <Stack gap={2}>
              <Code>initialWidth: 40%</Code>
              <Code>initialHeight: 400</Code>
              <Code>minHeight: 300</Code>
              <Code>maxHeight: 500</Code>
              <Code>Width: {secondPaneSize?.width}px</Code>
              <Code>Height: {secondPaneSize?.height}px</Code>
            </Stack>
          </Paper>
        </Split.Pane>
      </Split>
      <Group>
        <Button onClick={() => setMode((c) => (c === 'horizontal' ? 'vertical' : 'horizontal'))}>
          Change orientation
        </Button>
      </Group>
    </Stack>
  );
}

Note well

When resizing the panes, both will adjust simultaneously. This means the maximum and minimum values affect each other. For example, the minimum width of the second panel can impact the maximum width of the first panel. Choose the values carefully.

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.

Below the grow property is set to true for the first pane and false for the second pane. The first pane will occupy all the available space, while the second pane will occupy only the space needed for its content.

Pane 1

Pane 2

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

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

      <Split.Resizer />

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

Below the grow property is set to true for the second pane and false for the first pane. The second pane will occupy all the available space, while the first pane will occupy only the space needed for its content.

Pane 1

Pane 2

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

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

      <Split.Resizer />

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

Below an interesting example of a layout with three panes. The first and third panes are set to grow={false} and the second pane is set to grow={true}. The second pane will occupy all the available space, while the first and third panes will occupy only the space needed for their content.

Pane 1

Pane 2

Pane 2

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

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

      <Split.Resizer />

      <Split.Pane grow>
        <Paper withBorder>
          <Title>Pane 2</Title>
        </Paper>
      </Split.Pane>

      <Split.Resizer />

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

Of course, the grow feature can be use also when the orientation is vertical. In the example below, the first pane is set to grow={false} and the second pane is set to grow={true}. The second pane will occupy all the available space, while the first pane will occupy only the space needed for its content.

Pane 1

Pane 2

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

function Demo() {
  const paperProps = {
    withBorder: true,
    w: '100%',
    h: '100%',
  };

  return (
    <Split orientation="horizontal" h={500}>
      <Split.Pane minHeight="10%">
        <Paper {...paperProps}>
          <Title>Pane 1</Title>
        </Paper>
      </Split.Pane>

      <Split.Resizer />

      <Split.Pane grow minHeight="10%">
        <Paper {...paperProps}>
          <Title>Pane 2</Title>
        </Paper>
      </Split.Pane>
    </Split>
  );
}

💡 Suggestions

Try to use the grow property with the initialWidth, initialHeight, minWidth, maxWidth, minHeight, and maxHeight props to create a dynamic layout.

onResizeStart, onResizing, and onResizeEnd events

The Split.Pane provides the same events onResizeStart, onResizing, and onResizeEnd that are available in the Split.Resizer. However, pay attention to the difference in the parameter found in onResizing and onResizeEnd. In the case of the Split.Pane, the parameter is an object with the properties width and height of the selected pane.

export type SPLIT_PANE_SIZE = {
  width: number;
  height: number;
};

The events released are therefore:

export interface SplitPaneProps {
  /** Event called when pane size starts changing */
  onResizeStart?: () => void;

  /** Event called when pane size changes */
  onResizing?: (size: SPLIT_PANE_SIZE) => void;

  /** Event called when pane size changes */
  onResizeEnd?: (size: SPLIT_PANE_SIZE) => void;

  /** Event called to reset initial size */
  onResetInitialSize?: (
    e: React.MouseEvent<HTMLButtonElement>
  ) => void;
}

Pane 1

Pane 2

Start: falseEnd (Pane 1): w= h=Resizing (Pane 1): w= h=
Orientation
import { useState } from 'react';
import { Split, SPLIT_PANE_SIZE, SplitProps } from '@gfazioli/mantine-split-pane';
import { Code, Paper, Stack, Title } from '@mantine/core';

function Demo() {
  const [start, setStart] = useState(false);
  const [end, setEnd] = useState<SPLIT_PANE_SIZE>();
  const [resizing, setResizing] = useState<SPLIT_PANE_SIZE>()
  
  return (
    <Stack>
      <Split>
        <Split.Pane
          onResizeStart={() => {
            setStart(true);
          }}
          onResizeEnd={(size) => {
            setStart(false);
            setEnd(size);
          }}
          onResizing={setResizing}
        >
          <Paper withBorder w="100%" mih="100%">
            <Title>Pane 1</Title>
          </Paper>
        </Split.Pane>

        <Split.Resizer />

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

      <Stack gap={2}>
        <Code>Start: {start ? 'true' : 'false'}</Code>
        <Code>
          End (Pane 1): w={end?.width} h={end?.height}
        </Code>
        <Code>
          Resizing (Pane 1): w={resizing?.width} h={resizing?.height}
        </Code>
      </Stack>
    </Stack>
}

Multiple Panes

You can add as many panes as you want, and you can also set the minWidth and maxWidth props for each pane along 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>
          <Title>Pane 1</Title>
        </Paper>
      </Split.Pane>

      <Split.Resizer />

      <Split.Pane>
        <Paper withBorder>
          <Title>Pane 2</Title>
        </Paper>
      </Split.Pane>

      <Split.Resizer variant="outline" />

      <Split.Pane initialWidth={280} maxWidth={300}>
        <Paper withBorder>
          <Title>Pane 3</Title>
          <Code>{`initialWidth={280} maxWidth={300}`}</Code>
        </Paper>
      </Split.Pane>

      <Split.Resizer color="red" />

      <Split.Pane>
        <Paper withBorder>
          <Title>Pane 4</Title>
        </Paper>
      </Split.Pane>
    </Split>
  );
}

Reset with Double Click

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

Resize the panes, then double-click the resizer to reset their size.

Pane 1

Pane 2

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

function Demo() {
  return (
    <Stack>
      <Text>Resize the panes, then double-click the resizer to reset their size.</Text>
      <Split>
        <Split.Pane initialWidth="50%">
          <Paper withBorder>
            <Title>Pane 1</Title>
          </Paper>
        </Split.Pane>

        <Split.Resizer />

        <Split.Pane initialWidth="50%">
          <Paper withBorder>
            <Title>Pane 2</Title>
          </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.

Double-click on the resizer to swap the initial width of the panes.

Pane 1

Pane 2

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

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

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

  return (
    <Stack>
      <Text>Double-click on the resizer to swap the initial width of the panes.</Text>
      <Split>
        <Split.Pane initialWidth={initialWidth}>
          <Paper withBorder>
            <Title>Pane 1</Title>
          </Paper>
        </Split.Pane>

        <Split.Resizer onDoubleClick={handleDoubleClick} />

        <Split.Pane grow>
          <Paper withBorder>
            <Title>Pane 2</Title>
          </Paper>
        </Split.Pane>
      </Split>
    </Stack>
  );
}

Note well

The onResetInitialSize event is triggered when the user double clicks on the resizer to reset the initial size of the pane. You can use this event to manage the double click event in the Split.Pane component. The code below shows how to use the onResetInitialSize event to manage the double click event in the Split.Pane component.

<Stack>
  <Text>
    Double-click on the resizer to swap the initial width of the
    panes.
  </Text>
  <Split>
    <Split.Pane
      initialWidth={initialWidth}
      onResetInitialSize={handleDoubleClick}
    >
      <Paper withBorder>
        <Title>Pane 1</Title>
      </Paper>
    </Split.Pane>

    <Split.Resizer />

    <Split.Pane grow>
      <Paper withBorder>
        <Title>Pane 2</Title>
      </Paper>
    </Split.Pane>
  </Split>
</Stack>

Alternatively, which is the same:

<Stack>
  <Text>
    Double-click on the resizer to swap the initial width of the
    panes.
  </Text>
  <Split>
    <Split.Pane initialWidth={initialWidth}>
      <Paper withBorder>
        <Title>Pane 1</Title>
      </Paper>
    </Split.Pane>

    <Split.Resizer />

    <Split.Pane grow onResetInitialSize={handleDoubleClick}>
      <Paper withBorder>
        <Title>Pane 2</Title>
      </Paper>
    </Split.Pane>
  </Split>
</Stack>

Nested Split

Naturally, you can nest Split components to create more intricate layouts.

Pane 1

Pane 2

Pane 3

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

function Demo() {
  const paperProps = { withBorder: true, w: '100%', h: '100%'};

  return (
    <Split>
      <Split.Pane initialWidth={300}>
        <Paper {...paperProps}>
          <Title>Pane 1</Title>
        </Paper>
      </Split.Pane>

      <Split.Resizer />

      <Split.Pane grow>
        <Split orientation="horizontal">
          <Split.Pane>
            <Paper {...paperProps}>
              <Title>Pane 2</Title>
            </Paper>
          </Split.Pane>

          <Split.Resizer color="violet" />

          <Split.Pane initialHeight={200}>
            <Paper {...paperProps}>
              <Title>Pane 3</Title>
            </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, Title } from '@mantine/core';

function Demo() {
  const paperProps = { withBorder: true, w: '100%', h: '100%' };

  return (
    <Split>
      <Split.Pane initialWidth={300}>
        <Paper {...paperProps}>
          <Title>Pane 1</Title>
        </Paper>
      </Split.Pane>

      <Split.Resizer />

      <Split.Pane grow>
        <Split orientation="horizontal">
          <Split.Pane initialHeight={200}>
            <Paper {...paperProps}>
              <Title>Pane 2</Title>
            </Paper>
          </Split.Pane>

          <Split.Resizer radius={256} spacing={16} color="red.3" hoverColor="blue.7" size="xl" />

          <Split.Pane initialHeight={300}>
            <Paper {...paperProps}>
              <Title>Pane 3</Title>
            </Paper>
          </Split.Pane>
        </Split>
      </Split.Pane>
    </Split>
  );
}

Note well

Remember that you can set the resizer props both on the Split component and the Split.Resizer component. The Split.Resizer component will inherit the props from the Split component, but you can override them by setting them on the Split.Resizer component.

For example, in the code below, the Split.Resizer component will inherit the spacing and radius prop from the Split component.

<Split orientation="horizontal" radius={256} spacing={16}>
  <Split.Pane initialHeight={200}>
    <Paper {...paperProps}>
      <Title>Pane 2</Title>
    </Paper>
  </Split.Pane>

  <Split.Resizer color="red.3" hoverColor="blue.7" size="xl" />

  <Split.Pane initialHeight={300}>
    <Paper {...paperProps}>
      <Title>Pane 3</Title>
    </Paper>
  </Split.Pane>
</Split>

The same with:

<Split orientation="horizontal">
  <Split.Pane initialHeight={200}>
    <Paper {...paperProps}>
      <Title>Pane 2</Title>
    </Paper>
  </Split.Pane>

  <Split.Resizer
    color="red.3"
    hoverColor="blue.7"
    size="xl"
    radius={256}
    spacing={16}
  />

  <Split.Pane initialHeight={300}>
    <Paper {...paperProps}>
      <Title>Pane 3</Title>
    </Paper>
  </Split.Pane>
</Split>

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.toString())}>
        <Paper withBorder>
          <Title>Pane 1</Title>
        </Paper>
      </Split.Pane>

      <Split.Resizer />

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

As previously explained, you can choose to use events from either the Split.Pane or the Split.Resizer. Here, the code uses events from the Split.Pane, but you can easily use events from the Split.Resizer and decide to store a specific pane or both panels.

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 {...paperProps}>
          <Title>Pane 1</Title>
        </Paper>
      </Split.Pane>

      <Split.Resizer />

      <Split.Pane>
        <Paper {...paperProps}>
          <Title>Pane 2</Title>
        </Paper>
      </Split.Pane>

      <Split.Resizer />

      <Split.Pane grow>
        <Split orientation="horizontal">
          <Split.Pane>
            <Paper {...paperProps}>
              <Title>Pane 1</Title>
            </Paper>
          </Split.Pane>

          <Split.Resizer step={1} color="violet" />

          <Split.Pane>
            <Paper {...paperProps}>
              <Title>Pane 2</Title>
            </Paper>
          </Split.Pane>
        </Split>
      </Split.Pane>
    </Split>
  );
}

Note well

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

<Split step={16} shiftStep={128}>
  <Split.Pane>
    <h1>Pane 1</h1>
  </Split.Pane>

  <Split.Resizer step={32} shiftStep={256} />

  <Split.Pane>
    <h1>Pane 2</h1>
  </Split.Pane>

  <Split.Resizer />

  <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.