# Splitter

URL: https://ark-ui.com/docs/components/splitter
Source: https://raw.githubusercontent.com/chakra-ui/ark/refs/heads/main/website/src/content/pages/components/splitter.mdx

A component that divides your interface into resizable sections

---

<ComponentPreview id="Splitter" />

## Anatomy

<Anatomy id="splitter" />

```tsx
<Splitter.Root>
  <Splitter.Panel />
  <Splitter.ResizeTrigger>
    <Splitter.ResizeTriggerIndicator />
  </Splitter.ResizeTrigger>
  <Splitter.Panel />
</Splitter.Root>
```

## Examples

**Example: basic**

```ripple
import { Splitter } from 'ark-ripple/splitter';
import styles from 'styles/splitter.module.css';

export component Basic() {
  <Splitter.Root class={styles.Root} panels={[{ id: 'a' }, { id: 'b' }]}>
    <Splitter.Panel class={styles.Panel} id="a">{'A'}</Splitter.Panel>
    <Splitter.ResizeTrigger class={styles.ResizeTrigger} id="a:b" aria-label="Resize">
      <Splitter.ResizeTriggerIndicator class={styles.ResizeTriggerIndicator} />
    </Splitter.ResizeTrigger>
    <Splitter.Panel class={styles.Panel} id="b">{'B'}</Splitter.Panel>
  </Splitter.Root>
}

```

### Context

Access the splitter's API with `Splitter.Context` or the `useSplitterContext` hook. This lets you resize panels
programmatically:

**Example: context**

```ripple
import { Splitter } from 'ark-ripple/splitter';
import button from 'styles/button.module.css';
import styles from 'styles/splitter.module.css';

export component Context() {
  <Splitter.Root class={styles.Root} panels={[{ id: 'a' }, { id: 'b' }]}>
    <Splitter.Context>
      component children({ context }) {
        <Splitter.Panel class={styles.Panel} id="a">
          <button class={button.Root} type="button" onClick={() => @context.resizePanel('a', 10)}>
            {'Set to 10%'}
          </button>
        </Splitter.Panel>
        <Splitter.ResizeTrigger class={styles.ResizeTrigger} id="a:b" aria-label="Resize">
          <Splitter.ResizeTriggerIndicator class={styles.ResizeTriggerIndicator} />
        </Splitter.ResizeTrigger>
        <Splitter.Panel class={styles.Panel} id="b">
          <button class={button.Root} type="button" onClick={() => @context.resizePanel('b', 10)}>
            {'Set to 10%'}
          </button>
        </Splitter.Panel>
      }
    </Splitter.Context>
  </Splitter.Root>
}

```

### Vertical

By default, the Splitter component is horizontal. If you need a vertical splitter, use the `orientation` prop:

**Example: vertical**

```ripple
import { Splitter } from 'ark-ripple/splitter';
import styles from 'styles/splitter.module.css';

export component Vertical() {
  <Splitter.Root class={styles.Root} orientation="vertical" panels={[{ id: 'a' }, { id: 'b' }]}>
    <Splitter.Panel class={styles.Panel} id="a">{'A'}</Splitter.Panel>
    <Splitter.ResizeTrigger class={styles.ResizeTrigger} id="a:b" aria-label="Resize">
      <Splitter.ResizeTriggerIndicator class={styles.ResizeTriggerIndicator} />
    </Splitter.ResizeTrigger>
    <Splitter.Panel class={styles.Panel} id="b">{'B'}</Splitter.Panel>
  </Splitter.Root>
}

```

### Collapsible Panels

To make a panel collapsible, set the `collapsible` prop to `true` on the panel you want to make collapsible.
Additionally, you can use the `collapsedSize` prop to set the size of the panel when it's collapsed.

> This can be useful for building sidebar layouts.

**Example: collapsible**

```ripple
import { Splitter } from 'ark-ripple/splitter';
import styles from 'styles/splitter.module.css';

export component Collapsible() {
  <Splitter.Root
    class={styles.Root}
    defaultSize={[15, 20]}
    panels={[
      { id: 'a', collapsible: true, collapsedSize: 5, minSize: 10, maxSize: 20 },
      { id: 'b', minSize: 50 },
    ]}
  >
    <Splitter.Panel class={styles.Panel} id="a">{'A'}</Splitter.Panel>
    <Splitter.ResizeTrigger class={styles.ResizeTrigger} id="a:b" aria-label="Resize">
      <Splitter.ResizeTriggerIndicator class={styles.ResizeTriggerIndicator} />
    </Splitter.ResizeTrigger>
    <Splitter.Panel class={styles.Panel} id="b">{'B'}</Splitter.Panel>
  </Splitter.Root>
}

```

