Splitter
<script setup lang="ts">
import { SplitterGroup, SplitterPanel, SplitterResizeHandle } from 'radix-vue'
</script>
<template>
<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="bg-white rounded-xl flex items-center justify-center">
Panel A
</SplitterPanel>
<SplitterResizeHandle id="splitter-group-1-resize-handle-1" class="w-2" />
<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="bg-white rounded-xl flex items-center justify-center">
Panel B
</SplitterPanel>
<SplitterResizeHandle id="splitter-group-2-resize-handle-1" class="h-2" />
<SplitterPanel id="splitter-group-2-panel-2" :min-size="20" class="bg-white rounded-xl flex items-center justify-center">
Panel C
</SplitterPanel>
</SplitterGroup>
</SplitterPanel>
</SplitterGroup>
</div>
</template>
Credit
This component was heavily inspired by react-resizable-panels by Bryan Vaughn.
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.
Installation
Install the component from your command line.
npm install radix-vue
Anatomy
Import all parts and piece them together.
<script setup>
import { SplitterGroup, SplitterPanel, SplitterResizeHandle } from 'radix-vue'
</script>
<template>
<SplitterGroup>
<SplitterPanel />
<SplitterResizeHandle />
</SplitterGroup>
</template>
API Reference
Group
Contains all the parts of a Splitter.
Prop | Default | Type |
---|---|---|
as | 'div' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
autoSaveId | null | string | null Unique id used to auto-save group arrangement via |
direction* | 'vertical' | 'horizontal' The group orientation of splitter. | |
id | string | null Group id; falls back to | |
keyboardResizeBy | 10 | number | null Step size when arrow key was pressed. |
storage | defaultStorage | PanelGroupStorage Custom storage API; defaults to localStorage |
Emit | Payload |
---|---|
layout | [val: number[]] Event handler called when group layout changes |
Slots (default) | Payload |
---|---|
layout | number[] Current size of layout |
Data Attribute | Value |
---|---|
[data-orientation] | "vertical" | "horizontal" |
[data-state] | "collapsed" | "expanded" | "Present when collapsbile" |
Panel
A collapsible section.
Prop | Default | Type |
---|---|---|
as | 'div' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
collapsedSize | number The size of panel when it is collapsed. | |
collapsible | boolean Should panel collapse when resized beyond its | |
defaultSize | number Initial size of panel (numeric value between 1-100) | |
id | string Panel id (unique within group); falls back to | |
maxSize | number The maximum allowable size of panel (numeric value between 1-100); defaults to | |
minSize | number The minimum allowable size of panel (numeric value between 1-100); defaults to | |
order | number The order of panel within group; required for groups with conditionally rendered panels |
Emit | Payload |
---|---|
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 (default) | Payload |
---|---|
isCollapsed | boolean Is the panel collapsed |
isExpanded | boolean Is the panel expanded |
Methods | Type |
---|---|
collapse | () => void If panel is |
expand | () => void If panel is currently collapsed, expand it to its most recent size. |
getSize | () => number Gets the current size of the panel as a percentage (1 - 100). |
resize | (size: number) => void Resize panel to the specified percentage (1 - 100). |
Resize Handle
Handle that use for resizing.
Prop | Default | Type |
---|---|---|
as | 'div' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our 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 | |
tabindex | 0 | number Tabindex for the handle |
Emit | Payload |
---|---|
dragging | [isDragging: boolean] Event handler called when dragging the handler. |
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.)
<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
.
<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:
<!-- 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.
<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>
Custom handle
Customize the handle by passing any element as the slot.
<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
.
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>
.
<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.
Keyboard Interactions
Key | Description |
---|---|
Enter | If the primary pane is not collapsed, collapses the pane. If the pane is collapsed, restores the splitter to its previous position. |
ArrowDown | Moves a horizontal splitter down. |
ArrowUp | Moves a horizontal splitter up. |
ArrowRight | Moves a vertical splitter to the right. |
ArrowLeft | Moves a vertical splitter to the left. |
Home | Moves splitter to the position that gives the primary pane its smallest allowed size. |
End | Moves splitter to the position that gives the primary pane its largest allowed size. |