---
title: "Splitter"
description: "A component that divides your layout into resizable sections."
canonical_url: "https://vinicunca.dev/akar/components/splitter"
---
# Splitter

> A component that divides your layout into resizable sections.

```vue [index.vue]
<script setup lang="ts">
import { SplitterGroup, SplitterPanel, SplitterResizeHandle } from 'akar'
</script>

<template>
  <div class="akar-demo akar-demo-Splitter">
    <div class="w-full h-64 px-8 text-green9 font-medium text-sm">
        <SplitterGroup
          id="splitter-group-1"
          direction="horizontal"
        >
          <SplitterPanel
            id="splitter-group-1-panel-1"
            :min-size="20"
            class="SplitterPanel"
          >
            Panel A
          </SplitterPanel>
          <SplitterResizeHandle
            id="splitter-group-1-resize-handle-1"
            class="SplitterResizeHandle"
          />
          <SplitterPanel
            id="splitter-group-1-panel-2"
            :min-size="20"
          >
            <SplitterGroup
              id="splitter-group-2"
              direction="vertical"
            >
              <SplitterPanel
                id="splitter-group-2-panel-1"
                :min-size="20"
                class="SplitterPanel"
              >
                Panel B
              </SplitterPanel>
              <SplitterResizeHandle
                id="splitter-group-2-resize-handle-1"
                class="SplitterResizeHandle"
              />
              <SplitterPanel
                id="splitter-group-2-panel-2"
                :min-size="20"
                class="SplitterPanel"
              >
                Panel C
              </SplitterPanel>
            </SplitterGroup>
          </SplitterPanel>
        </SplitterGroup>
      </div>
  </div>
</template>
```

```css [styles.css]
@import '../_colors/black-alpha.css';

@scope (.akar-demo-Splitter) {
/* reset */
button {
  all: unset;
}

.SplitterPanel {
  background-color: #ffffff;
  border-radius: 1.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
} 

.SplitterResizeHandle[data-orientation="horizontal"] {
  width: 0.5rem
}

.SplitterResizeHandle[data-orientation="vertical"] {
  height: 0.5rem
}
}
```

## Features

- Supports keyboard interaction.
- Supports horizontal/vertical layout.
- Supports nested layout.
- Supports Right to Left direction.
- Can resize across another panel.
- Can be mounted conditionally.
- Supports percent and pixel sizing.

## Installation

Install the component from your command line.

```bash [pnpm]
pnpm add akar
```

```bash [yarn]
yarn add akar
```

```bash [npm]
npm install akar
```

```bash [bun]
bun add akar
```

## Anatomy

Import all parts and piece them together.

```vue
<script setup>
import { SplitterGroup, SplitterPanel, SplitterResizeHandle } from 'akar';
</script>

<template>
  <SplitterGroup>
    <SplitterPanel />
    <SplitterResizeHandle />
  </SplitterGroup>
</template>
```

## API Reference

### Group

Contains all the parts of a Splitter.

**Props**

| Prop | Default | Type | Description |
| --- | --- | --- | --- |
| `as` | `"div"` | `AsTag \| Component` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `asChild` |  | `boolean` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://vinicunca.dev/akar/guides/composition) guide for more details. |
| `autoSaveId` | `null` | `string \| null` | Unique id used to auto-save group arrangement via `localStorage`. |
| `direction*` |  | `"vertical" \| "horizontal"` | The group orientation of splitter. |
| `id` |  | `string \| null` | Group id; falls back to `useId` when not provided. |
| `keyboardResizeBy` | `10` | `number \| null` | Step size when arrow key was pressed. |
| `storage` | `defaultStorage` | `PanelGroupStorage` | Custom storage API; defaults to localStorage |

**Events**

| Event | Payload | Description |
| --- | --- | --- |
| `layout` | `val: number[]` | Event handler called when group layout changes |

**Slots**

| Slot | Payload | Description |
| --- | --- | --- |
| `layout` | `number[]` | Current size of layout |