### Multiple Panels

Here's an example of how to use the `Splitter` component with multiple panels.

**Example: multiple-panels**

```ripple
import { Splitter } from 'ark-ripple/splitter';
import styles from 'styles/splitter.module.css';

export component MultiplePanels() {
  <Splitter.Root
    class={styles.Root}
    panels={[
      { id: 'a', minSize: 20 },
      { id: 'b', minSize: 40 },
      { id: 'c', minSize: 20 },
    ]}
    defaultSize={[20, 60, 20]}
  >
    <Splitter.Panel class={styles.Panel} id="a">{'A'}</Splitter.Panel>
    <Splitter.ResizeTrigger class={styles.ResizeTrigger} id="a:b" aria-label="Resize">
      <Splitter.ResizeTriggerIndicator class={styles.ResizeTriggerIndicator} />
    </Splitter.ResizeTrigger>
    <Splitter.Panel class={styles.Panel} id="b">{'B'}</Splitter.Panel>
    <Splitter.ResizeTrigger class={styles.ResizeTrigger} id="b:c" aria-label="Resize">
      <Splitter.ResizeTriggerIndicator class={styles.ResizeTriggerIndicator} />
    </Splitter.ResizeTrigger>
    <Splitter.Panel class={styles.Panel} id="c">{'C'}</Splitter.Panel>
  </Splitter.Root>
}

```

### Root Provider

An alternative way to control the splitter is to use the `RootProvider` component and the `useSplitter` hook. This way
you can access the state and methods from outside the component.

**Example: root-provider**

```ripple
import { Splitter, useSplitter } from 'ark-ripple/splitter';
import styles from 'styles/splitter.module.css';

export component RootProvider() {
  const splitter = useSplitter(
    {
      defaultSize: [50, 50],
      panels: [{ id: 'a' }, { id: 'b' }],
    },
  );

  <div class="stack">
    <output>
      {'current size: '}
      {JSON.stringify(@splitter.getSizes())}
    </output>

    <Splitter.RootProvider class={styles.Root} value={splitter}>
      <Splitter.Panel class={styles.Panel} id="a">{'A'}</Splitter.Panel>
      <Splitter.ResizeTrigger class={styles.ResizeTrigger} id="a:b" aria-label="Resize">
        <Splitter.ResizeTriggerIndicator class={styles.ResizeTriggerIndicator} />
      </Splitter.ResizeTrigger>
      <Splitter.Panel class={styles.Panel} id="b">{'B'}</Splitter.Panel>
    </Splitter.RootProvider>
  </div>
}

```

### Resize Indicator

Use the `Splitter.ResizeTriggerIndicator` component to show a visual indicator on the resize handle.

**Example: resize-indicator**

```ripple
import { Splitter } from 'ark-ripple/splitter';
import styles from 'styles/splitter.module.css';

export component ResizeIndicator() {
  <Splitter.Root class={styles.Root} panels={[{ id: 'a' }, { id: 'b' }]}>
    <Splitter.Panel class={styles.Panel} id="a">{'A'}</Splitter.Panel>
    <Splitter.ResizeTrigger class={styles.ResizeTrigger} id="a:b" aria-label="Resize">
      <Splitter.ResizeTriggerIndicator class={styles.ResizeTriggerIndicator} />
    </Splitter.ResizeTrigger>
    <Splitter.Panel class={styles.Panel} id="b">{'B'}</Splitter.Panel>
  </Splitter.Root>
}

```

### Dynamic Collapsible

Use the `collapsePanel()` and `expandPanel()` methods to programmatically control panel collapse based on viewport size.
This is useful for responsive sidebar layouts that collapse on smaller screens.

**Example: dynamic-collapsible**

```ripple
import { Splitter, useSplitter } from 'ark-ripple/splitter';
import { track, effect } from 'ripple';
import styles from 'styles/splitter.module.css';

export component DynamicCollapsible() {
  let rootSize = track<number | null>(null);
  let rootEl: HTMLDivElement | null = null;

  effect(() => {
    const handleResize = () => {
      @rootSize = rootEl?.offsetWidth ?? null;
    };
    handleResize();
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  });

  const isBelowMd = track(() => @rootSize != null && @rootSize < 600);

  const splitterProps = track(
    () => ({
      panels: [
        { id: 'a', collapsible: @isBelowMd, collapsedSize: 5, minSize: 20, maxSize: 40 },
        { id: 'b' },
      ] as const,
      defaultSize: [15, 85],
    }),
  );

  const splitter = useSplitter(splitterProps);

  effect(() => {
    if (@isBelowMd) @splitter.collapsePanel('a');
    else @splitter.expandPanel('a');
  });

  <Splitter.RootProvider
    class={styles.Root}
    value={splitter}
    {ref (el: HTMLDivElement) => {
      rootEl = el;
    }}
  >
    <Splitter.Panel class={styles.Panel} id="a">{'A'}</Splitter.Panel>
    <Splitter.ResizeTrigger class={styles.ResizeTrigger} id="a:b" aria-label="Resize panels">
      <Splitter.ResizeTriggerIndicator class={styles.ResizeTriggerIndicator} />
    </Splitter.ResizeTrigger>
    <Splitter.Panel class={styles.Panel} id="b">{'B'}</Splitter.Panel>
  </Splitter.RootProvider>
}

```

