Splitter
A splitter allow create dynamic layouts split into vertically or horizontally arranged panes. Panes are separated by the splitter bars that allow dragging to resize or expand/collapse them.
A
B
Features
- Double click a splitter to maximize pane
- Programmatic control of width or height
Installation
To use the splitter machine in your project, run the following command in your command line:
npm install @zag-js/splitter @zag-js/react # or yarn add @zag-js/splitter @zag-js/react
npm install @zag-js/splitter @zag-js/solid # or yarn add @zag-js/splitter @zag-js/solid
npm install @zag-js/splitter @zag-js/vue # or yarn add @zag-js/splitter @zag-js/vue
npm install @zag-js/splitter @zag-js/vue # or yarn add @zag-js/splitter @zag-js/vue
This command will install the framework agnostic splitter logic and the reactive utilities for your framework of choice.
Anatomy
To set up the slider correctly, you'll need to understand its anatomy and how we name its parts.
Each part includes a
data-part
attribute to help identify them in the DOM.
Usage
First, import the splitter package into your project
import * as splitter from "@zag-js/splitter"
The splitter package exports two key functions:
machine
— The state machine logic for the splitter widget.connect
— The function that translates the machine's state to JSX attributes and event handlers.
You'll also need to provide a unique
id
to theuseMachine
hook. This is used to ensure that every part has a unique identifier.
Next, import the required hooks and functions for your framework and use the splitter machine in your project 🔥
import * as splitter from "@zag-js/splitter" import { useMachine, normalizeProps } from "@zag-js/react" import { useId } from "react" export function Splitter() { const [state, send] = useMachine( splitter.machine({ id: useId(), size: [ { id: "a", size: 50 }, { id: "b", size: 50 }, ], }), ) const api = slider.connect(state, send, normalizeProps) return ( <div {...api.rootProps}> <div {...api.getPanelProps({ id: "a" })}> <p>A</p> </div> <div {...api.getResizeTriggerProps({ id: "a:b" })} /> <div {...api.getPanelProps({ id: "b" })}> <p>B</p> </div> </div> ) }
import * as splitter from "@zag-js/splitter" import { normalizeProps, useMachine } from "@zag-js/solid" import { createMemo, createUniqueId } from "solid-js" export function Splitter() { const [state, send] = useMachine( splitter.machine({ id: createUniqueId(), size: [ { id: "a", size: 50 }, { id: "b", size: 50 }, ], }) ) const api = createMemo(() => splitter.connect(state, send, normalizeProps)) return ( <div {...api().rootProps}> <div {...api().getPanelProps({ id: "a" })}> <p>A</p> </div> <div {...api().getResizeTriggerProps({ id: "a:b" })} /> <div {...api().getPanelProps({ id: "b" })}> <p>B</p> </div> </div> ) }
import * as splitter from "@zag-js/splitter" import { normalizeProps, useMachine } from "@zag-js/vue" import { computed, defineComponent } from "vue" export default defineComponent({ name: "Splitter", setup() { const [state, send] = useMachine( splitter.machine({ id: "1", size: [ { id: "a", size: 50 }, { id: "b", size: 50 } ], }) ) const apiRef = computed(() => splitter.connect(state.value, send, normalizeProps)) return () => { const api = apiRef.value return ( <div {...api.rootProps}> <div {...api.getPanelProps({ id: "a" })}> <p>A</p> </div> <div {...api.getResizeTriggerProps({ id: "a:b" })} /> <div {...api.getPanelProps({ id: "b" })}> <p>B</p> </div> </div> ) } }, })
<script setup> import * as splitter from "@zag-js/splitter" import { normalizeProps, useMachine } from "@zag-js/vue" import { computed } from "vue" const [state, send] = useMachine( splitter.machine({ id: "1", size: [ { id: "a", size: 50 }, { id: "b", size: 50 }, ] }) ) const api = computed(() => splitter.connect(state.value, send, normalizeProps)) </script> <template> <div v-bind="api.rootProps"> <div v-bind="api.getPanelProps({ id: 'a' })"> <p>A</p> </div> <div v-bind="api.getResizeTriggerProps({ id: 'a:b' })" /> <div v-bind="api.getPanelProps({ id: 'b' })"> <p>B</p> </div> </div> </template>
Listening for events
When the resize trigger of splitter changes, the onSizeChangeStart
and onSizeChangeEnd
callback is invoked.
const [state, send] = useMachine( splitter.machine({ id: useId(), size: [ { id: "a", size: 50 }, { id: "b", size: 50 }, ], onSizeChangeStart(detail) { console.log("change start", detail) }, onSizeChangeEnd(detail) { console.log("change end", detail) }, }), )
Changing the orientation
By default, the splitter is assumed to be horizontal. To change the orientation to
vertical, set the orientation
property in the machine's context to vertical
.
const [state, send] = useMachine( splitter.machine({ id: useId(), size: [ { id: "a", size: 50 }, { id: "b", size: 50 }, ], orientation: "vertical" }) )
Styling guide
Earlier, we mentioned that each accordion part has a data-part
attribute added
to them to select and style them in the DOM.
Resize trigger
When an splitter item is horizontal or vertical, a data-state
attribute is set
on the item and content elements.
[data-scope="splitter"][data-part="resize-trigger"] { /* styles for the item */ } [data-scope="splitter"][data-part="resize-trigger"][data-orientation="horizontal"] { /* styles for the item is horizontal state */ } [data-scope="splitter"][data-part="resize-trigger"][data-orientation="vertical"] { /* styles for the item is horizontal state */ } [data-scope="splitter"][data-part="resize-trigger"][data-focus] { /* styles for the item is focus state */ } [data-scope="splitter"][data-part="resize-trigger"]:active { /* styles for the item is active state */ } [data-scope="splitter"][data-part="resize-trigger"][data-disabled] { /* styles for the item is disabled state */ }
Methods and Properties
isFocused
boolean
Whether the splitter is focused.isDragging
boolean
Whether the splitter is being dragged.bounds
PanelBounds
The bounds of the currently dragged splitter handle.setToMinSize
(id: PanelId) => void
Function to set a panel to its minimum size.setToMaxSize
(id: PanelId) => void
Function to set a panel to its maximum size.setSize
(id: PanelId, size: number) => void
Function to set the size of a panel.getResizeTriggerState
(props: ResizeTriggerProps) => ResizeTriggerState
Returns the state details for a resize trigger.
Edit this page on GitHub