[SplitterGroup.vue source](https://github.com/vinicunca/akar/blob/v2.0.20/packages/core/src/Splitter/SplitterGroup.vue)

| Data attribute | Value |
| --- | --- |
| `[data-orientation]` | "vertical" \\| "horizontal" |
| `[data-state]` | "collapsed" \\| "expanded" \\| "Present when collapsbile" |

### Panel

A collapsible section.

**Props**

| Prop | Default | Type | Description |
| --- | --- | --- | --- |
| `as` | `"div"` | `AsTag \| Component` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `asChild` |  | `boolean` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://vinicunca.dev/akar/guides/composition) guide for more details. |
| `collapsedSize` |  | `number` | The size of panel when it is collapsed; interpreted using `sizeUnit`. |
| `collapsible` |  | `boolean` | Should panel collapse when resized beyond its `minSize`. When `true`, it will be collapsed to `collapsedSize`. |
| `defaultSize` |  | `number` | Initial size of panel, interpreted using `sizeUnit` (percent by default). |
| `id` |  | `string` | Panel id (unique within group); falls back to `useId` when not provided |
| `maxSize` |  | `number` | The maximum allowable size of panel, interpreted using `sizeUnit`; defaults to `100` (percent). |
| `minSize` |  | `number` | The minimum allowable size of panel, interpreted using `sizeUnit`; defaults to `10` (percent). |
| `order` |  | `number` | The order of panel within group; required for groups with conditionally rendered panels |
| `sizeUnit` |  | `"%" \| "px"` | Unit used for sizing values; `%` by default, or `px` for fixed sizing. |

**Events**

| Event | Payload | Description |
| --- | --- | --- |
| `collapse` |  | Event handler called when panel is collapsed. |
| `expand` |  | Event handler called when panel is expanded. |
| `resize` | `size: number, prevSize: number` | Event handler called when panel is resized; size parameter is a numeric value between 1-100. |

**Slots**

| Slot | Payload | Description |
| --- | --- | --- |
| `isCollapsed` | `boolean` | Is the panel collapsed |
| `isExpanded` | `boolean` | Is the panel expanded |
| `collapse` | `(): void` | If panel is `collapsible`, collapse it fully. |
| `expand` | `(): void` | If panel is currently collapsed, expand it to its most recent size. |
| `resize` | `(size: number): void` | Resize panel to the specified percentage (1 - 100). |

**Methods**

| Method | Type | Description |
| --- | --- | --- |
| `collapse` | `() => void` | If panel is `collapsible`, collapse it fully. |
| `expand` | `() => void` | If panel is currently collapsed, expand it to its most recent size. |
| `getSize` | `() => number` | Gets the current size of the panel (in the panel's sizeUnit: percentage for '%', pixels for 'px'). |
| `resize` | `(size: number) => void` | Resize panel to the specified size (in the panel's sizeUnit: percentage for '%', pixels for 'px'). |

[SplitterPanel.vue source](https://github.com/vinicunca/akar/blob/v2.0.20/packages/core/src/Splitter/SplitterPanel.vue)

### Resize Handle

Handle that use for resizing.

**Props**

| Prop | Default | Type | Description |
| --- | --- | --- | --- |
| `as` | `"div"` | `AsTag \| Component` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `asChild` |  | `boolean` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://vinicunca.dev/akar/guides/composition) guide for more details. |
| `disabled` |  | `boolean` | Disable drag handle |
| `hitAreaMargins` |  | `PointerHitAreaMargins` | Allow this much margin when determining resizable handle hit detection |
| `id` |  | `string` | Resize handle id (unique within group); falls back to `useId` when not provided |
| `nonce` |  | `string` | Will add `nonce` attribute to the style tag which can be used by Content Security Policy.<br><br>If omitted, inherits globally from `ConfigProvider`. |
| `tabindex` | `0` | `number` | Tabindex for the handle |

**Events**

| Event | Payload | Description |
| --- | --- | --- |
| `dragging` | `isDragging: boolean` | Event handler called when dragging the handler. |

[SplitterResizeHandle.vue source](https://github.com/vinicunca/akar/blob/v2.0.20/packages/core/src/Splitter/SplitterResizeHandle.vue)

| Data attribute | Value |
| --- | --- |
| `[data-state]` | "drag" \\| "hover" \\| "inactive" |
| `[data-disabled]` | Present when disabled |
| `[data-orientation]` | "vertical" \\| "horizontal" |

## Examples

### Collapsible

Use the `collapsible` prop to allow the panel to collapse into `collapsedSize` when `minSize` is reached.

(`collapsedSize` and `minSize` props are required.)

```vue
<template>
  <SplitterGroup>
    <SplitterPanel
      collapsible
      :collapsed-size="10"
      :min-size="35"
    >
      Panel A
    </SplitterPanel>
    <SplitterResizeHandle />
    <SplitterPanel>
      Panel B
    </SplitterPanel>
  </SplitterGroup>
</template>
```

### Persist in localStorage

Use the `autoSaveId` prop to save the layout data into `localStorage`.

```vue
<template>
  <SplitterGroup auto-save-id="any-id">
    …
  </SplitterGroup>
</template>
```

### Persist layout with SSR

By default, Splitter uses `localStorage` to persist layouts. With server rendering, this can cause a flicker when the default layout (rendered on the server) is replaced with the persisted layout (in `localStorage`). The way to avoid this flicker is to also persist the layout with a cookie like so:

```vue
<!-- with Nuxt -->
<script setup lang="ts">
const layout = useCookie<number[]>('splitter:layout')
</script>

<template>
  <SplitterGroup
    direction="horizontal"
    @layout="layout = $event"
  >
    <SplitterPanel :default-size="layout[0]">
      …
    </SplitterPanel>
    <SplitterResizeHandle />
    <SplitterPanel :default-size="layout[1]">
      …
    </SplitterPanel>
  </SplitterGroup>
</template>
```

### Collapse/Expand programmatically

Sometimes panels need to resize or collapse/expand in response to user actions. `SplitterPanel` exposes the `collapse` and `expand` methods to achieve this.

```vue
<script setup lang="ts">
const panelRef = ref<InstanceType<typeof SplitterPanel>>()
</script>

<template>
  <button
    @click="panelRef?.isCollapsed ? panelRef?.expand() : panelRef?.collapse() "
  >
    {{ panelRef?.isCollapsed ? 'Expand' : 'Collapse' }}
  </button>

  <SplitterGroup>
    <SplitterPanel
      ref="panelRef"
      collapsible
      :collapsed-size="10"
      :min-size="35"
    >
      …
    </SplitterPanel>
    <SplitterResizeHandle />
    <SplitterPanel>
      …
    </SplitterPanel>
  </SplitterGroup>
</template>
```

### Pixel sizing

Use `sizeUnit="px"` when you need a panel to stay a fixed pixel width (great for sidebars) even if the parent container resizes. All sizing props (`defaultSize`, `minSize`, `maxSize`, `collapsedSize`) are interpreted using the chosen unit.

```vue
<template>
  <SplitterGroup direction="horizontal">
    <SplitterPanel
      size-unit="px"
      :default-size="240"
      :min-size="160"
      :max-size="320"
    >
      Fixed-width sidebar
    </SplitterPanel>
    <SplitterResizeHandle />
    <SplitterPanel>
      Flexible content
    </SplitterPanel>
  </SplitterGroup>
</template>
```

Pixel-sized panels also work with persistence (`autoSaveId`) and collapse/expand APIs; sizes are restored using the correct unit.

### Custom handle

Customize the handle by passing any element as the slot.

```vueline=6-8
<template>
  <SplitterGroup>
    <SplitterPanel>
      …
    </SplitterPanel>
    <SplitterResizeHandle>
      <Icon icon="radix-icons-drag-handle-dots-2" />
    </SplitterResizeHandle>
    <SplitterPanel>
      …
    </SplitterPanel>
  </SplitterGroup>
</template>
```

### SSR

Splitter component heavily relies on unique `id`, however for Vue<3.4 we don't have a reliable way of generating [SSR-friendly `id`](https://github.com/vuejs/rfcs/discussions/557).

Thus, if you are using Nuxt or other SSR framework, you are required to manually add the `id` for all Splitter components. Alternatively, you can wrap the component with `<ClientOnly>`.

```vue
<template>
  <SplitterGroup id="group-1">
    <SplitterPanel id="group-1-panel-1">
      …
    </SplitterPanel>
    <SplitterResizeHandle id="group-1-resize-1">
      <Icon icon="radix-icons-drag-handle-dots-2" />
    </SplitterResizeHandle>
    <SplitterPanel id="group-1-panel-2">
      …
    </SplitterPanel>
  </SplitterGroup>
</template>
```

## Accessibility

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

### Keyboard Interactions

| Key | Description |
| --- | --- |
| <kbd>Enter</kbd> | If the primary pane is not collapsed, collapses the pane. If the pane is collapsed, restores the splitter to its previous position. |
| <kbd>ArrowDown</kbd> | Moves a horizontal splitter down. |
| <kbd>ArrowUp</kbd> | Moves a horizontal splitter up. |
| <kbd>ArrowRight</kbd> | Moves a vertical splitter to the right. |
| <kbd>ArrowLeft</kbd> | Moves a vertical splitter to the left. |
| <kbd>Home</kbd> | Moves splitter to the position that gives the primary pane its smallest allowed size. |
| <kbd>End</kbd> | Moves splitter to the position that gives the primary pane its largest allowed size. |

---

- [ARIA](https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/)
- [GitHub](https://github.com/vinicunca/akar/blob/v2.0.20/packages/core/src/Splitter)
- [npm](https://www.npmjs.com/package/akar)


## Sitemap

See the full [sitemap](https://vinicunca.dev/sitemap.md) for all pages.
