# Splitter

URL: https://ark-ui.com/docs/components/splitter
LLM: https://ark-ui.com/llms.txt/components/splitter

A component that divides your interface into resizable sections

---



## Anatomy



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

## Examples

```tsx
import { Splitter } from '@ark-ui/react/splitter'
import styles from 'styles/splitter.module.css'

export const Basic = () => (
  <Splitter.Root className={styles.Root} panels={[{ id: 'a' }, { id: 'b' }]}>
    <Splitter.Panel className={styles.Panel} id="a">
      A
    </Splitter.Panel>
    <Splitter.ResizeTrigger className={styles.ResizeTrigger} id="a:b" aria-label="Resize">
      <Splitter.ResizeTriggerIndicator className={styles.ResizeTriggerIndicator} />
    </Splitter.ResizeTrigger>
    <Splitter.Panel className={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:

```tsx
import { Splitter } from '@ark-ui/react/splitter'
import button from 'styles/button.module.css'
import styles from 'styles/splitter.module.css'

export const Context = () => (
  <Splitter.Root className={styles.Root} panels={[{ id: 'a' }, { id: 'b' }]}>
    <Splitter.Context>
      {(splitter) => (
        <>
          <Splitter.Panel className={styles.Panel} id="a">
            <button className={button.Root} type="button" onClick={() => splitter.resizePanel('a', 10)}>
              Set to 10%
            </button>
          </Splitter.Panel>
          <Splitter.ResizeTrigger className={styles.ResizeTrigger} id="a:b" aria-label="Resize">
            <Splitter.ResizeTriggerIndicator className={styles.ResizeTriggerIndicator} />
          </Splitter.ResizeTrigger>
          <Splitter.Panel className={styles.Panel} id="b">
            <button className={button.Root} type="button" onClick={() => splitter.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:

```tsx
import { Splitter } from '@ark-ui/react/splitter'
import styles from 'styles/splitter.module.css'

export const Vertical = () => (
  <Splitter.Root className={styles.Root} orientation="vertical" panels={[{ id: 'a' }, { id: 'b' }]}>
    <Splitter.Panel className={styles.Panel} id="a">
      A
    </Splitter.Panel>
    <Splitter.ResizeTrigger className={styles.ResizeTrigger} id="a:b" aria-label="Resize">
      <Splitter.ResizeTriggerIndicator className={styles.ResizeTriggerIndicator} />
    </Splitter.ResizeTrigger>
    <Splitter.Panel className={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.

```tsx
import { Splitter } from '@ark-ui/react/splitter'
import styles from 'styles/splitter.module.css'

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

```tsx
import { Splitter } from '@ark-ui/react/splitter'
import styles from 'styles/splitter.module.css'

export const MultiplePanels = () => (
  <Splitter.Root
    className={styles.Root}
    panels={[
      { id: 'a', minSize: 20 },
      { id: 'b', minSize: 40 },
      { id: 'c', minSize: 20 },
    ]}
    defaultSize={[20, 60, 20]}
  >
    <Splitter.Panel className={styles.Panel} id="a">
      A
    </Splitter.Panel>
    <Splitter.ResizeTrigger className={styles.ResizeTrigger} id="a:b" aria-label="Resize">
      <Splitter.ResizeTriggerIndicator className={styles.ResizeTriggerIndicator} />
    </Splitter.ResizeTrigger>
    <Splitter.Panel className={styles.Panel} id="b">
      B
    </Splitter.Panel>
    <Splitter.ResizeTrigger className={styles.ResizeTrigger} id="b:c" aria-label="Resize">
      <Splitter.ResizeTriggerIndicator className={styles.ResizeTriggerIndicator} />
    </Splitter.ResizeTrigger>
    <Splitter.Panel className={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.

```tsx
import { Splitter, useSplitter } from '@ark-ui/react/splitter'
import styles from 'styles/splitter.module.css'

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

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

      <Splitter.RootProvider className={styles.Root} value={splitter}>
        <Splitter.Panel className={styles.Panel} id="a">
          A
        </Splitter.Panel>
        <Splitter.ResizeTrigger className={styles.ResizeTrigger} id="a:b" aria-label="Resize">
          <Splitter.ResizeTriggerIndicator className={styles.ResizeTriggerIndicator} />
        </Splitter.ResizeTrigger>
        <Splitter.Panel className={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.

```tsx
import { Splitter } from '@ark-ui/react/splitter'
import styles from 'styles/splitter.module.css'

export const ResizeIndicator = () => (
  <Splitter.Root className={styles.Root} panels={[{ id: 'a' }, { id: 'b' }]}>
    <Splitter.Panel className={styles.Panel} id="a">
      A
    </Splitter.Panel>
    <Splitter.ResizeTrigger className={styles.ResizeTrigger} id="a:b" aria-label="Resize">
      <Splitter.ResizeTriggerIndicator className={styles.ResizeTriggerIndicator} />
    </Splitter.ResizeTrigger>
    <Splitter.Panel className={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.

```tsx
/** biome-ignore-all lint/correctness/useExhaustiveDependencies: intentional */

import { Splitter, useSplitter } from '@ark-ui/react/splitter'
import { useLayoutEffect, useRef, useState } from 'react'
import styles from 'styles/splitter.module.css'

export const DynamicCollapsible = () => {
  const [rootSize, setRootSize] = useState<number | null>(null)
  const ref = useRef<HTMLDivElement>(null)

  useLayoutEffect(() => {
    const handleResize = () => setRootSize(ref.current?.offsetWidth ?? null)
    handleResize()
    window.addEventListener('resize', handleResize)
    return () => window.removeEventListener('resize', handleResize)
  }, [])

  const isBelowMd = rootSize != null && rootSize < 600

  useLayoutEffect(() => {
    if (isBelowMd) splitter.collapsePanel('a')
    else splitter.expandPanel('a')
  }, [isBelowMd])

  const splitter = useSplitter({
    panels: [{ id: 'a', collapsible: isBelowMd, collapsedSize: 5, minSize: 20, maxSize: 40 }, { id: 'b' }],
    defaultSize: [15, 85],
  })

  return (
    <Splitter.RootProvider className={styles.Root} value={splitter} ref={ref}>
      <Splitter.Panel className={styles.Panel} id="a">
        A
      </Splitter.Panel>
      <Splitter.ResizeTrigger className={styles.ResizeTrigger} id="a:b" aria-label="Resize panels">
        <Splitter.ResizeTriggerIndicator className={styles.ResizeTriggerIndicator} />
      </Splitter.ResizeTrigger>
      <Splitter.Panel className={styles.Panel} id="b">
        B
      </Splitter.Panel>
    </Splitter.RootProvider>
  )
}
```

### Nested

Nest splitters to build grid-like layouts. Use `Splitter.createRegistry()` to create a shared registry between splitter
instances — this enables multi-drag at intersection points where horizontal and vertical splitters meet.

```tsx
import { Splitter } from '@ark-ui/react/splitter'
import styles from 'styles/splitter.module.css'

const registry = Splitter.createRegistry()

export const Nested = () => (
  <Splitter.Root
    className={styles.Root}
    orientation="horizontal"
    panels={[{ id: 'left' }, { id: 'center' }, { id: 'right' }]}
    registry={registry}
  >
    <Splitter.Panel className={styles.Panel} id="left">
      Left
    </Splitter.Panel>
    <Splitter.ResizeTrigger className={styles.ResizeTrigger} id="left:center" aria-label="Resize">
      <Splitter.ResizeTriggerIndicator className={styles.ResizeTriggerIndicator} />
    </Splitter.ResizeTrigger>
    <Splitter.Panel className={styles.Panel} id="center">
      <Splitter.Root orientation="vertical" panels={[{ id: 'top' }, { id: 'bottom' }]} registry={registry}>
        <Splitter.Panel className={styles.Panel} id="top">
          Top
        </Splitter.Panel>
        <Splitter.ResizeTrigger className={styles.ResizeTrigger} id="top:bottom" aria-label="Resize">
          <Splitter.ResizeTriggerIndicator className={styles.ResizeTriggerIndicator} />
        </Splitter.ResizeTrigger>
        <Splitter.Panel className={styles.Panel} id="bottom">
          Bottom
        </Splitter.Panel>
      </Splitter.Root>
    </Splitter.Panel>
    <Splitter.ResizeTrigger className={styles.ResizeTrigger} id="center:right" aria-label="Resize">
      <Splitter.ResizeTriggerIndicator className={styles.ResizeTriggerIndicator} />
    </Splitter.ResizeTrigger>
    <Splitter.Panel className={styles.Panel} id="right">
      Right
    </Splitter.Panel>
  </Splitter.Root>
)
```

## API Reference

### Props

### Root

#### Props

**`panels`**
Type: `PanelData[]`
Required: true
Default Value: `undefined`
Description: The size constraints of the panels.

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

**`defaultSize`**
Type: `PanelSize[]`
Required: false
Default Value: `undefined`
Description: The initial size of the panels when rendered.
Use when you don't need to control the size of the panels.

**`id`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The unique identifier of the machine.

**`ids`**
Type: `Partial<{
  root: string
  resizeTrigger: (id: string) => string
  label: (id: string) => string
  panel: (id: string | number) => string
}>`
Required: false
Default Value: `undefined`
Description: The ids of the elements in the splitter. Useful for composition.

**`keyboardResizeBy`**
Type: `number`
Required: false
Default Value: `undefined`
Description: The number of pixels to resize the panel by when the keyboard is used.

**`nonce`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The nonce for the injected splitter cursor stylesheet.

**`onCollapse`**
Type: `(details: ExpandCollapseDetails) => void`
Required: false
Default Value: `undefined`
Description: Function called when a panel is collapsed.

**`onExpand`**
Type: `(details: ExpandCollapseDetails) => void`
Required: false
Default Value: `undefined`
Description: Function called when a panel is expanded.

**`onResize`**
Type: `(details: ResizeDetails) => void`
Required: false
Default Value: `undefined`
Description: Function called when the splitter is resized.

**`onResizeEnd`**
Type: `(details: ResizeEndDetails) => void`
Required: false
Default Value: `undefined`
Description: Function called when the splitter resize ends.

**`onResizeStart`**
Type: `() => void`
Required: false
Default Value: `undefined`
Description: Function called when the splitter resize starts.

**`orientation`**
Type: `'horizontal' | 'vertical'`
Required: false
Default Value: `"horizontal"`
Description: The orientation of the splitter. Can be `horizontal` or `vertical`

**`registry`**
Type: `SplitterRegistry`
Required: false
Default Value: `undefined`
Description: The splitter registry to use for multi-drag support.
When provided, enables dragging at the intersection of multiple splitters.

**`size`**
Type: `PanelSize[]`
Required: false
Default Value: `undefined`
Description: The controlled size data of the panels

#### Data Attributes

**`data-scope`**: splitter
**`data-part`**: root
**`data-orientation`**: The orientation of the splitter
**`data-dragging`**: Present when in the dragging state

### Panel

#### Props

**`id`**
Type: `string`
Required: true
Default Value: `undefined`
Description: undefined

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

#### Data Attributes

**`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

### Registry

#### Props

**`hitAreaMargins`**
Type: `HitAreaMargins`
Required: false
Default Value: `undefined`
Description: The hit area margins for resize handles.
Larger margins make it easier to grab handles, especially on touch devices.

**`nonce`**
Type: `string`
Required: false
Default Value: `undefined`
Description: The nonce for the injected cursor stylesheet (for CSP compliance).

### ResizeTriggerIndicator

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

### ResizeTrigger

#### Props

**`id`**
Type: `ResizeTriggerId`
Required: true
Default Value: `undefined`
Description: undefined

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

**`disabled`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: undefined

#### Data Attributes

**`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

**`value`**
Type: `UseSplitterReturn`
Required: true
Default Value: `undefined`
Description: undefined

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: 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: PanelSize[]) => 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: PanelId) => PanelData` | Returns the panel with the specified id. |
| `getPanelSize` | `(id: PanelId) => number` | Returns the size of the specified panel. |
| `isPanelCollapsed` | `(id: PanelId) => boolean` | Returns whether the specified panel is collapsed. |
| `isPanelExpanded` | `(id: PanelId) => boolean` | Returns whether the specified panel is expanded. |
| `collapsePanel` | `(id: PanelId) => void` | Collapses the specified panel. |
| `expandPanel` | `(id: PanelId, minSize?: number) => void` | Expands the specified panel. |
| `resizePanel` | `(id: PanelId, 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/).