An accordion is a vertically stacked set of interactive headings containing a
title, content snippet, or thumbnail representing a section of content.
## Resources
[Latest version: v2.0.0-next.1](https://www.npmjs.com/package/@zag-js/accordion)
[Logic Visualizer](https://zag-visualizer.vercel.app/accordion)
[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/accordion)
**Features**
- Full keyboard navigation
- Supports single and multiple expanded items
- Supports collapsible items
- Supports horizontal and vertical orientation
## Installation
Install the accordion package:
```bash
npm install @zag-js/accordion @zag-js/react
# or
yarn add @zag-js/accordion @zag-js/react
```
## Anatomy
Check the accordion anatomy and part names.
> Each part includes a `data-part` attribute to help identify them in the DOM.
## Usage
Import the accordion package:
```tsx
import * as accordion from "@zag-js/accordion"
```
The accordion package exports two key functions:
- `machine` - State machine logic.
- `connect` - Maps machine state to JSX props and event handlers.
> Pass a unique `id` to `useMachine` so generated element ids stay predictable.
Then use the framework integration helpers:
```tsx
import * as accordion from "@zag-js/accordion"
import { useMachine, normalizeProps } from "@zag-js/react"
import { useId } from "react"
const data = [
{ title: "Watercraft", content: "Sample accordion content" },
{ title: "Automobiles", content: "Sample accordion content" },
{ title: "Aircraft", content: "Sample accordion content" },
]
function Accordion() {
const service = useMachine(accordion.machine, { id: useId() })
const api = accordion.connect(service, normalizeProps)
return (
{data.map((item) => (
{item.content}
))}
)
}
```
You may have noticed we wrapped each accordion trigger within an `h3`. This is
recommended by the
[WAI-ARIA](https://www.w3.org/TR/wai-aria-practices-1.1/#wai-aria-roles-states-and-properties)
design pattern to ensure the accordion has the appropriate hierarchy on the
page.
### Opening multiple items
Set `multiple` to `true` to allow more than one expanded item at a time.
```tsx {2}
const service = useMachine(accordion.machine, {
multiple: true,
})
```
### Setting the initial value
Set `defaultValue` to define expanded items on first render.
```tsx
// multiple mode
const service = useMachine(accordion.machine, {
multiple: true,
defaultValue: ["home", "about"],
})
// single mode
const service = useMachine(accordion.machine, {
defaultValue: ["home"],
})
```
### Controlled accordions
Use `value` and `onValueChange` to control expanded items externally.
```tsx
import { useState } from "react"
export function ControlledAccordion() {
const [value, setValue] = useState(["home"])
const service = useMachine(accordion.machine, {
value,
onValueChange(details) {
setValue(details.value)
},
})
return (
// ...
)
}
```
### Making items collapsible
Set `collapsible` to `true` to allow closing an expanded item by clicking it
again.
> Note: If `multiple` is `true`, we internally set `collapsible` to be `true`.
```tsx {2}
const service = useMachine(accordion.machine, {
collapsible: true,
})
```
### Listening for value changes
When the accordion value changes, the `onValueChange` callback is invoked.
```tsx {2-5}
const service = useMachine(accordion.machine, {
onValueChange(details) {
// details => { value: string[] }
console.log("selected accordion:", details.value)
},
})
```
### Listening for focus changes
Use `onFocusChange` to react when keyboard focus moves between item triggers.
```tsx
const service = useMachine(accordion.machine, {
onFocusChange(details) {
// details => { value: string | null }
console.log("focused item:", details.value)
},
})
```
### Horizontal orientation
Set `orientation` to `horizontal` when rendering items side by side.
```tsx {2}
const service = useMachine(accordion.machine, {
orientation: "horizontal",
})
```
### Disabling an accordion item
To disable a specific accordion item, pass the `disabled: true` property to the
`getItemProps`, `getItemTriggerProps` and `getItemContentProps`.
When an accordion item is disabled, it is skipped from keyboard navigation and
can't be interacted with.
```tsx
//...
Content
//...
```
You can also disable the entire accordion by setting `disabled` on the machine.
```tsx {2}
const service = useMachine(accordion.machine, {
disabled: true,
})
```
## Styling guide
Each part includes a `data-part` attribute you can target in CSS.
### Open and closed state
When an accordion item expands or collapses, `data-state` is set to `open` or
`closed` on the item, trigger, and content elements.
```css
[data-part="item"][data-state="open|closed"] {
/* styles for the item is open or closed state */
}
[data-part="item-trigger"][data-state="open|closed"] {
/* styles for the item is open or closed state */
}
[data-part="item-content"][data-state="open|closed"] {
/* styles for the item is open or closed state */
}
```
### Focused state
When an accordion item's trigger is focused, a `data-focus` attribute is set on
the item and content.
```css
[data-part="item"][data-focus] {
/* styles for the item's focus state */
}
[data-part="item-trigger"]:focus {
/* styles for the trigger's focus state */
}
[data-part="item-content"][data-focus] {
/* styles for the content's focus state */
}
```
## Creating a component
Create your accordion component by abstracting the machine into your own
component.
### Usage
```tsx
import { Accordion } from "./your-accordion"
function Demo() {
return (
)
}
```
### Implementation
Use the `splitProps` utility to separate the machine's props from the
component's props.
```tsx
import * as accordion from "@zag-js/accordion"
import { useMachine, normalizeProps } from "@zag-js/react"
import { useId } from "react"
interface Item {
value: string
title: React.ReactNode
content: React.ReactNode
}
export interface AccordionProps extends Omit {
items: Item[]
}
export function Accordion(props: AccordionProps) {
const [machineProps, localProps] = accordion.splitProps(props)
const service = useMachine(accordion.machine, {
id: useId(),
...machineProps,
})
const api = accordion.connect(service, normalizeProps)
return (
{localProps.items.map((item) => (
{item.content}
))}
)
}
```
## Methods and Properties
The accordion's `api` exposes the following methods and properties:
### Machine Context
The accordion machine exposes the following context properties:
**`ids`**
Type: `ElementIds | undefined`
Description: The ids of the elements in the accordion. Useful for composition.
**`multiple`**
Type: `boolean | undefined`
Description: Whether multiple accordion items can be expanded at the same time.
**`collapsible`**
Type: `boolean | undefined`
Description: Whether an accordion item can be closed after it has been expanded.
**`value`**
Type: `string[] | undefined`
Description: The controlled value of the expanded accordion items.
**`defaultValue`**
Type: `string[] | undefined`
Description: The initial value of the expanded accordion items.
Use when you don't need to control the value of the accordion.
**`disabled`**
Type: `boolean | undefined`
Description: Whether the accordion items are disabled
**`onValueChange`**
Type: `((details: ValueChangeDetails) => void) | undefined`
Description: The callback fired when the state of expanded/collapsed accordion items changes.
**`onFocusChange`**
Type: `((details: FocusChangeDetails) => void) | undefined`
Description: The callback fired when the focused accordion item changes.
**`orientation`**
Type: `"horizontal" | "vertical" | undefined`
Description: The orientation of the accordion items.
**`loopFocus`**
Type: `boolean | undefined`
Description: Whether to loop focus back to the first/last trigger when navigating
past the end/start with the arrow keys.
**`dir`**
Type: `"ltr" | "rtl" | undefined`
Description: The document's text/writing direction.
**`id`**
Type: `string`
Description: The unique identifier of the machine.
**`getRootNode`**
Type: `(() => ShadowRoot | Document | Node) | undefined`
Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
### Machine API
The accordion `api` exposes the following methods:
**`focusedValue`**
Type: `string | null`
Description: The value of the focused accordion item.
**`value`**
Type: `string[]`
Description: The value of the accordion
**`setValue`**
Type: `(value: string[]) => void`
Description: Sets the value of the accordion
**`getItemState`**
Type: `(props: ItemProps) => ItemState`
Description: Returns the state of an accordion item.
**`getItemHeaderProps`**
Type: `(props: ItemProps) => T["element"]`
Description: Returns props for the item heading wrapper around the trigger.
Render this as a heading element (e.g. `h3`) per the WAI-ARIA accordion pattern.
### Data Attributes
**`Root`**
**`data-accordion-root`**:
**`data-orientation`**: The orientation of the accordion
**`Item`**
**`data-accordion-item`**:
**`data-state`**: "open" | "closed"
**`data-focus`**: Present when focused
**`data-disabled`**: Present when disabled
**`data-orientation`**: The orientation of the item
**`ItemContent`**
**`data-accordion-item-content`**:
**`data-state`**: "open" | "closed"
**`data-disabled`**: Present when disabled
**`data-focus`**: Present when focused
**`data-orientation`**: The orientation of the item
**`ItemIndicator`**
**`data-accordion-item-indicator`**:
**`data-state`**: "open" | "closed"
**`data-disabled`**: Present when disabled
**`data-focus`**: Present when focused
**`data-orientation`**: The orientation of the item
**`ItemHeader`**
**`data-accordion-item-header`**:
**`data-state`**: "open" | "closed"
**`data-focus`**: Present when focused
**`data-disabled`**: Present when disabled
**`data-orientation`**: The orientation of the item
**`ItemTrigger`**
**`data-accordion-item-trigger`**:
**`data-controls`**:
**`data-orientation`**: The orientation of the item
**`data-state`**: "open" | "closed"
**`data-focus`**: Present when focused
## Accessibility
### Keyboard Interactions
**`Space`**
Description: When focus is on an trigger of a collapsed item, the item is expanded
**`Enter`**
Description: When focus is on an trigger of a collapsed section, expands the section.
**`Tab`**
Description: Moves focus to the next focusable element
**`Shift + Tab`**
Description: Moves focus to the previous focusable element
**`ArrowDown`**
Description: Moves focus to the next trigger
**`ArrowUp`**
Description: Moves focus to the previous trigger.
**`Home`**
Description: When focus is on an trigger, moves focus to the first trigger.
**`End`**
Description: When focus is on an trigger, moves focus to the last trigger.
An angle slider is a circular dial that allows users to select an angle,
typically in degrees, within a 360° range. It provides an intuitive way to
control rotations or orientations, offering accessibility features.
## Resources
[Latest version: v2.0.0-next.1](https://www.npmjs.com/package/@zag-js/angle-slider)
[Logic Visualizer](https://zag-visualizer.vercel.app/angle-slider)
[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/angle-slider)
**Features**
- Fully managed keyboard navigation
- Supports touch or click on the track to update value
- Supports right-to-left direction
## Installation
Install the angle slider package:
```bash
npm install @zag-js/angle-slider @zag-js/react
# or
yarn add @zag-js/angle-slider @zag-js/react
```
## Anatomy
To set up the angle 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
Import the angle-slider package:
```tsx
import * as angleSlider from "@zag-js/angle-slider"
```
The angle slider package exports two key functions:
- `machine` - State machine logic.
- `connect` - Maps machine state to JSX props and event handlers.
> Pass a unique `id` to `useMachine` so generated element ids stay predictable.
Then use the framework integration helpers:
```tsx
import * as angleSlider from "@zag-js/angle-slider"
import { normalizeProps, useMachine } from "@zag-js/react"
import { useId } from "react"
export function AngleSlider() {
const service = useMachine(angleSlider.machine, { id: useId() })
const api = angleSlider.connect(service, normalizeProps)
return (
)
}
```
### Setting the initial value
Set `defaultValue` to define the initial slider value.
```tsx {2}
const service = useMachine(angleSlider.machine, {
defaultValue: 45,
})
```
### Controlled angle slider
Use `value` and `onValueChange` to control the value externally.
```tsx
import { useState } from "react"
export function ControlledAngleSlider() {
const [value, setValue] = useState(45)
const service = useMachine(angleSlider.machine, {
value,
onValueChange(details) {
setValue(details.value)
},
})
return (
// ...
)
}
```
### Setting the value's granularity
By default, `step` is `1`, so values move in whole-number increments. Set `step`
to control granularity.
For example, set `step` to `0.01` for two-decimal precision:
```tsx {2}
const service = useMachine(angleSlider.machine, {
step: 0.01,
})
```
### Listening for changes
When the angle slider value changes, the `onValueChange` and `onValueChangeEnd`
callbacks are invoked.
```tsx {2-7}
const service = useMachine(angleSlider.machine, {
onValueChange(details) {
console.log("value:", details.value)
console.log("as degree:", details.valueAsDegree)
},
onValueChangeEnd(details) {
console.log("final value:", details.value)
},
})
```
### Read-only mode
Set `readOnly` to prevent updates while preserving focus and form semantics.
```tsx {2}
const service = useMachine(angleSlider.machine, {
readOnly: true,
})
```
### Usage in forms
To submit the value with a form:
- Set `name` on the machine.
- Render the hidden input from `api.getHiddenInputProps()`.
```tsx {2}
const service = useMachine(angleSlider.machine, {
name: "wind-direction",
})
```
### Labeling the thumb for assistive tech
Use `aria-label` or `aria-labelledby` when you need custom labeling.
```tsx {2}
const service = useMachine(angleSlider.machine, {
"aria-label": "Wind direction",
})
```
### Using angle slider marks
To show marks or ticks along the angle slider track, use the exposed
`api.getMarkerProps()` method to position the angle slider marks at desired
angles.
```tsx {7-11}
//...
//...
```
## Styling guide
Each part includes a `data-part` attribute you can target in CSS.
### Disabled State
When the angle slider is disabled, the `data-disabled` attribute is added to the
root, label, control, thumb and marker.
```css
[data-part="root"][data-disabled] {
/* styles for root disabled state */
}
[data-part="label"][data-disabled] {
/* styles for label disabled state */
}
[data-part="control"][data-disabled] {
/* styles for control disabled state */
}
[data-part="thumb"][data-disabled] {
/* styles for thumb disabled state */
}
[data-part="range"][data-disabled] {
/* styles for thumb disabled state */
}
```
### Invalid State
When the slider is invalid, the `data-invalid` attribute is added to the root,
track, range, label, and thumb parts.
```css
[data-part="root"][data-invalid] {
/* styles for root invalid state */
}
[data-part="label"][data-invalid] {
/* styles for label invalid state */
}
[data-part="control"][data-invalid] {
/* styles for control invalid state */
}
[data-part="valueText"][data-invalid] {
/* styles for output invalid state */
}
[data-part="thumb"][data-invalid] {
/* styles for thumb invalid state */
}
[data-part="marker"][data-invalid] {
/* styles for marker invalid state */
}
```
### Styling the markers
```css
[data-part="marker"][data-state="(at|under|over)-value"] {
/* styles for when the value exceeds the marker's value */
}
```
## Methods and Properties
### Machine Context
The angle slider machine exposes the following context properties:
**`ids`**
Type: `ElementIds | undefined`
Description: The ids of the elements in the machine.
Useful for composition.
**`step`**
Type: `number | undefined`
Description: The step value for the slider.
**`value`**
Type: `number | undefined`
Description: The value of the slider.
**`defaultValue`**
Type: `number | undefined`
Description: The initial value of the slider.
Use when you don't need to control the value of the slider.
**`onValueChange`**
Type: `((details: ValueChangeDetails) => void) | undefined`
Description: The callback function for when the value changes.
**`onValueChangeEnd`**
Type: `((details: ValueChangeDetails) => void) | undefined`
Description: The callback function for when the value changes ends.
**`disabled`**
Type: `boolean | undefined`
Description: Whether the slider is disabled.
**`readOnly`**
Type: `boolean | undefined`
Description: Whether the slider is read-only.
**`invalid`**
Type: `boolean | undefined`
Description: Whether the slider is invalid.
**`name`**
Type: `string | undefined`
Description: The name of the slider. Useful for form submission.
**`aria-label`**
Type: `string | undefined`
Description: The accessible label for the slider thumb.
**`aria-labelledby`**
Type: `string | undefined`
Description: The id of the element that labels the slider thumb.
**`dir`**
Type: `"ltr" | "rtl" | undefined`
Description: The document's text/writing direction.
**`id`**
Type: `string`
Description: The unique identifier of the machine.
**`getRootNode`**
Type: `(() => ShadowRoot | Document | Node) | undefined`
Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
### Machine API
The angle slider `api` exposes the following methods:
**`value`**
Type: `number`
Description: The current value of the angle slider
**`valueAsDegree`**
Type: `string`
Description: The current value as a degree string
**`setValue`**
Type: `(value: number) => void`
Description: Sets the value of the angle slider
**`dragging`**
Type: `boolean`
Description: Whether the slider is being dragged.
### Data Attributes
**`Root`**
**`data-angle-slider-root`**:
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-readonly`**: Present when read-only
**`Label`**
**`data-angle-slider-label`**:
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-readonly`**: Present when read-only
**`Control`**
**`data-angle-slider-control`**:
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-readonly`**: Present when read-only
**`Thumb`**
**`data-angle-slider-thumb`**:
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-readonly`**: Present when read-only
**`Marker`**
**`data-angle-slider-marker`**:
**`data-value`**: The value of the item
**`data-state`**:
**`data-disabled`**: Present when disabled
### CSS Variables
### Keyboard Interactions
**`ArrowRight`**
Description: Increments the angle slider based on defined step
**`ArrowLeft`**
Description: Decrements the angle slider based on defined step
**`ArrowUp`**
Description: Decreases the value by the step amount.
**`ArrowDown`**
Description: Increases the value by the step amount.
**`Shift + ArrowUp`**
Description: Decreases the value by a larger step
**`Shift + ArrowDown`**
Description: Increases the value by a larger step
**`Home`**
Description: Sets the value to 0 degrees.
**`End`**
Description: Sets the value to 360 degrees.
An avatar represents a user profile picture. It displays an image or fallback
content in a container.
Avatar supports fallback text or elements when the image fails to load or when
no image is provided.
## Resources
[Latest version: v2.0.0-next.1](https://www.npmjs.com/package/@zag-js/avatar)
[Logic Visualizer](https://zag-visualizer.vercel.app/avatar)
[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/avatar)
## Installation
Install the avatar package:
```bash
npm install @zag-js/avatar @zag-js/react
# or
yarn add @zag-js/avatar @zag-js/react
```
## Anatomy
Check the avatar anatomy and part names.
> Each part includes a `data-part` attribute to help identify them in the DOM.
## Usage
Import the avatar package:
```tsx
import * as avatar from "@zag-js/avatar"
```
The avatar package exports two key functions:
- `machine` - State machine logic.
- `connect` - Maps machine state to JSX props and event handlers.
> Pass a unique `id` to `useMachine` so generated element ids stay predictable.
Then use the framework integration helpers:
```tsx
import * as avatar from "@zag-js/avatar"
import { useMachine, normalizeProps } from "@zag-js/react"
import { useId } from "react"
function Avatar() {
const service = useMachine(avatar.machine, { id: useId() })
const api = avatar.connect(service, normalizeProps)
return (
PA
)
}
```
### Loading status
The image loading status is `loading`, `loaded`, or `error`. Read the current
value from `api.status`, or react to changes with `onStatusChange`.
```tsx {2}
const service = useMachine(avatar.machine, {
onStatusChange(details) {
// details => { status: "loading" | "error" | "loaded" }
},
})
```
### Updating the image source
In framework adapters, update the image `src` through your own state. The
machine watches the rendered image and reacts automatically, resetting to
`loading` and then to `loaded` or `error`.
For imperative usage like vanilla JS, `api.setSrc` sets the source on the
machine's image element directly.
```tsx
api.setSrc(nextSrc)
```
## Styling guide
Each avatar part includes a `data-part` attribute you can target in CSS.
```css
[data-scope="avatar"][data-part="root"] {
/* Styles for the root part */
}
[data-scope="avatar"][data-part="image"] {
/* Styles for the image part */
}
[data-scope="avatar"][data-part="fallback"] {
/* Styles for the fallback part */
}
```
## Creating a component
Create your avatar component by abstracting the machine into your own component.
### Usage
```tsx
import { Avatar } from "./your-avatar"
function Demo() {
return (
)
}
```
### Implementation
Use the `splitProps` utility to separate the machine's props from the
component's props.
```tsx
import * as avatar from "@zag-js/avatar"
import { useMachine, normalizeProps } from "@zag-js/react"
import { useId } from "react"
export interface AvatarProps extends Omit {
/**
* The src of the avatar image
*/
src?: string
/**
* The srcSet of the avatar image
*/
srcSet?: string
/**
* The name of the avatar
*/
name: string
}
function Avatar(props: AvatarProps) {
const [machineProps, localProps] = avatar.splitProps(props)
const service = useMachine(avatar.machine, {
id: useId(),
...machineProps,
})
const api = avatar.connect(service, normalizeProps)
return (
{getInitials(localProps.name)}
)
}
function getInitials(name: string) {
return name
.split(" ")
.map((word) => word[0])
.join("")
}
```
## Methods and Properties
### Machine Context
The avatar machine exposes the following context properties:
**`onStatusChange`**
Type: `((details: StatusChangeDetails) => void) | undefined`
Description: Functional called when the image loading status changes.
**`ids`**
Type: `ElementIds | undefined`
Description: The ids of the elements in the avatar. Useful for composition.
**`id`**
Type: `string`
Description: The unique identifier of the machine.
**`getRootNode`**
Type: `(() => ShadowRoot | Document | Node) | undefined`
Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
**`dir`**
Type: `"ltr" | "rtl" | undefined`
Description: The document's text/writing direction.
### Machine API
The avatar `api` exposes the following methods:
**`status`**
Type: `LoadStatus`
Description: The current image loading status.
**`loaded`**
Type: `boolean`
Description: Whether the image is loaded.
**`setSrc`**
Type: `(src: string) => void`
Description: Function to set new src.
**`setLoaded`**
Type: `VoidFunction`
Description: Function to set loaded state.
**`setError`**
Type: `VoidFunction`
Description: Function to set error state.
### Data Attributes
**`Image`**
**`data-avatar-image`**:
**`data-state`**: "visible" | "hidden"
**`Fallback`**
**`data-avatar-fallback`**:
**`data-state`**: "hidden" | "visible"
A carousel component that leverages native CSS Scroll Snap for smooth,
performant scrolling between slides.
## Resources
[Latest version: v2.0.0-next.1](https://www.npmjs.com/package/@zag-js/carousel)
[Logic Visualizer](https://zag-visualizer.vercel.app/carousel)
[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/carousel)
**Features**
- Uses native CSS Scroll Snap
- Supports horizontal and vertical orientations
- Supports slide alignment (`start`, `center`, `end`)
- Supports showing multiple slides at a time
- Supports looping and auto-playing
- Supports custom spacing between slides
## Installation
Install the carousel package:
```bash
npm install @zag-js/carousel @zag-js/react
# or
yarn add @zag-js/carousel @zag-js/react
```
## Anatomy
Check the carousel anatomy and part names.
> Each part includes a `data-part` attribute to help identify them in the DOM.
## Usage
Import the carousel package:
```tsx
import * as carousel from "@zag-js/carousel"
```
The carousel package exports two key functions:
- `machine` - State machine logic.
- `connect` - Maps machine state to JSX props and event handlers.
> Pass a unique `id` to `useMachine` so generated element ids stay predictable.
Then use the framework integration helpers:
> **Note:** The carousel requires that you provide a `count` property in the
> machine context. This is the total number of slides.
```tsx
import * as carousel from "@zag-js/carousel"
import { normalizeProps, useMachine } from "@zag-js/react"
const items = [
"https://tinyurl.com/5b6ka8jd",
"https://tinyurl.com/7rmccdn5",
"https://tinyurl.com/59jxz9uu",
]
export function Carousel() {
const service = useMachine(carousel.machine, {
id: "1",
count: items.length,
})
const api = carousel.connect(service, normalizeProps)
return (
{items.map((image, index) => (
))}
{api.pageSnapPoints.map((_, index) => (
))}
)
}
```
### Vertical orientation
Set `orientation` to `vertical` to render a vertical carousel.
```tsx {2}
const service = useMachine(carousel.machine, {
orientation: "vertical",
})
```
### Setting the initial slide
Set `defaultPage` to define the initial page.
> The `defaultPage` corresponds to the scroll snap position index based on the
> layout. It does not necessarily correspond to the index of the slide in the
> carousel.
```tsx {2}
const service = useMachine(carousel.machine, {
defaultPage: 2,
})
```
### Controlling the current page
Use `page` and `onPageChange` for controlled navigation.
```tsx
const service = useMachine(carousel.machine, {
count: 8,
page,
onPageChange(details) {
setPage(details.page)
},
})
```
### Setting slides per page
Set `slidesPerPage` to control how many slides are visible per page.
```tsx {2}
const service = useMachine(carousel.machine, {
slidesPerPage: 2,
})
```
### Setting slides per move
Set `slidesPerMove` to control how many slides advance on next/previous.
```tsx {2}
const service = useMachine(carousel.machine, {
slidesPerMove: 2,
})
```
**Considerations**
- If the value is `auto`, the carousel will move the number of slides equal to
the number of slides per page.
- Ensure the `slidesPerMove` is less than or equal to the `slidesPerPage` to
avoid skipping slides.
- On touch devices, `slidesPerMove` is not enforced during active swiping. The
browser's native scrolling and CSS Scroll Snap determine slide movement for
optimal performance and UX.
### Looping pages
Set `loop` to `true` to wrap around from last to first page.
```tsx {2}
const service = useMachine(carousel.machine, {
loop: true,
})
```
### Setting the gap between slides
Set `spacing` to control the gap between slides.
```tsx {2}
const service = useMachine(carousel.machine, {
spacing: "16px",
})
```
### Setting viewport padding
Set `itemSpacing` to keep neighboring slides partially visible.
```tsx {2}
const service = useMachine(carousel.machine, {
itemSpacing: "16px",
})
```
### Variable-width slides
Set `autoSize` to `true` when slides have different widths.
```tsx {2}
const service = useMachine(carousel.machine, {
autoSize: true,
})
```
### Listening for page changes
When the carousel page changes, the `onPageChange` callback is invoked.
```tsx {2-5}
const service = useMachine(carousel.machine, {
onPageChange(details) {
// details => { page: number }
console.log("selected page:", details.page)
},
})
```
### Listening for drag and autoplay status
Use status callbacks to react to dragging and autoplay lifecycle changes.
```tsx
const service = useMachine(carousel.machine, {
onDragStatusChange(details) {
console.log(details.type, details.isDragging)
},
onAutoplayStatusChange(details) {
console.log(details.type, details.isPlaying)
},
})
```
### Dragging the carousel
Set `allowMouseDrag` to `true` to drag the carousel with a mouse.
```tsx {2}
const service = useMachine(carousel.machine, {
allowMouseDrag: true,
})
```
### Autoplaying the carousel
Set `autoPlay` to `true` to start automatic slide movement.
```tsx {2}
const service = useMachine(carousel.machine, {
autoPlay: true,
})
```
Alternatively, you can configure the autoplay interval by setting the `delay`
property.
```tsx {2}
const service = useMachine(carousel.machine, {
autoPlay: { delay: 2000 },
})
```
### Customizing accessibility messages
Use `translations` to customize localized trigger, item, and progress text.
```tsx
const service = useMachine(carousel.machine, {
count: 5,
translations: {
nextTrigger: "Next slide",
prevTrigger: "Previous slide",
indicator: (index) => `Go to slide ${index + 1}`,
item: (index, count) => `Slide ${index + 1} of ${count}`,
autoplayStart: "Start autoplay",
autoplayStop: "Stop autoplay",
progressText: ({ page, totalPages }) => `Page ${page + 1} of ${totalPages}`,
},
})
```
## Styling guide
Each part includes a `data-part` attribute you can target in CSS.
```css
[data-part="root"] {
/* styles for the root part */
}
[data-part="item-group"] {
/* styles for the item-group part */
}
[data-part="item"] {
/* styles for the root part */
}
[data-part="control"] {
/* styles for the control part */
}
[data-part="next-trigger"] {
/* styles for the next-trigger part */
}
[data-part="prev-trigger"] {
/* styles for the prev-trigger part */
}
[data-part="indicator-group"] {
/* styles for the indicator-group part */
}
[data-part="indicator"] {
/* styles for the indicator part */
}
[data-part="autoplay-trigger"] {
/* styles for the autoplay-trigger part */
}
```
### Active state
When a carousel's indicator is active, a `data-current` attribute is set on the
indicator.
```css
[data-part="indicator"][data-current] {
/* styles for the indicator's active state */
}
```
## Methods and Properties
The carousel's `api` exposes the following methods and properties:
### Machine Context
The carousel machine exposes the following context properties:
**`ids`**
Type: `ElementIds | undefined`
Description: The ids of the elements in the carousel. Useful for composition.
**`translations`**
Type: `IntlTranslations | undefined`
Description: The localized messages to use.
**`slidesPerPage`**
Type: `number | undefined`
Description: The number of slides to show at a time.
**`autoSize`**
Type: `boolean | undefined`
Description: Whether to enable variable width slides.
**`slidesPerMove`**
Type: `number | "auto" | undefined`
Description: The number of slides to scroll at a time.
When set to `auto`, the number of slides to scroll is determined by the
`slidesPerPage` property.
**`autoPlay`**
Type: `boolean | { delay: number } | undefined`
Description: Whether to scroll automatically. The default delay is 4000ms.
**`allowMouseDrag`**
Type: `boolean | undefined`
Description: Whether to allow scrolling via dragging with mouse
**`loop`**
Type: `boolean | undefined`
Description: Whether the carousel should loop around.
**`page`**
Type: `number | undefined`
Description: The controlled page of the carousel.
**`defaultPage`**
Type: `number | undefined`
Description: The initial page to scroll to when rendered.
Use when you don't need to control the page of the carousel.
**`spacing`**
Type: `string | undefined`
Description: The amount of space between items.
**`itemSpacing`**
Type: `string | undefined`
Description: Defines the extra space added around the scrollable area,
enabling nearby items to remain partially in view.
**`onPageChange`**
Type: `((details: PageChangeDetails) => void) | undefined`
Description: Function called when the page changes.
**`inViewThreshold`**
Type: `number | number[] | undefined`
Description: The threshold for determining if an item is in view.
**`snapType`**
Type: `"proximity" | "mandatory" | undefined`
Description: The snap type of the item.
**`count`**
Type: `number`
Description: The total number of slides.
Useful for SSR to render the initial snap points.
**`onDragStatusChange`**
Type: `((details: DragStatusDetails) => void) | undefined`
Description: Function called when the drag status changes.
**`onAutoplayStatusChange`**
Type: `((details: AutoplayStatusDetails) => void) | undefined`
Description: Function called when the autoplay status changes.
**`dir`**
Type: `"ltr" | "rtl" | undefined`
Description: The document's text/writing direction.
**`id`**
Type: `string`
Description: The unique identifier of the machine.
**`getRootNode`**
Type: `(() => ShadowRoot | Document | Node) | undefined`
Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
**`orientation`**
Type: `"horizontal" | "vertical" | undefined`
Description: The orientation of the element.
### Machine API
The carousel `api` exposes the following methods:
**`page`**
Type: `number`
Description: The current index of the carousel
**`pageSnapPoints`**
Type: `number[]`
Description: The current snap points of the carousel
**`isPlaying`**
Type: `boolean`
Description: Whether the carousel is auto playing
**`isDragging`**
Type: `boolean`
Description: Whether the carousel is being dragged. This only works when `draggable` is true.
**`canScrollNext`**
Type: `boolean`
Description: Whether the carousel is can scroll to the next view
**`canScrollPrev`**
Type: `boolean`
Description: Whether the carousel is can scroll to the previous view
**`scrollToIndex`**
Type: `(index: number, instant?: boolean) => void`
Description: Function to scroll to a specific item index
**`scrollTo`**
Type: `(page: number, instant?: boolean) => void`
Description: Function to scroll to a specific page
**`scrollNext`**
Type: `(instant?: boolean) => void`
Description: Function to scroll to the next page
**`scrollPrev`**
Type: `(instant?: boolean) => void`
Description: Function to scroll to the previous page
**`getProgress`**
Type: `() => number`
Description: Returns the current scroll progress as a percentage
**`getProgressText`**
Type: `() => string`
Description: Returns the progress text
**`play`**
Type: `VoidFunction`
Description: Function to start/resume autoplay
**`pause`**
Type: `VoidFunction`
Description: Function to pause autoplay
**`isInView`**
Type: `(index: number) => boolean`
Description: Whether the item is in view
**`refresh`**
Type: `VoidFunction`
Description: Function to re-compute the snap points
and clamp the page
### Data Attributes
**`Root`**
**`data-carousel-root`**:
**`data-orientation`**: The orientation of the carousel
**`ItemGroup`**
**`data-carousel-item-group`**:
**`data-orientation`**: The orientation of the item
**`data-dragging`**: Present when in the dragging state
**`Item`**
**`data-carousel-item`**:
**`data-index`**: The index of the item
**`data-inview`**: Present when in viewport
**`data-orientation`**: The orientation of the item
**`Control`**
**`data-carousel-control`**:
**`data-orientation`**: The orientation of the control
**`PrevTrigger`**
**`data-carousel-prev-trigger`**:
**`data-orientation`**: The orientation of the prevtrigger
**`NextTrigger`**
**`data-carousel-next-trigger`**:
**`data-orientation`**: The orientation of the nexttrigger
**`IndicatorGroup`**
**`data-carousel-indicator-group`**:
**`data-orientation`**: The orientation of the indicatorgroup
**`Indicator`**
**`data-carousel-indicator`**:
**`data-orientation`**: The orientation of the indicator
**`data-index`**: The index of the item
**`data-readonly`**: Present when read-only
**`data-current`**: Present when current
**`AutoplayTrigger`**
**`data-carousel-autoplay-trigger`**:
**`data-orientation`**: The orientation of the autoplaytrigger
**`data-pressed`**: Present when pressed
### CSS Variables
A cascade select component allows users to select from hierarchical data through
multiple linked levels of dropdown menus.
## Resources
[Latest version: v2.0.0-next.1](https://www.npmjs.com/package/@zag-js/cascade-select)
[Logic Visualizer](https://zag-visualizer.vercel.app/cascade-select)
[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/cascade-select)
**Features**
- Supports hierarchical data with unlimited depth levels
- Full keyboard navigation across all levels with arrow keys
- Supports single and multiple selections
- Supports both click and hover triggering modes
- Supports looping keyboard navigation
- Built-in accessibility with ARIA roles and keyboard interactions
- Supports disabled items and read-only state
- Form integration with hidden input element
- Supports right-to-left direction
## Installation
Install the cascade select package:
```bash
npm install @zag-js/cascade-select @zag-js/react
# or
yarn add @zag-js/cascade-select @zag-js/react
```
## Anatomy
Check the cascade select anatomy and part names.
> Each part includes a `data-part` attribute to help identify them in the DOM.
## Usage
Import the cascade select package:
```tsx
import * as cascadeSelect from "@zag-js/cascade-select"
```
These are the key exports:
- `machine` - State machine logic.
- `connect` - Maps machine state to JSX props and event handlers.
- `collection` - Creates a tree collection from your hierarchical data.
### Create the collection
Use the `collection` function to create a tree collection from your hierarchical
data. Pass a `rootNode` along with functions to extract each node's value,
string label, and children.
```ts
import * as cascadeSelect from "@zag-js/cascade-select"
interface Node {
label: string
value: string
children?: Node[]
}
const collection = cascadeSelect.collection({
nodeToValue: (node) => node.value,
nodeToString: (node) => node.label,
nodeToChildren: (node) => node.children,
rootNode: {
label: "ROOT",
value: "ROOT",
children: [
{
label: "North America",
value: "north-america",
children: [
{
label: "United States",
value: "us",
children: [
{ label: "New York", value: "ny" },
{ label: "California", value: "ca" },
],
},
{ label: "Canada", value: "canada" },
],
},
{
label: "Africa",
value: "africa",
children: [
{ label: "Nigeria", value: "ng" },
{ label: "Kenya", value: "ke" },
],
},
],
},
})
```
### Create the cascade select
> Pass a unique `id` to `useMachine` so generated element ids stay predictable.
Then use the framework integration helpers:
```tsx
import * as cascadeSelect from "@zag-js/cascade-select"
import { normalizeProps, Portal, useMachine } from "@zag-js/react"
import { JSX, useId } from "react"
// 1. Create the collection (see above)
const collection = cascadeSelect.collection({
// ...
})
// 2. Create the recursive tree node
interface TreeNodeProps {
node: Node
indexPath?: number[]
value?: string[]
api: cascadeSelect.Api
}
const TreeNode = (props: TreeNodeProps): JSX.Element => {
const { node, indexPath = [], value = [], api } = props
const nodeProps = { indexPath, value, item: node }
const nodeState = api.getItemState(nodeProps)
const children = collection.getNodeChildren(node)
return (
<>
{nodeState.highlightedChild &&
collection.isBranchNode(nodeState.highlightedChild) && (
)}
>
)
}
// 3. Create the cascade select
export function CascadeSelect() {
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
})
const api = cascadeSelect.connect(service, normalizeProps)
return (
)
}
```
### Setting the initial value
Use the `defaultValue` property to set the initial value of the cascade select.
> The `value` property must be an array of string paths. Each path is an array
> of values from the root to the selected leaf item.
```tsx {4}
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
defaultValue: [["north-america", "us", "ny"]],
})
```
### Controlled selection
Use `value` and `onValueChange` for controlled selection state.
```tsx
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
value,
onValueChange(details) {
setValue(details.value)
},
})
```
### Selecting multiple values
To allow selecting multiple values, set the `multiple` property to `true`.
```tsx {4}
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
multiple: true,
})
```
### Hover triggering
By default, items are highlighted when clicked. To highlight items on hover
instead (like a traditional cascading menu), set the `highlightTrigger` property
to `"hover"`.
```tsx {4}
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
highlightTrigger: "hover",
})
```
### Allowing parent selection
By default, only leaf nodes (items without children) can be selected. To allow
parent (branch) nodes to also be selectable, set `allowParentSelection` to
`true`.
```tsx {4}
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
allowParentSelection: true,
})
```
### Close on select
By default, the menu closes when you select an item. Set `closeOnSelect` to
`false` to keep it open.
```tsx {4}
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
closeOnSelect: false,
})
```
### Looping the keyboard navigation
By default, arrow-key navigation stops at the first and last items. Set
`loopFocus: true` to loop back around.
```tsx {4}
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
loopFocus: true,
})
```
### Listening for highlight changes
When an item is highlighted with the pointer or keyboard, use the
`onHighlightChange` to listen for the change and do something with it.
```tsx {3-6}
const service = useMachine(cascadeSelect.machine, {
id: useId(),
onHighlightChange(details) {
// details => { highlightedValue: string[], highlightedItems: Item[] }
console.log(details)
},
})
```
### Setting the initial highlighted path
Use `defaultHighlightedValue` to set the initially highlighted path.
```tsx
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
defaultHighlightedValue: ["north-america", "us"],
})
```
### Controlled highlighted path
Use `highlightedValue` and `onHighlightChange` to control highlighting
externally.
```tsx
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
highlightedValue,
onHighlightChange(details) {
setHighlightedValue(details.highlightedValue)
},
})
```
### Listening for selection changes
When an item is selected, use the `onValueChange` property to listen for the
change and do something with it.
```tsx {4-7}
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
onValueChange(details) {
// details => { value: string[][], items: Item[][] }
console.log(details)
},
})
```
### Listening for open and close events
Use `onOpenChange` to listen for open and close events.
```tsx {4-7}
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
onOpenChange(details) {
// details => { open: boolean, value: string[][] }
console.log(details)
},
})
```
### Controlling open state
Use `open` and `onOpenChange` for controlled open state, or `defaultOpen` for an
uncontrolled initial state.
```tsx
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
open,
onOpenChange({ open }) {
setOpen(open)
},
})
```
```tsx
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
defaultOpen: true,
})
```
### Positioning submenu panels
Use `positioning` to configure placement and collision behavior.
```tsx
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
positioning: {
placement: "right-start",
gutter: 4,
},
})
```
### Custom scroll behavior
Use `scrollToIndexFn` to customize how each level scrolls highlighted items into
view.
```tsx
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
scrollToIndexFn(details) {
// details => { index, depth, immediate? }
customScroll(details)
},
})
```
### Customizing the trigger label
Use `formatValue` to control how selected paths are rendered in the trigger.
```tsx
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
formatValue(selectedItems) {
return selectedItems
.map((path) => path.map((item) => item.label).join(" / "))
.join(", ")
},
})
```
### Usage within a form
To use cascade select in a form, pass `name`. A hidden input is rendered with
`getHiddenInputProps()`.
```tsx {4}
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
name: "location",
})
// In your JSX
```
If the hidden input belongs to a different form element, also set `form`.
```tsx
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
name: "location",
form: "checkout-form",
})
```
### Validation and read-only state
Use `readOnly`, `required`, and `invalid` to control interaction and form state.
```tsx
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
readOnly: true,
required: true,
invalid: false,
})
```
## Styling guide
Each cascade select part includes a `data-part` attribute you can target in CSS.
### Open and closed state
When the cascade select is open, the trigger and content is given a `data-state`
attribute.
```css
[data-part="trigger"][data-state="open|closed"] {
/* styles for open or closed state */
}
[data-part="content"][data-state="open|closed"] {
/* styles for open or closed state */
}
```
### Selected state
Items are given a `data-state` attribute, indicating whether they are selected.
```css
[data-part="item"][data-state="checked|unchecked"] {
/* styles for selected or unselected state */
}
```
### Highlighted state
When an item is highlighted, via keyboard navigation or pointer, it is given a
`data-highlighted` attribute.
```css
[data-part="item"][data-highlighted] {
/* styles for highlighted state */
}
```
### Branch items
When an item has children (is a branch node), it is given a `data-has-children`
attribute.
```css
[data-part="item"][data-has-children] {
/* styles for items with children (branch nodes) */
}
```
### Invalid state
When the cascade select is invalid, the label and trigger is given a
`data-invalid` attribute.
```css
[data-part="label"][data-invalid] {
/* styles for invalid state */
}
[data-part="trigger"][data-invalid] {
/* styles for invalid state */
}
```
### Disabled state
When the cascade select is disabled, the trigger and label is given a
`data-disabled` attribute.
```css
[data-part="trigger"][data-disabled] {
/* styles for disabled state */
}
[data-part="label"][data-disabled] {
/* styles for disabled label state */
}
[data-part="item"][data-disabled] {
/* styles for disabled item state */
}
```
### Empty state
When no option is selected, the trigger is given a `data-placeholder-shown`
attribute.
```css
[data-part="trigger"][data-placeholder-shown] {
/* styles for empty state */
}
```
## Methods and Properties
### Machine Context
The cascade select machine exposes the following context properties:
**`collection`**
Type: `TreeCollection | undefined`
Description: The tree collection data
**`ids`**
Type: `ElementIds | undefined`
Description: The ids of the cascade-select elements. Useful for composition.
**`name`**
Type: `string | undefined`
Description: The name attribute of the underlying input element
**`form`**
Type: `string | undefined`
Description: The form attribute of the underlying input element
**`value`**
Type: `string[][] | undefined`
Description: The controlled value of the cascade-select
**`defaultValue`**
Type: `string[][] | undefined`
Description: The initial value of the cascade-select when rendered.
Use when you don't need to control the value.
**`highlightedValue`**
Type: `string[] | undefined`
Description: The controlled highlighted value of the cascade-select
**`defaultHighlightedValue`**
Type: `string[] | undefined`
Description: The initial highlighted value of the cascade-select when rendered.
**`multiple`**
Type: `boolean | undefined`
Description: Whether to allow multiple selections
**`open`**
Type: `boolean | undefined`
Description: The controlled open state of the cascade-select
**`defaultOpen`**
Type: `boolean | undefined`
Description: The initial open state of the cascade-select when rendered.
Use when you don't need to control the open state.
**`highlightTrigger`**
Type: `"click" | "hover" | undefined`
Description: What triggers highlighting of items
**`closeOnSelect`**
Type: `boolean | undefined`
Description: Whether the cascade-select should close when an item is selected
**`loopFocus`**
Type: `boolean | undefined`
Description: Whether the cascade-select should loop focus when navigating with keyboard
**`disabled`**
Type: `boolean | undefined`
Description: Whether the cascade-select is disabled
**`readOnly`**
Type: `boolean | undefined`
Description: Whether the cascade-select is read-only
**`required`**
Type: `boolean | undefined`
Description: Whether the cascade-select is required
**`invalid`**
Type: `boolean | undefined`
Description: Whether the cascade-select is invalid
**`positioning`**
Type: `PositioningOptions | undefined`
Description: The positioning options for the cascade-select content
**`scrollToIndexFn`**
Type: `((details: ScrollToIndexDetails) => void) | undefined`
Description: Function to scroll to a specific index in a list
**`formatValue`**
Type: `((selectedItems: T[][]) => string) | undefined`
Description: Function to format the display value
**`onValueChange`**
Type: `((details: ValueChangeDetails) => void) | undefined`
Description: Called when the value changes
**`onHighlightChange`**
Type: `((details: HighlightChangeDetails) => void) | undefined`
Description: Called when the highlighted value changes
**`onOpenChange`**
Type: `((details: OpenChangeDetails) => void) | undefined`
Description: Called when the open state changes
**`allowParentSelection`**
Type: `boolean | undefined`
Description: Whether parent (branch) items can be selectable
**`dir`**
Type: `"ltr" | "rtl" | undefined`
Description: The document's text/writing direction.
**`id`**
Type: `string`
Description: The unique identifier of the machine.
**`getRootNode`**
Type: `(() => ShadowRoot | Document | Node) | undefined`
Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
**`onPointerDownOutside`**
Type: `((event: PointerDownOutsideEvent) => void) | undefined`
Description: Function called when the pointer is pressed down outside the component
**`onFocusOutside`**
Type: `((event: FocusOutsideEvent) => void) | undefined`
Description: Function called when the focus is moved outside the component
**`onInteractOutside`**
Type: `((event: InteractOutsideEvent) => void) | undefined`
Description: Function called when an interaction happens outside the component
### Machine API
The cascade select `api` exposes the following methods:
**`collection`**
Type: `TreeCollection`
Description: The tree collection data
**`open`**
Type: `boolean`
Description: Whether the cascade-select is open
**`focused`**
Type: `boolean`
Description: Whether the cascade-select is focused
**`multiple`**
Type: `boolean`
Description: Whether the cascade-select allows multiple selections
**`disabled`**
Type: `boolean`
Description: Whether the cascade-select is disabled
**`highlightedValue`**
Type: `string[]`
Description: The value of the highlighted item
**`highlightedItems`**
Type: `V[]`
Description: The items along the highlighted path
**`selectedItems`**
Type: `V[][]`
Description: The selected items
**`hasSelectedItems`**
Type: `boolean`
Description: Whether there's a selected option
**`empty`**
Type: `boolean`
Description: Whether the cascade-select value is empty
**`value`**
Type: `string[][]`
Description: The current value of the cascade-select
**`valueAsString`**
Type: `string`
Description: The current value as text
**`focus`**
Type: `() => void`
Description: Function to focus on the select input
**`reposition`**
Type: `(options?: any) => void`
Description: Function to set the positioning options of the cascade-select
**`setOpen`**
Type: `(open: boolean) => void`
Description: Function to open the cascade-select
**`setHighlightValue`**
Type: `(value: string | string[]) => void`
Description: Function to set the highlighted value (path or single value to find)
**`clearHighlightValue`**
Type: `() => void`
Description: Function to clear the highlighted value
**`selectValue`**
Type: `(value: string[]) => void`
Description: Function to select a value
**`setValue`**
Type: `(value: string[][]) => void`
Description: Function to set the value
**`clearValue`**
Type: `(value?: string[] | undefined) => void`
Description: Function to clear the value
**`getItemState`**
Type: `(props: ItemProps) => ItemState`
Description: Returns the state of a cascade-select item
### Data Attributes
**`Root`**
**`data-cascade-select-root`**:
**`data-disabled`**: Present when disabled
**`data-readonly`**: Present when read-only
**`data-invalid`**: Present when invalid
**`data-state`**: "open" | "closed"
**`Label`**
**`data-cascade-select-label`**:
**`data-disabled`**: Present when disabled
**`data-readonly`**: Present when read-only
**`data-invalid`**: Present when invalid
**`Control`**
**`data-cascade-select-control`**:
**`data-disabled`**: Present when disabled
**`data-focus`**: Present when focused
**`data-readonly`**: Present when read-only
**`data-invalid`**: Present when invalid
**`data-state`**: "open" | "closed"
**`Trigger`**
**`data-cascade-select-trigger`**:
**`data-state`**: "open" | "closed"
**`data-disabled`**: Present when disabled
**`data-readonly`**: Present when read-only
**`data-invalid`**: Present when invalid
**`data-focus`**: Present when focused
**`data-placement`**: The placement of the trigger
**`data-side`**: The side of the trigger that the trigger is positioned on
**`data-placeholder-shown`**: Present when placeholder is shown
**`ClearTrigger`**
**`data-cascade-select-clear-trigger`**:
**`data-disabled`**: Present when disabled
**`data-readonly`**: Present when read-only
**`data-invalid`**: Present when invalid
**`Content`**
**`data-cascade-select-content`**:
**`data-activedescendant`**: The id the active descendant of the content
**`data-state`**: "open" | "closed"
**`List`**
**`data-cascade-select-list`**:
**`data-depth`**: The depth of the item
**`Indicator`**
**`data-cascade-select-indicator`**:
**`data-state`**: "open" | "closed"
**`data-disabled`**: Present when disabled
**`data-readonly`**: Present when read-only
**`data-invalid`**: Present when invalid
**`Item`**
**`data-cascade-select-item`**:
**`data-value`**: The value of the item
**`data-disabled`**: Present when disabled
**`data-highlighted`**: Present when highlighted
**`data-selected`**: Present when selected
**`data-depth`**: The depth of the item
**`data-state`**: "checked" | "unchecked"
**`data-type`**: The type of the item
**`data-index-path`**:
**`ItemText`**
**`data-cascade-select-item-text`**:
**`data-value`**: The value of the item
**`data-highlighted`**: Present when highlighted
**`data-state`**: "checked" | "unchecked"
**`data-disabled`**: Present when disabled
**`ItemIndicator`**
**`data-cascade-select-item-indicator`**:
**`data-value`**: The value of the item
**`data-highlighted`**: Present when highlighted
**`data-type`**: The type of the item
**`data-state`**: "checked" | "unchecked"
**`ValueText`**
**`data-cascade-select-value-text`**:
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-focus`**: Present when focused
### CSS Variables
## Accessibility
Adheres to the
[ListBox WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/listbox).
### Keyboard Interactions
**`Space`**
Description: When focus is on trigger, opens the cascade select and focuses the first item. When focus is on the content, selects the highlighted item.
**`Enter`**
Description: When focus is on trigger, opens the cascade select and focuses the first item. When focus is on content, selects the highlighted item.
**`ArrowDown`**
Description: When focus is on trigger, opens the cascade select. When focus is on content, moves focus to the next item in the current level.
**`ArrowUp`**
Description: When focus is on trigger, opens the cascade select and focuses the last item. When focus is on content, moves focus to the previous item in the current level.
**`ArrowRight`**
Description: When focus is on a branch item, expands the next level and moves focus into it.
**`ArrowLeft`**
Description: When focus is on a nested level, collapses it and moves focus back to the parent. When focus is at the root level, closes the cascade select.
**`Home`**
Description: Moves focus to the first item in the current level.
**`End`**
Description: Moves focus to the last item in the current level.
**`Esc`**
Description: Closes the cascade select and moves focus to trigger.
A checkbox allows users to make a binary choice, i.e. a choice between one of
two possible mutually exclusive options.
## Resources
[Latest version: v2.0.0-next.1](https://www.npmjs.com/package/@zag-js/checkbox)
[Logic Visualizer](https://zag-visualizer.vercel.app/checkbox)
[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/checkbox)
**Features**
- Tri-state checkbox (`indeterminate` state)
- Syncs with `disabled` state of fieldset
- Syncs with form `reset` events
- Can be toggled programmatically
## Installation
Install the checkbox package:
```bash
npm install @zag-js/checkbox @zag-js/react
# or
yarn add @zag-js/checkbox @zag-js/react
```
## Anatomy
Check the checkbox anatomy and part names.
> Each part includes a `data-part` attribute to help identify them in the DOM.
## Usage
Import the checkbox package:
```tsx
import * as checkbox from "@zag-js/checkbox"
```
The checkbox package exports two key functions:
- `machine` - State machine logic.
- `connect` - Maps machine state to JSX props and event handlers.
Then use the framework integration helpers:
```tsx
import * as checkbox from "@zag-js/checkbox"
import { useMachine, normalizeProps } from "@zag-js/react"
import { useId } from "react"
function Checkbox() {
const service = useMachine(checkbox.machine, { id: useId() })
const api = checkbox.connect(service, normalizeProps)
return (
)
}
```
### Setting the initial checked state
Set `defaultChecked` to `true` to start checked.
```tsx {2}
const service = useMachine(checkbox.machine, {
defaultChecked: true,
})
```
### Indeterminate checkboxes
Set `defaultChecked` or `checked` to `"indeterminate"` for a tri-state checkbox.
```tsx {2}
const service = useMachine(checkbox.machine, {
defaultChecked: "indeterminate",
})
```
### Controlled checkbox
Use `checked` and `onCheckedChange` to control state externally.
```tsx
import { useState } from "react"
export function ControlledCheckbox() {
const [checked, setChecked] = useState(false)
const service = useMachine(checkbox.machine, {
checked,
onCheckedChange(details) {
setChecked(details.checked)
},
})
return (
// ...
)
}
```
### Disabling the checkbox
Set `disabled` to `true` to prevent interaction.
```tsx {2}
const service = useMachine(checkbox.machine, {
disabled: true,
})
```
### Listening for changes
When the checked state changes, `onCheckedChange` is invoked.
```tsx {2-5}
const service = useMachine(checkbox.machine, {
onCheckedChange(details) {
// details => { checked: boolean | "indeterminate" }
console.log("checkbox is:", details.checked)
},
})
```
### Read-only checkbox
Set `readOnly` to keep the checkbox focusable but prevent toggling.
```tsx {2}
const service = useMachine(checkbox.machine, {
readOnly: true,
})
```
### Usage within forms
To use checkbox within forms, set `name` and render `api.getHiddenInputProps()`.
```tsx {2}
const service = useMachine(checkbox.machine, {
name: "fruits",
})
```
Next, render the hidden input and ensure the value changes get propagated to the
form correctly.
```tsx
```
### Customizing submitted value
Set `value` to customize the submitted form value when checked.
```tsx
const service = useMachine(checkbox.machine, {
name: "newsletter",
value: "subscribed",
})
```
### Associating with an external form
If the input belongs to a different form element, set `form`.
```tsx
const service = useMachine(checkbox.machine, {
name: "newsletter",
form: "settings-form",
})
```
## Styling guide
Each part includes a `data-part` attribute you can target in CSS.
### Checked state
When the checkbox input is checked, the `data-state` attribute is added to the
root, control and label parts.
```css
[data-part="root"][data-state="checked|unchecked|indeterminate"] {
/* styles for when checkbox is checked */
}
[data-part="control"][data-state="checked|unchecked|indeterminate"] {
/* styles for when checkbox is checked */
}
[data-part="label"][data-state="checked|unchecked|indeterminate"] {
/* styles for when checkbox is checked */
}
```
### Focused State
When the checkbox input is focused, the `data-focus` attribute is added to the
root, control and label parts.
```css
[data-part="root"][data-focus] {
/* styles for root focus state */
}
[data-part="control"][data-focus] {
/* styles for control focus state */
}
[data-part="label"][data-focus] {
/* styles for label focus state */
}
```
### Disabled State
When the checkbox is disabled, the `data-disabled` attribute is added to the
root, control and label parts.
```css
[data-part="root"][data-disabled] {
/* styles for root disabled state */
}
[data-part="control"][data-disabled] {
/* styles for control disabled state */
}
[data-part="label"][data-disabled] {
/* styles for label disabled state */
}
```
### Invalid State
When the checkbox is invalid, the `data-invalid` attribute is added to the root,
control and label parts.
```css
[data-part="root"][data-invalid] {
/* styles for root invalid state */
}
[data-part="control"][data-invalid] {
/* styles for control invalid state */
}
[data-part="label"][data-invalid] {
/* styles for label invalid state */
}
```
## Methods and Properties
### Machine Context
The checkbox machine exposes the following context properties:
**`ids`**
Type: `ElementIds | undefined`
Description: The ids of the elements in the checkbox. Useful for composition.
**`disabled`**
Type: `boolean | undefined`
Description: Whether the checkbox is disabled
**`invalid`**
Type: `boolean | undefined`
Description: Whether the checkbox is invalid
**`required`**
Type: `boolean | undefined`
Description: Whether the checkbox is required
**`checked`**
Type: `CheckedState | undefined`
Description: The controlled checked state of the checkbox
**`defaultChecked`**
Type: `CheckedState | undefined`
Description: The initial checked state of the checkbox when rendered.
Use when you don't need to control the checked state of the checkbox.
**`readOnly`**
Type: `boolean | undefined`
Description: Whether the checkbox is read-only
**`onCheckedChange`**
Type: `((details: CheckedChangeDetails) => void) | undefined`
Description: The callback invoked when the checked state changes.
**`name`**
Type: `string | undefined`
Description: The name of the input field in a checkbox.
Useful for form submission.
**`form`**
Type: `string | undefined`
Description: The id of the form that the checkbox belongs to.
**`value`**
Type: `string | undefined`
Description: The value of checkbox input. Useful for form submission.
**`dir`**
Type: `"ltr" | "rtl" | undefined`
Description: The document's text/writing direction.
**`id`**
Type: `string`
Description: The unique identifier of the machine.
**`getRootNode`**
Type: `(() => ShadowRoot | Document | Node) | undefined`
Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
### Machine API
The checkbox `api` exposes the following methods:
**`checked`**
Type: `boolean`
Description: Whether the checkbox is checked
**`disabled`**
Type: `boolean | undefined`
Description: Whether the checkbox is disabled
**`indeterminate`**
Type: `boolean`
Description: Whether the checkbox is indeterminate
**`focused`**
Type: `boolean | undefined`
Description: Whether the checkbox is focused
**`checkedState`**
Type: `CheckedState`
Description: The checked state of the checkbox
**`setChecked`**
Type: `(checked: CheckedState) => void`
Description: Function to set the checked state of the checkbox
**`toggleChecked`**
Type: `VoidFunction`
Description: Function to toggle the checked state of the checkbox
### Data Attributes
**`Root`**
**`data-checkbox-root`**:
**`data-active`**: Present when active or pressed
**`data-focus`**: Present when focused
**`data-focus-visible`**: Present when focused with keyboard
**`data-readonly`**: Present when read-only
**`data-hover`**: Present when hovered
**`data-disabled`**: Present when disabled
**`data-state`**: "indeterminate" | "checked" | "unchecked"
**`data-invalid`**: Present when invalid
**`data-required`**: Present when required
**`Label`**
**`data-checkbox-label`**:
**`data-active`**: Present when active or pressed
**`data-focus`**: Present when focused
**`data-focus-visible`**: Present when focused with keyboard
**`data-readonly`**: Present when read-only
**`data-hover`**: Present when hovered
**`data-disabled`**: Present when disabled
**`data-state`**: "indeterminate" | "checked" | "unchecked"
**`data-invalid`**: Present when invalid
**`data-required`**: Present when required
**`Control`**
**`data-checkbox-control`**:
**`data-active`**: Present when active or pressed
**`data-focus`**: Present when focused
**`data-focus-visible`**: Present when focused with keyboard
**`data-readonly`**: Present when read-only
**`data-hover`**: Present when hovered
**`data-disabled`**: Present when disabled
**`data-state`**: "indeterminate" | "checked" | "unchecked"
**`data-invalid`**: Present when invalid
**`data-required`**: Present when required
**`Indicator`**
**`data-checkbox-indicator`**:
**`data-active`**: Present when active or pressed
**`data-focus`**: Present when focused
**`data-focus-visible`**: Present when focused with keyboard
**`data-readonly`**: Present when read-only
**`data-hover`**: Present when hovered
**`data-disabled`**: Present when disabled
**`data-state`**: "indeterminate" | "checked" | "unchecked"
**`data-invalid`**: Present when invalid
**`data-required`**: Present when required
## Accessibility
### Keyboard Interactions
**`Space`**
Description: Toggle the checkbox
The clipboard machine lets users quickly copy content to the clipboard.
## Resources
[Latest version: v2.0.0-next.1](https://www.npmjs.com/package/@zag-js/clipboard)
[Logic Visualizer](https://zag-visualizer.vercel.app/clipboard)
[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/clipboard)
## Installation
Install the clipboard package:
```bash
npm install @zag-js/clipboard @zag-js/react
# or
yarn add @zag-js/clipboard @zag-js/react
```
## Anatomy
Check the clipboard anatomy and part names.
> Each part includes a `data-part` attribute to help identify them in the DOM.
## Usage
Import the clipboard package:
```tsx
import * as clipboard from "@zag-js/clipboard"
```
The clipboard package exports two key functions:
- `machine` - State machine logic.
- `connect` - Maps machine state to JSX props and event handlers.
> Pass a unique `id` to `useMachine` so generated element ids stay predictable.
Then use the framework integration helpers:
```tsx
import * as clipboard from "@zag-js/clipboard"
import { useMachine, normalizeProps } from "@zag-js/react"
import { ClipboardCheck, ClipboardCopyIcon } from "lucide-react"
import { useId } from "react"
function Clipboard() {
const service = useMachine(clipboard.machine, {
id: useId(),
value: "https://github.com/chakra-ui/zag",
})
const api = clipboard.connect(service, normalizeProps)
return (
)
}
```
### Setting the clipboard value
Set `value` to control what gets copied.
```tsx {2}
const service = useMachine(clipboard.machine, {
value: "Hello, world!",
})
```
### Setting an initial value
Use `defaultValue` for uncontrolled initial value.
```tsx
const service = useMachine(clipboard.machine, {
defaultValue: "Hello, world!",
})
```
### Listening for value changes
Use `onValueChange` to react when the clipboard value changes.
```tsx
const service = useMachine(clipboard.machine, {
onValueChange(details) {
console.log("Value changed to", details.value)
},
})
```
### Listening to copy events
When the value is copied, `onStatusChange` is fired.
```tsx {2}
const service = useMachine(clipboard.machine, {
onStatusChange: (details) => {
console.log("Copy status changed to", details.copied)
},
})
```
### Checking if the value is copied
Use `api.copied` to check if the value was copied.
```tsx {2}
const api = clipboard.connect(service)
if (api.copied) {
console.log("Value is copied to the clipboard")
}
```
### Changing the timeout
By default, the copied feedback resets after `3000ms`. Set `timeout` to change
this delay.
```tsx {2}
const service = useMachine(clipboard.machine, {
timeout: 5000,
})
```
## Styling guide
Each part includes a `data-part` attribute you can target in CSS.
```css
[data-scope="clipboard"][data-part="root"] {
/* styles for the root part */
}
```
## Methods and Properties
### Machine Context
The clipboard machine exposes the following context properties:
**`translations`**
Type: `IntlTranslations | undefined`
Description: Specifies the localized strings that identifies the accessibility elements and their states
**`ids`**
Type: `ElementIds | undefined`
Description: The ids of the elements in the clipboard. Useful for composition.
**`value`**
Type: `string | undefined`
Description: The controlled value of the clipboard
**`defaultValue`**
Type: `string | undefined`
Description: The initial value to be copied to the clipboard when rendered.
Use when you don't need to control the value of the clipboard.
**`onValueChange`**
Type: `((details: ValueChangeDetails) => void) | undefined`
Description: The function to be called when the value changes
**`onStatusChange`**
Type: `((details: CopyStatusDetails) => void) | undefined`
Description: The function to be called when the value is copied to the clipboard
**`timeout`**
Type: `number | undefined`
Description: The timeout for the copy operation
**`id`**
Type: `string`
Description: The unique identifier of the machine.
**`getRootNode`**
Type: `(() => ShadowRoot | Document | Node) | undefined`
Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
### Machine API
The clipboard `api` exposes the following methods:
**`copied`**
Type: `boolean`
Description: Whether the value has been copied to the clipboard
**`value`**
Type: `string`
Description: The value to be copied to the clipboard
**`setValue`**
Type: `(value: string) => void`
Description: Set the value to be copied to the clipboard
**`copy`**
Type: `VoidFunction`
Description: Copy the value to the clipboard
### Data Attributes
**`Root`**
**`data-clipboard-root`**:
**`data-copied`**: Present when copied state is true
**`Label`**
**`data-clipboard-label`**:
**`data-copied`**: Present when copied state is true
**`Control`**
**`data-clipboard-control`**:
**`data-copied`**: Present when copied state is true
**`Input`**
**`data-clipboard-input`**:
**`data-copied`**: Present when copied state is true
**`data-readonly`**: Present when read-only
**`Trigger`**
**`data-clipboard-trigger`**:
**`data-copied`**: Present when copied state is true
A collapsible is a component which expands and collapses a panel.
## Resources
[Latest version: v2.0.0-next.1](https://www.npmjs.com/package/@zag-js/collapsible)
[Logic Visualizer](https://zag-visualizer.vercel.app/collapsible)
[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/collapsible)
**Features**
- Can be controlled or uncontrolled
- Works for width and height collapsibles
## Installation
Install the collapsible package:
```bash
npm install @zag-js/collapsible @zag-js/react
# or
yarn add @zag-js/collapsible @zag-js/react
```
## Usage
Import the collapsible package:
```tsx
import * as collapsible from "@zag-js/collapsible"
```
The collapsible package exports two key functions:
- `machine` - State logic for the collapsible.
- `connect` - Maps state to JSX props and event handlers.
> Pass a unique `id` to `useMachine` so generated element ids stay predictable.
Then use the framework integration helpers:
```tsx
import * as collapsible from "@zag-js/collapsible"
import { normalizeProps, useMachine } from "@zag-js/react"
import { useId } from "react"
function Collapsible() {
const service = useMachine(collapsible.machine, { id: useId() })
const api = collapsible.connect(service, normalizeProps)
return (
Collape Content
)
}
```
### Setting the initial state
Set `defaultOpen` to control the initial open state.
```tsx
const service = useMachine(collapsible.machine, {
defaultOpen: true,
})
```
### Controlled collapsible
Use `open` and `onOpenChange` to control the open state externally.
```tsx
import { useState } from "react"
export function ControlledCollapsible() {
const [open, setOpen] = useState(false)
const service = useMachine(collapsible.machine, {
open,
onOpenChange(details) {
setOpen(details.open)
}
})
return (
// ...
)
}
```
### Listening for changes
When the collapsible state changes, the `onOpenChange` callback is invoked.
```tsx {2-5}
const service = useMachine(collapsible.machine, {
onOpenChange(details) {
// details => { open: boolean }
console.log("collapsible open:", details.open)
},
})
```
### Disabling the collapsible
Set `disabled` to `true` to disable interaction.
```tsx {2}
const service = useMachine(collapsible.machine, {
disabled: true,
})
```
### Partial collapse (setting minimum dimensions)
Use `collapsedHeight` or `collapsedWidth` to create a "partially collapsed"
state. When collapsed, the content keeps the specified minimum size instead of
collapsing to `0px`.
```tsx {3}
const service = useMachine(collapsible.machine, {
// Content shows 100px height when collapsed
collapsedHeight: "100px",
})
```
This is useful for creating "show more/less" content sections or preview states
where a portion of the content shows even when collapsed.
### Running code after close animation
Use `onExitComplete` to run code when the close animation finishes.
```tsx
const service = useMachine(collapsible.machine, {
onExitComplete() {
console.log("Collapsible is fully closed")
},
})
```
### Animating the collapsible
Use CSS animations to animate the collapsible when it expands and collapses. The
`--height` and `--width` custom properties are attached to the content part.
```css
@keyframes expand {
from {
height: var(--collapsed-height, 0);
}
to {
height: var(--height);
}
}
@keyframes collapse {
from {
height: var(--height);
}
to {
height: var(--collapsed-height, 0);
}
}
[data-scope="collapsible"][data-part="content"] {
overflow: hidden;
max-width: 400px;
}
[data-scope="collapsible"][data-part="content"][data-state="open"] {
animation: expand 110ms cubic-bezier(0, 0, 0.38, 0.9);
}
[data-scope="collapsible"][data-part="content"][data-state="closed"] {
animation: collapse 110ms cubic-bezier(0, 0, 0.38, 0.9);
}
```
## Styling guide
Each part includes a `data-part` attribute you can target in CSS.
### Open and closed state
When a collapsible opens or closes, `data-state` is set to `open` or `closed` on
the root, trigger, and content.
```css
[data-part="root"][data-state="open|closed"] {
/* styles for the collapsible is open or closed state */
}
[data-part="trigger"][data-state="open|closed"] {
/* styles for the collapsible is open or closed state */
}
[data-part="content"][data-state="open|closed"] {
/* styles for the collapsible is open or closed state */
}
```
### Focused state
When a collapsible's trigger is focused, a `data-focus` attribute is set on the
root, trigger and content.
```css
[data-part="root"][data-focus] {
/* styles for the item's focus state */
}
[data-part="trigger"][data-focus] {
/* styles for the content's focus state */
}
[data-part="content"][data-focus] {
/* styles for the content's focus state */
}
```
### Collapse animation
The collapsible content provides `--width`, `--height`, `--collapsed-width`, and
`--collapsed-height` CSS variables that can be used to create smooth animations.
These variables are automatically calculated and updated based on the content's
dimensions.
```css
[data-scope="collapsible"][data-part="content"][data-state="open"] {
animation: slideDown 200ms ease;
}
[data-scope="collapsible"][data-part="content"][data-state="closed"] {
animation: slideUp 200ms ease;
}
@keyframes slideDown {
from {
opacity: 0.01;
height: 0;
}
to {
opacity: 1;
height: var(--height);
}
}
@keyframes slideUp {
from {
opacity: 1;
height: var(--height);
}
to {
opacity: 0.01;
height: 0;
}
}
```
## Methods and Properties
The collapsible's `api` exposes the following methods and properties:
### Machine Context
The collapsible machine exposes the following context properties:
**`ids`**
Type: `ElementIds | undefined`
Description: The ids of the elements in the collapsible. Useful for composition.
**`open`**
Type: `boolean | undefined`
Description: The controlled open state of the collapsible.
**`defaultOpen`**
Type: `boolean | undefined`
Description: The initial open state of the collapsible when rendered.
Use when you don't need to control the open state of the collapsible.
**`onOpenChange`**
Type: `((details: OpenChangeDetails) => void) | undefined`
Description: The callback invoked when the open state changes.
**`onExitComplete`**
Type: `VoidFunction | undefined`
Description: The callback invoked when the exit animation completes.
**`disabled`**
Type: `boolean | undefined`
Description: Whether the collapsible is disabled.
**`collapsedHeight`**
Type: `number | string | undefined`
Description: The height of the content when collapsed.
**`collapsedWidth`**
Type: `number | string | undefined`
Description: The width of the content when collapsed.
**`id`**
Type: `string`
Description: The unique identifier of the machine.
**`getRootNode`**
Type: `(() => ShadowRoot | Document | Node) | undefined`
Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
**`dir`**
Type: `"ltr" | "rtl" | undefined`
Description: The document's text/writing direction.
### Machine API
The collapsible `api` exposes the following methods:
**`open`**
Type: `boolean`
Description: Whether the collapsible is open.
**`visible`**
Type: `boolean`
Description: Whether the collapsible is visible (open or closing)
**`disabled`**
Type: `boolean`
Description: Whether the collapsible is disabled
**`setOpen`**
Type: `(open: boolean) => void`
Description: Function to open or close the collapsible.
**`measureSize`**
Type: `VoidFunction`
Description: Function to measure the size of the content.
### Data Attributes
**`Root`**
**`data-collapsible-root`**:
**`data-state`**: "open" | "closed"
**`Content`**
**`data-collapsible-content`**:
**`data-collapsible`**:
**`data-state`**: "open" | "closed"
**`data-disabled`**: Present when disabled
**`data-has-collapsed-size`**: Present when the content has collapsed width or height
**`Trigger`**
**`data-collapsible-trigger`**:
**`data-state`**: "open" | "closed"
**`data-disabled`**: Present when disabled
**`Indicator`**
**`data-collapsible-indicator`**:
**`data-state`**: "open" | "closed"
**`data-disabled`**: Present when disabled
### CSS Variables
## Accessibility
Adheres to the
[Disclosure WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure).
### Keyboard Interactions
**`Space`**
Description: Opens/closes the collapsible.
**`Enter`**
Description: Opens/closes the collapsible.
The color picker is an input widget used to select a color value from a
predefined list or a color area.
This component builds on top of the native `` experience and
provides a more customizable and consistent user experience.
## Resources
[Latest version: v2.0.0-next.1](https://www.npmjs.com/package/@zag-js/color-picker)
[Logic Visualizer](https://zag-visualizer.vercel.app/color-picker)
[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/color-picker)
**Features**
- Supports custom color area
- Supports RGBA, HSLA, HEX, HSBA, **OKLab**, and **OKLCH** formats
- Optional **sRGB gamut overlay** on the 2D area when using OKLab/OKLCH (`getGamutOverlay`, `getGamutOverlayProps`, anatomy part `gamutOverlay`)
- Supports channel inputs and sliders
- Supports mouse, touch, and keyboard interactions
- Supports form submission and reset events
- Supports named CSS colors
## Installation
Install the color picker package:
```bash
npm install @zag-js/color-picker @zag-js/react
# or
yarn add @zag-js/color-picker @zag-js/react
```
## Anatomy
To set up the color picker 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
Import the color picker package:
```tsx
import * as colorPicker from "@zag-js/color-picker"
```
These are the key exports:
- `machine` - Behavior logic for the color picker.
- `connect` - Maps state to JSX props and event handlers.
- `parse` - Parses a color string into a `Color` object.
Then use the framework integration helpers:
```tsx
import * as colorPicker from "@zag-js/color-picker"
import { normalizeProps, useMachine } from "@zag-js/react"
import { useId } from "react"
function ColorPicker() {
const service = useMachine(colorPicker.machine, {
id: useId(),
defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"),
})
const api = colorPicker.connect(service, normalizeProps)
return (
)
}
```
### Setting the initial color
Set `defaultValue` to define the initial color.
```tsx
const [current, send] = useMachine(colorPicker.machine, {
defaultValue: colorPicker.parse("#ff0000"),
})
```
### Controlled color picker
Use `value` and `onValueChange` to control color externally.
> **Note:** We recommend preserving the value as a `Color` object rather than a
> string to prevent calculation errors by converting back and forth.
```tsx
import { useState } from "react"
import * as colorPicker from "@zag-js/color-picker"
export function ControlledColorPicker() {
const [value, setValue] = useState(colorPicker.parse("#ff0000"))
const service = useMachine(colorPicker.machine, {
value,
onValueChange(details) {
setValue(details.value)
}
})
return (
// ...
)
}
```
### Listening for change events
When the user selects a color using the color picker, the `onValueChange` and
`onValueChangeEnd` events will be fired.
- `onValueChange` — Fires in sync as the user selects a color
- `onValueChangeEnd` — Fires when the user stops selecting a color (useful for
debounced updates)
```tsx
const [current, send] = useMachine(colorPicker.machine, {
onValueChange: (details) => {
// details => { value: Color, valueAsString: string }
},
onValueChangeEnd: (details) => {
// details => { value: Color, valueAsString: string }
},
})
```
### Using a custom color format
By default, the color picker's output format is `rgba`. You can change this
format to either `hsla` or `hsba` with `format`.
When this is set, the `value` and `valueAsString` properties of the
`onValueChange` event will be updated to reflect the new format.
```tsx
const [current, send] = useMachine(colorPicker.machine, {
format: "hsla",
onValueChange: (details) => {
// details => { value: HSLAColor, valueAsString: string }
},
})
```
### Wide gamut (OKLab / OKLCH)
The **2D color area** stays **HSBA** (saturation × brightness, **hue** on the third-axis slider) for a familiar control surface, even when the **output format** is OKLab or OKLCH.
Use **`api.getGamutOverlay(props?)`** for SVG path data (`path`, `labelPosition`) that draws the **sRGB gamut boundary** in that HSB view. The boundary is computed like Chrome DevTools (HSV interpreted in a wide working space vs sRGB). Pass the **same** `props` to **`getAreaProps`**, **`getGamutOverlay`**, and **`getGamutOverlayProps`**—including optional `xChannel` / `yChannel` overrides and optional **`pixelRatio`** (defaults to `globalThis.devicePixelRatio` in the browser for sharper curves on retina).
**`api.isInSrgbGamut`** is `true` when the current color lies inside the sRGB cube in linear RGB space. When `false`, your OKLab/OKLCH **string** may still be wide-gamut while **hex** or 8-bit **RGBA** outputs **clamp**—use this flag for badges, tooltips, or warnings.
### Setting the initial format
Use `defaultFormat` to set the initial output format.
```tsx
const [current, send] = useMachine(colorPicker.machine, {
defaultFormat: "hsla",
})
```
### Controlled format
Use `format` and `onFormatChange` to control the active format.
```tsx
const [current, send] = useMachine(colorPicker.machine, {
format,
onFormatChange(details) {
setFormat(details.format)
},
})
```
### Showing color presets
Adding color presets as swatches can help users pick colors faster. To support
this, use the `getSwatchTriggerProps(...)` and `getSwatchProps(...)` to get the
props needed to render swatch buttons.
```tsx {18-31}
const ColorPicker = () => {
const service = useMachine(colorPicker.machine, {
id: useId(),
defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"),
})
const api = colorPicker.connect(service, normalizeProps)
const presets = ["#ff0000", "#00ff00", "#0000ff"]
return (
{/* ... */}
{presets.map((preset) => (
))}
)
}
```
### Closing after swatch selection
Set `closeOnSelect` to close the popup when a swatch is selected.
```tsx
const [current, send] = useMachine(colorPicker.machine, {
closeOnSelect: true,
})
```
### Disabling the color picker
Set `disabled` to `true` to disable user interaction.
```tsx
const [current, send] = useMachine(colorPicker.machine, {
disabled: true,
})
```
### Controlling the open and closed state
Use `open` and `onOpenChange` to control whether the picker is visible.
```tsx
const [current, send] = useMachine(colorPicker.machine, {
open: true,
onOpenChange: (details) => {
// details => { open: boolean, value: Color }
},
})
```
You can also leverage the `api.setOpen(...)` method to control the open and
closed state of the color picker.
### Positioning the popup
Use `positioning` to control popup placement and collision behavior.
```tsx
const [current, send] = useMachine(colorPicker.machine, {
positioning: { placement: "bottom-start", gutter: 8 },
})
```
### Setting initial focus when opened
Use `initialFocusEl` to choose which element receives focus when the popup
opens.
```tsx
const [current, send] = useMachine(colorPicker.machine, {
initialFocusEl: () => document.getElementById("alpha-input"),
})
```
### Inline rendering
Set `inline` to render the picker without a trigger and popup.
```tsx
const [current, send] = useMachine(colorPicker.machine, {
inline: true,
})
```
### Controlling individual color channel
In some cases, you may want to allow users to control the values of each color
channel individually. You can do this using an input element or a slider
element, or both.
To support this, use the `getChannelInputProps(...)` to show the channel inputs.
> Note: Make sure you only render the channel inputs that match the `format` of
> the color picker.
```tsx {16-38}
const ColorPicker = () => {
const service = useMachine(colorPicker.machine, {
id: useId(),
defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"),
})
const api = colorPicker.connect(service, normalizeProps)
return (
{/* ... */}
{api.format === "rgba" && (
R
G
B
A
)}
)
}
```
### Showing a color preview
To display the value of a color, use the `getSwatchProps(...)` and pass the
color value. To show the current color value, use `api.value`.
```tsx {13-16}
const ColorPicker = () => {
const service = useMachine(colorPicker.machine, {
id: useId(),
defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"),
})
const api = colorPicker.connect(service, normalizeProps)
return (
{/* ... */}
)
}
```
> You can pass `respectAlpha: false` to show the color value without the alpha
> channel
### Adding an eyedropper
The eye dropper tool is a native browser feature that lets users pick a color
from a current page's canvas. To support this, use the
`getEyeDropperTriggerProps(...)`.
> **Note:** The eye dropper tool only works in Chrome and Edge browsers
```tsx {16-18}
const ColorPicker = () => {
const service = useMachine(colorPicker.machine, {
id: useId(),
defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"),
})
const api = colorPicker.connect(service, normalizeProps)
return (
{/* ... */}
)
}
```
### Usage within forms
To use the color picker in a form, set `name` and render the hidden input.
```tsx {2}
const service = useMachine(colorPicker.machine, {
name: "color-preference",
})
```
## Styling guide
Each color picker part has a `data-part` attribute added to them to help you
identify and style them easily.
### Open and closed state
When the color picker is open or closed, the `data-state` attribute is added to
the trigger, content, control parts.
```css
[data-part="control"][data-state="open|closed"] {
/* styles for control open or state */
}
[data-part="trigger"][data-state="open|closed"] {
/* styles for control open or state */
}
[data-part="content"][data-state="open|closed"] {
/* styles for control open or state */
}
```
### Focused State
When the color picker is focused, the `data-focus` attribute is added to the
control and label parts.
```css
[data-part="control"][data-focus] {
/* styles for control focus state */
}
[data-part="label"][data-focus] {
/* styles for label focus state */
}
```
### Disabled State
When the color picker is disabled, the `data-disabled` attribute is added to the
label, control, trigger and option parts.
```css
[data-part="label"][data-disabled] {
/* styles for label disabled state */
}
[data-part="control"][data-disabled] {
/* styles for control disabled state */
}
[data-part="trigger"][data-disabled] {
/* styles for trigger disabled state */
}
[data-part="swatch-trigger"][data-disabled] {
/* styles for item disabled state */
}
```
### Swatch State
When a swatch's color value matches the color picker's value, the
`data-state=checked` attribute is added to the swatch part.
```css
[data-part="swatch-trigger"][data-state="checked|unchecked"] {
/* styles for swatch's checked state */
}
```
## Methods and Properties
### Machine Context
The color picker machine exposes the following context properties:
**`ids`**
Type: `ElementIds | undefined`
Description: The ids of the elements in the color picker. Useful for composition.
**`value`**
Type: `Color | undefined`
Description: The controlled color value of the color picker
**`defaultValue`**
Type: `Color | undefined`
Description: The initial color value when rendered.
Use when you don't need to control the color value of the color picker.
**`disabled`**
Type: `boolean | undefined`
Description: Whether the color picker is disabled
**`readOnly`**
Type: `boolean | undefined`
Description: Whether the color picker is read-only
**`required`**
Type: `boolean | undefined`
Description: Whether the color picker is required
**`invalid`**
Type: `boolean | undefined`
Description: Whether the color picker is invalid
**`onValueChange`**
Type: `((details: ValueChangeDetails) => void) | undefined`
Description: Handler that is called when the value changes, as the user drags.
**`onValueChangeEnd`**
Type: `((details: ValueChangeDetails) => void) | undefined`
Description: Handler that is called when the user stops dragging.
**`onOpenChange`**
Type: `((details: OpenChangeDetails) => void) | undefined`
Description: Handler that is called when the user opens or closes the color picker.
**`name`**
Type: `string | undefined`
Description: The name for the form input
**`positioning`**
Type: `PositioningOptions | undefined`
Description: The positioning options for the color picker
**`initialFocusEl`**
Type: `(() => HTMLElement | null) | undefined`
Description: The initial focus element when the color picker is opened.
**`open`**
Type: `boolean | undefined`
Description: The controlled open state of the color picker
**`defaultOpen`**
Type: `boolean | undefined`
Description: The initial open state of the color picker when rendered.
Use when you don't need to control the open state of the color picker.
**`format`**
Type: `ColorFormat | undefined`
Description: The controlled color format to use
**`defaultFormat`**
Type: `ColorFormat | undefined`
Description: The initial color format when rendered.
Use when you don't need to control the color format of the color picker.
**`onFormatChange`**
Type: `((details: FormatChangeDetails) => void) | undefined`
Description: Function called when the color format changes
**`closeOnSelect`**
Type: `boolean | undefined`
Description: Whether to close the color picker when a swatch is selected
**`openAutoFocus`**
Type: `boolean | undefined`
Description: Whether to auto focus the color picker when it is opened
**`inline`**
Type: `boolean | undefined`
Description: Whether to render the color picker inline
**`id`**
Type: `string`
Description: The unique identifier of the machine.
**`getRootNode`**
Type: `(() => ShadowRoot | Document | Node) | undefined`
Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
**`dir`**
Type: `"ltr" | "rtl" | undefined`
Description: The document's text/writing direction.
**`onPointerDownOutside`**
Type: `((event: PointerDownOutsideEvent) => void) | undefined`
Description: Function called when the pointer is pressed down outside the component
**`onFocusOutside`**
Type: `((event: FocusOutsideEvent) => void) | undefined`
Description: Function called when the focus is moved outside the component
**`onInteractOutside`**
Type: `((event: InteractOutsideEvent) => void) | undefined`
Description: Function called when an interaction happens outside the component
### Machine API
The color picker `api` exposes the following methods:
**`dragging`**
Type: `boolean`
Description: Whether the color picker is being dragged
**`open`**
Type: `boolean`
Description: Whether the color picker is open
**`inline`**
Type: `boolean`
Description: Whether the color picker is rendered inline
**`value`**
Type: `Color`
Description: The current color value (as a string)
**`valueAsString`**
Type: `string`
Description: The current color value (as a Color object)
**`isInSrgbGamut`**
Type: `boolean`
Description: Whether the current color lies inside the sRGB gamut (linear RGB cube).
When `false`, wide-gamut CSS strings may still be valid while 8-bit RGB / hex are clamped.
**`setValue`**
Type: `(value: string | Color) => void`
Description: Function to set the color value
**`getChannelValue`**
Type: `(channel: ColorChannel) => string`
Description: Function to set the color value
**`getChannelValueText`**
Type: `(channel: ColorChannel, locale: string) => string`
Description: Function to get the formatted and localized value of a specific channel
**`setChannelValue`**
Type: `(channel: ColorChannel, value: number) => void`
Description: Function to set the color value of a specific channel
**`format`**
Type: `ColorFormat`
Description: The current color format
**`setFormat`**
Type: `(format: ColorFormat) => void`
Description: Function to set the color format
**`alpha`**
Type: `number`
Description: The alpha value of the color
**`setAlpha`**
Type: `(value: number) => void`
Description: Function to set the color alpha
**`setOpen`**
Type: `(open: boolean) => void`
Description: Function to open or close the color picker
**`getGamutOverlay`**
Type: `(props?: GamutOverlayProps) => GamutOverlayData | null`
Description: SVG path + label position for the sRGB gamut boundary (oklch/oklab + HSB area only); `null` when not shown.
Use the **same** `props` as
**`getGamutOverlayProps`**
Type: `(props?: GamutOverlayProps) => T["svg"]`
Description: Props for an `