## API Reference

### Props

**Component API Reference**

**Root Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `panels` | `PanelData[]` | Yes | The size constraints of the panels. |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `defaultSize` | `number[]` | No | The initial size of the panels when rendered.
Use when you don't need to control the size of the panels. |
| `id` | `string` | No | The unique identifier of the machine. |
| `ids` | `Partial<{
  root: string
  resizeTrigger: (id: string) => string
  label: (id: string) => string
  panel: (id: string | number) => string
}>` | No | The ids of the elements in the splitter. Useful for composition. |
| `keyboardResizeBy` | `number` | No | The number of pixels to resize the panel by when the keyboard is used. |
| `nonce` | `string` | No | The nonce for the injected splitter cursor stylesheet. |
| `onCollapse` | `(details: ExpandCollapseDetails) => void` | No | Function called when a panel is collapsed. |
| `onExpand` | `(details: ExpandCollapseDetails) => void` | No | Function called when a panel is expanded. |
| `onResize` | `(details: ResizeDetails) => void` | No | Function called when the splitter is resized. |
| `onResizeEnd` | `(details: ResizeEndDetails) => void` | No | Function called when the splitter resize ends. |
| `onResizeStart` | `() => void` | No | Function called when the splitter resize starts. |
| `orientation` | `'horizontal' | 'vertical'` | No | The orientation of the splitter. Can be `horizontal` or `vertical` |
| `size` | `number[]` | No | The controlled size data of the panels |


**Root Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | splitter |
| `[data-part]` | root |
| `[data-orientation]` | The orientation of the splitter |
| `[data-dragging]` | Present when in the dragging state |


**Panel Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `id` | `string` | Yes |  |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Panel Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | splitter |
| `[data-part]` | panel |
| `[data-orientation]` | The orientation of the panel |
| `[data-dragging]` | Present when in the dragging state |
| `[data-id]` |  |
| `[data-index]` | The index of the item |


**ResizeTriggerIndicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**ResizeTrigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `id` | ``${string}:${string}`` | Yes |  |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `disabled` | `boolean` | No |  |


**ResizeTrigger Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | splitter |
| `[data-part]` | resize-trigger |
| `[data-id]` |  |
| `[data-orientation]` | The orientation of the resizetrigger |
| `[data-focus]` | Present when focused |
| `[data-dragging]` | Present when in the dragging state |
| `[data-disabled]` | Present when disabled |


**RootProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `value` | `UseSplitterReturn` | Yes |  |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


### Context

**API:**

| Property | Type | Description |
|----------|------|-------------|
| `dragging` | `boolean` | Whether the splitter is currently being resized. |
| `orientation` | `"horizontal" | "vertical"` | The orientation of the splitter. |
| `getSizes` | `() => number[]` | Returns the current sizes of the panels. |
| `setSizes` | `(size: number[]) => void` | Sets the sizes of the panels. |
| `getItems` | `() => SplitterItem[]` | Returns the items of the splitter. |
| `getPanels` | `() => PanelData[]` | Returns the panels of the splitter. |
| `getPanelById` | `(id: string) => PanelData` | Returns the panel with the specified id. |
| `getPanelSize` | `(id: string) => number` | Returns the size of the specified panel. |
| `isPanelCollapsed` | `(id: string) => boolean` | Returns whether the specified panel is collapsed. |
| `isPanelExpanded` | `(id: string) => boolean` | Returns whether the specified panel is expanded. |
| `collapsePanel` | `(id: string) => void` | Collapses the specified panel. |
| `expandPanel` | `(id: string, minSize?: number) => void` | Expands the specified panel. |
| `resizePanel` | `(id: string, unsafePanelSize: number) => void` | Resizes the specified panel. |
| `getLayout` | `() => string` | Returns the layout of the splitter. |
| `resetSizes` | `VoidFunction` | Resets the splitter to its initial state. |
| `getResizeTriggerState` | `(props: ResizeTriggerProps) => ResizeTriggerState` | Returns the state of the resize trigger. |


## Accessibility

Complies with the [Window Splitter WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/).