Zag is a framework agnostic toolkit for implementing complex, interactive, and
accessible UI components in your design system and web applications. Works for
React, Solid and Vue.
> Zag is part of the next evolution of Chakra UI, and one of the four arms of
> the future of Chakra UI.
> [**Watch the talk here**](https://www.youtube.com/watch?v=I5xEc9t-HZg)
## Motivation
In [Chakra UI React](https://chakra-ui.com/), we've experienced too many hiccups
and bugs in the past related to how we coordinate events, manage state, and side
effects. Most these bugs are associated with the orchestration within
`useEffect`, `useMemo`, `useCallback`, etc.
These issues were replicated in our [Chakra UI Vue](https://vue.chakra-ui.com/)
pursuit as well, and created a maintenance hell for us. We're grateful for this
experience because it made us take a step back to define how we would like to
build components in the future.
We believe that most widgets should function the same way regardless of the
framework they're built with. That's why we built Zag.
> Don't re-invent the wheel, **let the machines do the work 😊**
## Why Zag?
- **Powered by state machines 🌳**: Zag is built on top of the latest ideas in
Statecharts. We don't follow the SCXML specifications, but we've created an
API that we think will help us build more complex components fast.
- **Write once, use everywhere 🦄**: The component interactions are modelled in
a framework agnostic way. We provide adapters for JS frameworks so you can use
it in React, Solid, or Vue 3.
- **Focus on accessibility ♿️**: Zag is built with accessibility in mind. We
handle many details related to keyboard interactions, focus management, aria
roles and attributes.
- **Headless ✨**: The machine APIs are completely unstyled and gives you the
control to use any styling solution you prefer.
- **Incremental adoption ✨**: Adopt the machines as you need them. Each
component machine is an NPM package and can be installed individually so you
can use them incrementally.
## Learn
[Watch the course](https://egghead.io/courses/statechart-driven-ui-components-with-zag-js-53f85394)
on Egghead to learn how to build statechart-driven UI components with Zag.js.
This course will give you a deep dive into how Zag works and how you can use it
to build complex UI components.
## Fun Facts
**Zag** means to _take a sharp change in direction_. This clearly describes our
approach of using state machines to power the logic behind UI components.
### Teasers
- When you see someone using classic react, vue or solid to build an interactive
UI component that exists in Zag, tell them to **"zag it!"** ⚡️
- Anyone using Zag will be called a **"zagger"** 💥
- The feeling you get when you use Zag will be called **"zagadat!"** 🚀
- The Zag community will be called **"zag nation"** 🔥
## Community
### Discord
To get involved with the Zag community, ask questions, and chat with the
maintainers, join our Discord.
[Join our Discord](https://zagjs.com/discord)
### Twitter
To receive updates on new components, enhancements, blog posts, and tips, follow
our Twitter account.
[Follow us on Twitter](https://twitter.com/zag_js)
## Prior art
We strongly believe in open source and the power of open collaboration. In the
past, we've been inspired by other meaningful projects and amazing people who
have inspire(d) us to keep improving our ideas.
Some of the projects we've been inspired by include:
- Chakra UI - [https://chakra-ui.com/](https://chakra-ui.com/)
- Radix UI - [https://www.radix-ui.com/](https://www.radix-ui.com/)
- Material Web Components -
[https://github.com/material-components/material-components-web](https://github.com/material-components/material-components-web)
- React Aria -
[https://react-spectrum.adobe.com/react-aria](https://react-spectrum.adobe.com/react-aria)
- Goldman Sachs Design System - [https://design.gs.com/](https://design.gs.com/)
- Reakit - [https://reakit.io/](https://reakit.io/)
- Fast - [https://fast.design/](https://fast.design/)
## Additional Thanks
- [Guillermo](https://rauchg.com/2015/pure-ui) for writing a great article that
sparked the idea for Zag.
- [Open UI](https://open-ui.org/) for inspiring the pattern behind this library
- [XState](https://xstate.js.org/) for inspiring the base implementation of the
state machine
- [Vue.js](https://vuejs.org/) and [Lit](https://lit.dev/) for inspiring new
patterns in the machine (`computed` and `watch`)
- [David Khourshid](https://twitter.com/DavidKPiano) for talking about state
machines long enough to get me started on this project
Zag can be used within most JS frameworks like Vue, React, Svelte and Solid.
To get Zag running, you'll need to:
1. Install the machine for the component you're interested in. Let's say you
want to use the `tooltip` machine.
```bash
npm install @zag-js/tooltip
# or
yarn add @zag-js/tooltip
```
2. Install the adapter for the framework of your choice. At the moment, Zag is
available for React, Vue 3, Svelte and Solid.js. Let's say you use React.
```bash
npm install @zag-js/react
# or
yarn add @zag-js/react
```
> Congrats! You're ready to use tooltip machine in your project.
## Using the machine
Here's an example of the tooltip machine used in a React.js project.
```jsx
import * as tooltip from "@zag-js/tooltip"
import { useMachine, normalizeProps } from "@zag-js/react"
export function Tooltip() {
const service = useMachine(tooltip.machine, { id: "1" })
const api = tooltip.connect(service, normalizeProps)
return (
<>
{api.open && (
Tooltip
)}
>
)
}
```
### Usage with Vue 3 (JSX)
Zag works seamlessly with Vue's JSX approach. Here's how to use the same tooltip
logic in Vue:
```jsx
import * as tooltip from "@zag-js/tooltip"
import { normalizeProps, useMachine } from "@zag-js/vue"
import { computed, defineComponent, h, Fragment } from "vue"
export default defineComponent({
name: "Tooltip",
setup() {
const service = useMachine(tooltip.machine, { id: "1" })
const apiRef = computed(() => tooltip.connect(service, normalizeProps))
return () => {
const api = apiRef.current
return (
<>
{api.open && (
Tooltip
)}
>
)
}
},
})
```
There are some extra functions that need to be used in order to make it work:
- `normalizeProps` - Converts the props of the component into the format that is
compatible with Vue.
- `computed` - Ensures that the tooltip's `api` is always up to date with the
current state of the machine.
### Usage with Solid.js
We love Solid.js and we've added support for it. Here's how to use the same
tooltip logic in Solid:
```jsx
import * as tooltip from "@zag-js/tooltip"
import { normalizeProps, useMachine } from "@zag-js/solid"
import { createMemo, createUniqueId, Show } from "solid-js"
export function Tooltip() {
const service = useMachine(tooltip.machine, { id: createUniqueId() })
const api = createMemo(() => tooltip.connect(service, normalizeProps))
return (
Tooltip
)
}
```
There are some extra functions that need to be used in order to make it work:
- `normalizeProps` - Converts the props of the component into the format that is
compatible with Solid.
- `createMemo` - Ensures that the tooltip's `api` is always up to date with the
current state of the machine.
### Usage with Svelte
Here's how to use the same tooltip logic in Svelte:
```html
{#if api.open}
Tooltip
{/if}
```
There are some extra functions that need to be used in order to make it work:
- `normalizeProps` - Converts the props of the component into the format that is
compatible with Svelte.
- `$derived` - Ensures that the tooltip's `api` is always up to date with the
current state of the machine.
### About prop normalization
There are subtle difference between how JSX attributes are named across
frameworks like React, Solid, Vue and Svelte. Here are some examples:
**Keydown listener**
- React and Solid: The keydown listener property is `onKeyDown`.
- Vue: The keydown listener property is `onKeydown`.
**Styles**
- React: Pass a numeric value for margin attributes like `{ marginBottom: 4 }`.
- Solid: It has to be `{ "margin-bottom": "4px" }`.
- Vue: You need to ensure the value is a string with unit.
`{ marginBottom: "4px" }`.
These little nuances between frameworks are handled automatically when you use
`normalizeProps`.
> The goal of Zag is to help you abstract the interaction and accessibility
> patterns into a statechart so you never have to re-invent the wheel.
Thanks for reading! If you're curious about how state machines work, the next
page will give you a quick overview.
A state machine is a tool for modeling stateful, reactive systems. It is useful
for declaratively describing the behavior of an application or component.
To model any logic as using the state machine pattern, it must have:
- A finite number of states.
- A finite number of transitions between states.
## Creating a state machine
Consider a simple toggle or switch component that consists of two states,
`active` and `inactive`. The initial state will be `active`
The supported transitions that can happen here:
- In the `active` state, when we click the toggle, it should transition to the
`inactive` state
- In the `inactive` state, when we click the toggle, it should transition to the
`active` state
Here's how we'll model the logic in code:
```jsx
import { createMachine } from "@zag-js/core"
const machine = createMachine({
// initial state
initialState() {
return "active"
},
// the finite states
states: {
active: {
on: {
CLICK: {
// go to inactive
target: "inactive",
},
},
},
inactive: {
on: {
CLICK: {
// go to active
target: "active",
},
},
},
},
})
```
### TypeScript Guide
For TypeScript projects, you can add type definitions to make your machine
type-safe:
```tsx
import { createMachine, type Service } from "@zag-js/core"
import type { NormalizeProps, PropTypes } from "@zag-js/types"
interface ToggleSchema {
state: "active" | "inactive"
event: { type: "CLICK" }
}
export const machine = createMachine({
// ... same as above
})
```
## Writing the connect function
Now that we've modelled the component logic, let's map that to DOM attributes
and event handlers following the
[WAI-ARIA](https://www.w3.org/TR/wai-aria-practices-1.1/examples/checkbox/checkbox-1/checkbox-1.html)
specification for the switch component.
We'll write a function called `connect` to do this.
```jsx
function connect(service, normalize) {
const { state, send } = service
const active = state.matches("active")
return {
active,
getButtonProps() {
return normalize.button({
type: "button",
role: "switch",
"aria-checked": active,
onClick() {
send({ type: "CLICK" })
},
})
},
}
}
```
### TypeScript Guide
For TypeScript projects, you can add type definitions to make your connect
function type-safe:
```tsx
import { type Service } from "@zag-js/core"
import type { NormalizeProps, PropTypes } from "@zag-js/types"
interface ToggleSchema {
state: "active" | "inactive"
event: { type: "CLICK" }
}
export function connect(
service: Service,
normalize: NormalizeProps,
) {
const { state, send } = service
const active = state.matches("active")
return {
active,
getButtonProps() {
return normalize.button({
type: "button",
role: "switch",
"aria-checked": active,
onClick() {
send({ type: "CLICK" })
},
})
},
}
}
```
## Consuming the state machine
Here's how to consume the toggle machine logic and connect in React.js.
```jsx
import { useMachine, normalizeProps } from "@zag-js/react"
import { machine, connect } from "./toggle"
function Toggle() {
const service = useMachine(machine)
const api = connect(service, normalizeProps)
return (
)
}
```
That's it! Now you've learned the fundamentals of a component state machine.
## Next Steps
Now that you understand the basics, learn about the advanced concepts that power Zag machines:
- [Building Machines Guide](/guides/building-machines) - Deep dive into context (bindable), watch/track, computed, refs, and more
## Why the need for `normalizeProps`?
The goal of `normalizeProps` is to convert the props of the component into the
format that is compatible with the respective framework.
It is also used to ensure that the returned properties are strongly typed.
There are subtle differences between how element attributes are named across
frameworks like React, Solid and Vue. Here are some examples:
**Keydown listener**
- React and Solid: The keydown listener property is `onKeyDown`.
- Vue: The keydown listener property is `onKeydown`.
**Styles**
- React: Pass a numeric value for margin attributes like `{ marginBottom: 4 }`.
- Solid: It has to be `{ "margin-bottom": "4px" }`.
- Vue: You need to ensure the value is a string with unit.
`{ marginBottom: "4px" }`.
These little nuances between frameworks are handled automatically when you use
`normalizeProps`.
## How can I attach custom extra event handlers to the elements?
See the approach [here](/guides/composition#event-composition).
## How can I get Zag working in a custom window environment?
See the approach [here](/guides/composition#custom-window-environment).
## What would it take to support other frameworks?
We're currently interested in supporting as many frameworks as possible. The key
requirements are:
- **Support for "spread props"**: The framework should have support for
spreading attributes and event handlers.
- **Exposed Typings**: The framework should expose the typings for the
attributes and event handlers. This is optional but would provide the best DX.
## How do I upgrade all zag dependencies?
Since we use independent versioning for each zag package, it can sometimes be
inconvenient to upgrade all dependencies individually.
You can use scoped upgrades feature in your package manager to update all zag
packages seamlessly.
```bash
pnpm up "@zag-js/*"
# or
yarn upgrade --scope @zag-js
# or
npm up @zag-js/...
```
## What is LLMs.txt?
We support [LLMs.txt](https://llmstxt.org/) files for making the Zag JS
documentation available to large language models (LLMs). This feature helps AI
tools better understand our component library, its APIs, and usage patterns.
## Available Routes
We provide several LLMs.txt routes to help AI tools access our documentation:
- - Contains a structured overview of all
components and their documentation links
- - Provides comprehensive documentation
including implementation details and examples
- - React-specific documentation and
implementation details
- - SolidJS-specific documentation and
implementation details
- - Vue-specific documentation and
implementation details
- - Svelte-specific documentation and
implementation details
## Usage with AI Tools
### Cursor
Use the `@Docs` feature in Cursor to include the LLMs.txt files in your project.
This helps Cursor provide more accurate code suggestions and documentation for
Zag JS components.
[Read more about @Docs in Cursor](https://docs.cursor.com/context/@-symbols/@-docs)
### Windstatic
Reference the LLMs.txt files using `@` or in your `.windsurfrules` files to
enhance Windstatic's understanding of Zag JS components.
[Read more about Windstatic Memories](https://docs.codeium.com/windsurf/memories#memories-and-rules)
### Other AI Tools
Any AI tool that supports LLMs.txt can use these routes to better understand Zag
JS. Simply point your tool to any of the routes above based on your framework of
choice.
## React
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: v1.35.2](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:
```jsx
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:
```jsx
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.
```jsx {2}
const service = useMachine(accordion.machine, {
multiple: true,
})
```
### Setting the initial value
Set `defaultValue` to define expanded items on first render.
```jsx
// 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`.
```jsx {2}
const service = useMachine(accordion.machine, {
collapsible: true,
})
```
### Listening for value changes
When the accordion value changes, the `onValueChange` callback is invoked.
```jsx {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.
```jsx
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.
```jsx {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.
```jsx
//...
Content
//...
```
You can also disable the entire accordion by setting `disabled` on the machine.
```jsx {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: `Partial<{ root: string; item: (value: string) => string; itemContent: (value: string) => string; itemTrigger: (value: string) => string; }>`
Description: The ids of the elements in the accordion. Useful for composition.
**`multiple`**
Type: `boolean`
Description: Whether multiple accordion items can be expanded at the same time.
**`collapsible`**
Type: `boolean`
Description: Whether an accordion item can be closed after it has been expanded.
**`value`**
Type: `string[]`
Description: The controlled value of the expanded accordion items.
**`defaultValue`**
Type: `string[]`
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`
Description: Whether the accordion items are disabled
**`onValueChange`**
Type: `(details: ValueChangeDetails) => void`
Description: The callback fired when the state of expanded/collapsed accordion items changes.
**`onFocusChange`**
Type: `(details: FocusChangeDetails) => void`
Description: The callback fired when the focused accordion item changes.
**`orientation`**
Type: `"horizontal" | "vertical"`
Description: The orientation of the accordion items.
**`dir`**
Type: `"ltr" | "rtl"`
Description: The document's text/writing direction.
**`id`**
Type: `string`
Description: The unique identifier of the machine.
**`getRootNode`**
Type: `() => ShadowRoot | Node | Document`
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`
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.
### Data Attributes
**`Root`**
**`data-scope`**: accordion
**`data-part`**: root
**`data-orientation`**: The orientation of the accordion
**`Item`**
**`data-scope`**: accordion
**`data-part`**: item
**`data-state`**: "open" | "closed"
**`data-focus`**: Present when focused
**`data-disabled`**: Present when disabled
**`data-orientation`**: The orientation of the item
**`ItemContent`**
**`data-scope`**: accordion
**`data-part`**: 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-scope`**: accordion
**`data-part`**: item-indicator
**`data-state`**: "open" | "closed"
**`data-disabled`**: Present when disabled
**`data-focus`**: Present when focused
**`data-orientation`**: The orientation of the item
**`ItemTrigger`**
**`data-scope`**: accordion
**`data-part`**: item-trigger
**`data-controls`**:
**`data-orientation`**: The orientation of the item
**`data-state`**: "open" | "closed"
## 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: v1.35.2](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:
```jsx
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:
```jsx
import * as angleSlider from "@zag-js/angle-slider"
import { normalizeProps, useMachine } from "@zag-js/react"
export function AngleSlider() {
const service = useMachine(angleSlider.machine, { id: "1" })
const api = angleSlider.connect(service, normalizeProps)
return (
)
}
```
### Setting the initial value
Set `defaultValue` to define the initial slider value.
```jsx {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:
```jsx {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.
```jsx {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.
```jsx {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()`.
```jsx {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.
```jsx {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.
```jsx {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: `Partial<{ root: string; thumb: string; hiddenInput: string; control: string; valueText: string; label: string; }>`
Description: The ids of the elements in the machine.
Useful for composition.
**`step`**
Type: `number`
Description: The step value for the slider.
**`value`**
Type: `number`
Description: The value of the slider.
**`defaultValue`**
Type: `number`
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`
Description: The callback function for when the value changes.
**`onValueChangeEnd`**
Type: `(details: ValueChangeDetails) => void`
Description: The callback function for when the value changes ends.
**`disabled`**
Type: `boolean`
Description: Whether the slider is disabled.
**`readOnly`**
Type: `boolean`
Description: Whether the slider is read-only.
**`invalid`**
Type: `boolean`
Description: Whether the slider is invalid.
**`name`**
Type: `string`
Description: The name of the slider. Useful for form submission.
**`aria-label`**
Type: `string`
Description: The accessible label for the slider thumb.
**`aria-labelledby`**
Type: `string`
Description: The id of the element that labels the slider thumb.
**`dir`**
Type: `"ltr" | "rtl"`
Description: The document's text/writing direction.
**`id`**
Type: `string`
Description: The unique identifier of the machine.
**`getRootNode`**
Type: `() => ShadowRoot | Node | Document`
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-scope`**: angle-slider
**`data-part`**: root
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-readonly`**: Present when read-only
**`Label`**
**`data-scope`**: angle-slider
**`data-part`**: label
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-readonly`**: Present when read-only
**`Control`**
**`data-scope`**: angle-slider
**`data-part`**: control
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-readonly`**: Present when read-only
**`Thumb`**
**`data-scope`**: angle-slider
**`data-part`**: thumb
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-readonly`**: Present when read-only
**`Marker`**
**`data-scope`**: angle-slider
**`data-part`**: 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: v1.35.2](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:
```jsx
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:
```jsx
import * as avatar from "@zag-js/avatar"
import { useMachine, normalizeProps } from "@zag-js/react"
function Avatar() {
const service = useMachine(avatar.machine, { id: "1" })
const api = avatar.connect(service, normalizeProps)
return (
PA
)
}
```
### Listening for loading status changes
When the image loads or fails, `onStatusChange` is invoked.
```jsx {2}
const service = useMachine(avatar.machine, {
onStatusChange(details) {
// details => { status: "error" | "loaded" }
},
})
```
### Updating the image source programmatically
Use `api.setSrc` when the image source changes after mount.
```jsx
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"
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`
Description: Functional called when the image loading status changes.
**`ids`**
Type: `Partial<{ root: string; image: string; fallback: string; }>`
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 | Node | Document`
Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
**`dir`**
Type: `"ltr" | "rtl"`
Description: The document's text/writing direction.
### Machine API
The avatar `api` exposes the following methods:
**`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-scope`**: avatar
**`data-part`**: image
**`data-state`**: "visible" | "hidden"
**`Fallback`**
**`data-scope`**: avatar
**`data-part`**: fallback
**`data-state`**: "hidden" | "visible"
A carousel component that leverages native CSS Scroll Snap for smooth,
performant scrolling between slides.
## Resources
[Latest version: v1.35.2](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:
```jsx
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 `slideCount` property in
> the machine context. This is the total number of slides.
```jsx
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",
slideCount: 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.
```jsx {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.
```jsx {2}
const service = useMachine(carousel.machine, {
defaultPage: 2,
})
```
### Controlling the current page
Use `page` and `onPageChange` for controlled navigation.
```jsx
const service = useMachine(carousel.machine, {
slideCount: 8,
page,
onPageChange(details) {
setPage(details.page)
},
})
```
### Setting slides per page
Set `slidesPerPage` to control how many slides are visible per page.
```jsx {2}
const service = useMachine(carousel.machine, {
slidesPerPage: 2,
})
```
### Setting slides per move
Set `slidesPerMove` to control how many slides advance on next/previous.
```jsx {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.
```jsx {2}
const service = useMachine(carousel.machine, {
loop: true,
})
```
### Setting the gap between slides
Set `spacing` to control the gap between slides.
```jsx {2}
const service = useMachine(carousel.machine, {
spacing: "16px",
})
```
### Setting viewport padding
Set `padding` to keep neighboring slides partially visible.
```jsx {2}
const service = useMachine(carousel.machine, {
padding: "16px",
})
```
### Variable-width slides
Set `autoSize` to `true` when slides have different widths.
```jsx {2}
const service = useMachine(carousel.machine, {
autoSize: true,
})
```
### Listening for page changes
When the carousel page changes, the `onPageChange` callback is invoked.
```jsx {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.
```jsx
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.
```jsx {2}
const service = useMachine(carousel.machine, {
allowMouseDrag: true,
})
```
### Autoplaying the carousel
Set `autoplay` to `true` to start automatic slide movement.
```jsx {2}
const service = useMachine(carousel.machine, {
autoplay: true,
})
```
Alternatively, you can configure the autoplay interval by setting the `delay`
property.
```jsx {2}
const service = useMachine(carousel.machine, {
autoplay: { delay: 2000 },
})
```
### Customizing accessibility messages
Use `translations` to customize localized trigger, item, and progress text.
```jsx
const service = useMachine(carousel.machine, {
slideCount: 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: `Partial<{ root: string; item: (index: number) => string; itemGroup: string; nextTrigger: string; prevTrigger: string; indicatorGroup: string; indicator: (index: number) => string; }>`
Description: The ids of the elements in the carousel. Useful for composition.
**`translations`**
Type: `IntlTranslations`
Description: The localized messages to use.
**`slidesPerPage`**
Type: `number`
Description: The number of slides to show at a time.
**`autoSize`**
Type: `boolean`
Description: Whether to enable variable width slides.
**`slidesPerMove`**
Type: `number | "auto"`
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; }`
Description: Whether to scroll automatically. The default delay is 4000ms.
**`allowMouseDrag`**
Type: `boolean`
Description: Whether to allow scrolling via dragging with mouse
**`loop`**
Type: `boolean`
Description: Whether the carousel should loop around.
**`page`**
Type: `number`
Description: The controlled page of the carousel.
**`defaultPage`**
Type: `number`
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`
Description: The amount of space between items.
**`padding`**
Type: `string`
Description: Defines the extra space added around the scrollable area,
enabling nearby items to remain partially in view.
**`onPageChange`**
Type: `(details: PageChangeDetails) => void`
Description: Function called when the page changes.
**`inViewThreshold`**
Type: `number | number[]`
Description: The threshold for determining if an item is in view.
**`snapType`**
Type: `"proximity" | "mandatory"`
Description: The snap type of the item.
**`slideCount`**
Type: `number`
Description: The total number of slides.
Useful for SSR to render the initial ating the snap points.
**`onDragStatusChange`**
Type: `(details: DragStatusDetails) => void`
Description: Function called when the drag status changes.
**`onAutoplayStatusChange`**
Type: `(details: AutoplayStatusDetails) => void`
Description: Function called when the autoplay status changes.
**`dir`**
Type: `"ltr" | "rtl"`
Description: The document's text/writing direction.
**`id`**
Type: `string`
Description: The unique identifier of the machine.
**`getRootNode`**
Type: `() => ShadowRoot | Node | Document`
Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
**`orientation`**
Type: `"horizontal" | "vertical"`
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-scope`**: carousel
**`data-part`**: root
**`data-orientation`**: The orientation of the carousel
**`ItemGroup`**
**`data-scope`**: carousel
**`data-part`**: item-group
**`data-orientation`**: The orientation of the item
**`data-dragging`**: Present when in the dragging state
**`Item`**
**`data-scope`**: carousel
**`data-part`**: item
**`data-index`**: The index of the item
**`data-inview`**: Present when in viewport
**`data-orientation`**: The orientation of the item
**`Control`**
**`data-scope`**: carousel
**`data-part`**: control
**`data-orientation`**: The orientation of the control
**`PrevTrigger`**
**`data-scope`**: carousel
**`data-part`**: prev-trigger
**`data-orientation`**: The orientation of the prevtrigger
**`NextTrigger`**
**`data-scope`**: carousel
**`data-part`**: next-trigger
**`data-orientation`**: The orientation of the nexttrigger
**`IndicatorGroup`**
**`data-scope`**: carousel
**`data-part`**: indicator-group
**`data-orientation`**: The orientation of the indicatorgroup
**`Indicator`**
**`data-scope`**: carousel
**`data-part`**: 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-scope`**: carousel
**`data-part`**: 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: v1.35.2](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:
```jsx
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.
```jsx {4}
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
defaultValue: [["north-america", "us", "ny"]],
})
```
### Controlled selection
Use `value` and `onValueChange` for controlled selection state.
```jsx
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`.
```jsx {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"`.
```jsx {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`.
```jsx {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.
```jsx {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.
```jsx {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.
```jsx {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.
```jsx
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
defaultHighlightedValue: ["north-america", "us"],
})
```
### Controlled highlighted path
Use `highlightedValue` and `onHighlightChange` to control highlighting
externally.
```jsx
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.
```jsx {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.
```jsx {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.
```jsx
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
open,
onOpenChange({ open }) {
setOpen(open)
},
})
```
```jsx
const service = useMachine(cascadeSelect.machine, {
id: useId(),
collection,
defaultOpen: true,
})
```
### Positioning submenu panels
Use `positioning` to configure placement and collision behavior.
```jsx
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.
```jsx
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.
```jsx
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()`.
```jsx {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`.
```jsx
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.
```jsx
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`
Description: The tree collection data
**`ids`**
Type: `Partial<{ root: string; label: string; control: string; trigger: string; indicator: string; clearTrigger: string; positioner: string; content: string; hiddenInput: string; list(valuePath: string): string; item(valuePath: string): string; }>`
Description: The ids of the cascade-select elements. Useful for composition.
**`name`**
Type: `string`
Description: The name attribute of the underlying input element
**`form`**
Type: `string`
Description: The form attribute of the underlying input element
**`value`**
Type: `string[][]`
Description: The controlled value of the cascade-select
**`defaultValue`**
Type: `string[][]`
Description: The initial value of the cascade-select when rendered.
Use when you don't need to control the value.
**`highlightedValue`**
Type: `string[]`
Description: The controlled highlighted value of the cascade-select
**`defaultHighlightedValue`**
Type: `string[]`
Description: The initial highlighted value of the cascade-select when rendered.
**`multiple`**
Type: `boolean`
Description: Whether to allow multiple selections
**`open`**
Type: `boolean`
Description: The controlled open state of the cascade-select
**`defaultOpen`**
Type: `boolean`
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"`
Description: What triggers highlighting of items
**`closeOnSelect`**
Type: `boolean`
Description: Whether the cascade-select should close when an item is selected
**`loopFocus`**
Type: `boolean`
Description: Whether the cascade-select should loop focus when navigating with keyboard
**`disabled`**
Type: `boolean`
Description: Whether the cascade-select is disabled
**`readOnly`**
Type: `boolean`
Description: Whether the cascade-select is read-only
**`required`**
Type: `boolean`
Description: Whether the cascade-select is required
**`invalid`**
Type: `boolean`
Description: Whether the cascade-select is invalid
**`positioning`**
Type: `PositioningOptions`
Description: The positioning options for the cascade-select content
**`scrollToIndexFn`**
Type: `(details: ScrollToIndexDetails) => void`
Description: Function to scroll to a specific index in a list
**`formatValue`**
Type: `(selectedItems: T[][]) => string`
Description: Function to format the display value
**`onValueChange`**
Type: `(details: ValueChangeDetails) => void`
Description: Called when the value changes
**`onHighlightChange`**
Type: `(details: HighlightChangeDetails) => void`
Description: Called when the highlighted value changes
**`onOpenChange`**
Type: `(details: OpenChangeDetails) => void`
Description: Called when the open state changes
**`allowParentSelection`**
Type: `boolean`
Description: Whether parent (branch) items can be selectable
**`dir`**
Type: `"ltr" | "rtl"`
Description: The document's text/writing direction.
**`id`**
Type: `string`
Description: The unique identifier of the machine.
**`getRootNode`**
Type: `() => ShadowRoot | Node | Document`
Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
**`onPointerDownOutside`**
Type: `(event: PointerDownOutsideEvent) => void`
Description: Function called when the pointer is pressed down outside the component
**`onFocusOutside`**
Type: `(event: FocusOutsideEvent) => void`
Description: Function called when the focus is moved outside the component
**`onInteractOutside`**
Type: `(event: InteractOutsideEvent) => void`
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?: Partial) => 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[]) => 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-scope`**: cascade-select
**`data-part`**: root
**`data-disabled`**: Present when disabled
**`data-readonly`**: Present when read-only
**`data-invalid`**: Present when invalid
**`data-state`**: "open" | "closed"
**`Label`**
**`data-scope`**: cascade-select
**`data-part`**: label
**`data-disabled`**: Present when disabled
**`data-readonly`**: Present when read-only
**`data-invalid`**: Present when invalid
**`Control`**
**`data-scope`**: cascade-select
**`data-part`**: 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-scope`**: cascade-select
**`data-part`**: 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-placeholder-shown`**: Present when placeholder is shown
**`ClearTrigger`**
**`data-scope`**: cascade-select
**`data-part`**: clear-trigger
**`data-disabled`**: Present when disabled
**`data-readonly`**: Present when read-only
**`data-invalid`**: Present when invalid
**`Content`**
**`data-scope`**: cascade-select
**`data-part`**: content
**`data-activedescendant`**: The id the active descendant of the content
**`data-state`**: "open" | "closed"
**`List`**
**`data-scope`**: cascade-select
**`data-part`**: list
**`data-depth`**: The depth of the item
**`Indicator`**
**`data-scope`**: cascade-select
**`data-part`**: indicator
**`data-state`**: "open" | "closed"
**`data-disabled`**: Present when disabled
**`data-readonly`**: Present when read-only
**`data-invalid`**: Present when invalid
**`Item`**
**`data-scope`**: cascade-select
**`data-part`**: 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-scope`**: cascade-select
**`data-part`**: 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-scope`**: cascade-select
**`data-part`**: 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-scope`**: cascade-select
**`data-part`**: 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: v1.35.2](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:
```jsx
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:
```jsx
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.
```jsx {2}
const service = useMachine(checkbox.machine, {
defaultChecked: true,
})
```
### Indeterminate checkboxes
Set `defaultChecked` or `checked` to `"indeterminate"` for a tri-state checkbox.
```jsx {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.
```jsx {2}
const service = useMachine(checkbox.machine, {
disabled: true,
})
```
### Listening for changes
When the checked state changes, `onCheckedChange` is invoked.
```jsx {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.
```jsx {2}
const service = useMachine(checkbox.machine, {
readOnly: true,
})
```
### Usage within forms
To use checkbox within forms, set `name` and render `api.getHiddenInputProps()`.
```jsx {2}
const service = useMachine(checkbox.machine, {
name: "fruits",
})
```
Next, render the hidden input and ensure the value changes get propagated to the
form correctly.
```jsx
```
### Customizing submitted value
Set `value` to customize the submitted form value when checked.
```jsx
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`.
```jsx
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: `Partial<{ root: string; hiddenInput: string; control: string; label: string; }>`
Description: The ids of the elements in the checkbox. Useful for composition.
**`disabled`**
Type: `boolean`
Description: Whether the checkbox is disabled
**`invalid`**
Type: `boolean`
Description: Whether the checkbox is invalid
**`required`**
Type: `boolean`
Description: Whether the checkbox is required
**`checked`**
Type: `CheckedState`
Description: The controlled checked state of the checkbox
**`defaultChecked`**
Type: `CheckedState`
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`
Description: Whether the checkbox is read-only
**`onCheckedChange`**
Type: `(details: CheckedChangeDetails) => void`
Description: The callback invoked when the checked state changes.
**`name`**
Type: `string`
Description: The name of the input field in a checkbox.
Useful for form submission.
**`form`**
Type: `string`
Description: The id of the form that the checkbox belongs to.
**`value`**
Type: `string`
Description: The value of checkbox input. Useful for form submission.
**`dir`**
Type: `"ltr" | "rtl"`
Description: The document's text/writing direction.
**`id`**
Type: `string`
Description: The unique identifier of the machine.
**`getRootNode`**
Type: `() => ShadowRoot | Node | Document`
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`
Description: Whether the checkbox is disabled
**`indeterminate`**
Type: `boolean`
Description: Whether the checkbox is indeterminate
**`focused`**
Type: `boolean`
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-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-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-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-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: v1.35.2](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:
```jsx
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.
```jsx {2}
const service = useMachine(clipboard.machine, {
value: "Hello, world!",
})
```
### Setting an initial value
Use `defaultValue` for uncontrolled initial value.
```jsx
const service = useMachine(clipboard.machine, {
defaultValue: "Hello, world!",
})
```
### Listening for value changes
Use `onValueChange` to react when the clipboard value changes.
```jsx
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.
```jsx {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.
```jsx {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.
```jsx {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:
**`ids`**
Type: `Partial<{ root: string; input: string; label: string; }>`
Description: The ids of the elements in the clipboard. Useful for composition.
**`value`**
Type: `string`
Description: The controlled value of the clipboard
**`defaultValue`**
Type: `string`
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`
Description: The function to be called when the value changes
**`onStatusChange`**
Type: `(details: CopyStatusDetails) => void`
Description: The function to be called when the value is copied to the clipboard
**`timeout`**
Type: `number`
Description: The timeout for the copy operation
**`id`**
Type: `string`
Description: The unique identifier of the machine.
**`getRootNode`**
Type: `() => ShadowRoot | Node | Document`
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-scope`**: clipboard
**`data-part`**: root
**`data-copied`**: Present when copied state is true
**`Label`**
**`data-scope`**: clipboard
**`data-part`**: label
**`data-copied`**: Present when copied state is true
**`Control`**
**`data-scope`**: clipboard
**`data-part`**: control
**`data-copied`**: Present when copied state is true
**`Input`**
**`data-scope`**: clipboard
**`data-part`**: input
**`data-copied`**: Present when copied state is true
**`data-readonly`**: Present when read-only
**`Trigger`**
**`data-scope`**: clipboard
**`data-part`**: trigger
**`data-copied`**: Present when copied state is true
A collapsible is a component which expands and collapses a panel.
## Resources
[Latest version: v1.35.2](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:
```jsx
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.
```jsx
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.
```jsx {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.
```jsx {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`.
```jsx {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.
```jsx
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: `Partial<{ root: string; content: string; trigger: string; }>`
Description: The ids of the elements in the collapsible. Useful for composition.
**`open`**
Type: `boolean`
Description: The controlled open state of the collapsible.
**`defaultOpen`**
Type: `boolean`
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`
Description: The callback invoked when the open state changes.
**`onExitComplete`**
Type: `VoidFunction`
Description: The callback invoked when the exit animation completes.
**`disabled`**
Type: `boolean`
Description: Whether the collapsible is disabled.
**`collapsedHeight`**
Type: `string | number`
Description: The height of the content when collapsed.
**`collapsedWidth`**
Type: `string | number`
Description: The width of the content when collapsed.
**`id`**
Type: `string`
Description: The unique identifier of the machine.
**`getRootNode`**
Type: `() => ShadowRoot | Node | Document`
Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
**`dir`**
Type: `"ltr" | "rtl"`
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-scope`**: collapsible
**`data-part`**: root
**`data-state`**: "open" | "closed"
**`Content`**
**`data-scope`**: collapsible
**`data-part`**: 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-scope`**: collapsible
**`data-part`**: trigger
**`data-state`**: "open" | "closed"
**`data-disabled`**: Present when disabled
**`Indicator`**
**`data-scope`**: collapsible
**`data-part`**: 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: v1.35.2](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, and HSBA formats
- 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:
```jsx
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:
```jsx
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.
```jsx
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)
```jsx
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.
```jsx
const [current, send] = useMachine(colorPicker.machine, {
format: "hsla",
onValueChange: (details) => {
// details => { value: HSLAColor, valueAsString: string }
},
})
```
### Setting the initial format
Use `defaultFormat` to set the initial output format.
```jsx
const [current, send] = useMachine(colorPicker.machine, {
defaultFormat: "hsla",
})
```
### Controlled format
Use `format` and `onFormatChange` to control the active format.
```jsx
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.
```jsx
const [current, send] = useMachine(colorPicker.machine, {
closeOnSelect: true,
})
```
### Disabling the color picker
Set `disabled` to `true` to disable user interaction.
```jsx
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.
```jsx
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.
```jsx
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.
```jsx
const [current, send] = useMachine(colorPicker.machine, {
initialFocusEl: () => document.getElementById("alpha-input"),
})
```
### Inline rendering
Set `inline` to render the picker without a trigger and popup.
```jsx
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.
```jsx {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: `Partial<{ root: string; control: string; trigger: string; label: string; input: string; hiddenInput: string; content: string; area: string; areaGradient: string; positioner: string; formatSelect: string; areaThumb: string; channelInput: (id: string) => string; channelSliderTrack: (id: ColorChannel) => string; channelSliderThumb: (id: ColorChannel) => string; }>`
Description: The ids of the elements in the color picker. Useful for composition.
**`value`**
Type: `Color`
Description: The controlled color value of the color picker
**`defaultValue`**
Type: `Color`
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`
Description: Whether the color picker is disabled
**`readOnly`**
Type: `boolean`
Description: Whether the color picker is read-only
**`required`**
Type: `boolean`
Description: Whether the color picker is required
**`invalid`**
Type: `boolean`
Description: Whether the color picker is invalid
**`onValueChange`**
Type: `(details: ValueChangeDetails) => void`
Description: Handler that is called when the value changes, as the user drags.
**`onValueChangeEnd`**
Type: `(details: ValueChangeDetails) => void`
Description: Handler that is called when the user stops dragging.
**`onOpenChange`**
Type: `(details: OpenChangeDetails) => void`
Description: Handler that is called when the user opens or closes the color picker.
**`name`**
Type: `string`
Description: The name for the form input
**`positioning`**
Type: `PositioningOptions`
Description: The positioning options for the color picker
**`initialFocusEl`**
Type: `() => HTMLElement`
Description: The initial focus element when the color picker is opened.
**`open`**
Type: `boolean`
Description: The controlled open state of the color picker
**`defaultOpen`**
Type: `boolean`
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`
Description: The controlled color format to use
**`defaultFormat`**
Type: `ColorFormat`
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`
Description: Function called when the color format changes
**`closeOnSelect`**
Type: `boolean`
Description: Whether to close the color picker when a swatch is selected
**`openAutoFocus`**
Type: `boolean`
Description: Whether to auto focus the color picker when it is opened
**`inline`**
Type: `boolean`
Description: Whether to render the color picker inline
**`id`**
Type: `string`
Description: The unique identifier of the machine.
**`getRootNode`**
Type: `() => Node | ShadowRoot | Document`
Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
**`dir`**
Type: `"ltr" | "rtl"`
Description: The document's text/writing direction.
**`onPointerDownOutside`**
Type: `(event: PointerDownOutsideEvent) => void`
Description: Function called when the pointer is pressed down outside the component
**`onFocusOutside`**
Type: `(event: FocusOutsideEvent) => void`
Description: Function called when the focus is moved outside the component
**`onInteractOutside`**
Type: `(event: InteractOutsideEvent) => void`
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)
**`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
### Data Attributes
**`Root`**
**`data-scope`**: color-picker
**`data-part`**: root
**`data-disabled`**: Present when disabled
**`data-readonly`**: Present when read-only
**`data-invalid`**: Present when invalid
**`Label`**
**`data-scope`**: color-picker
**`data-part`**: label
**`data-disabled`**: Present when disabled
**`data-readonly`**: Present when read-only
**`data-invalid`**: Present when invalid
**`data-required`**: Present when required
**`data-focus`**: Present when focused
**`Control`**
**`data-scope`**: color-picker
**`data-part`**: control
**`data-disabled`**: Present when disabled
**`data-readonly`**: Present when read-only
**`data-invalid`**: Present when invalid
**`data-state`**: "open" | "closed"
**`data-focus`**: Present when focused
**`Trigger`**
**`data-scope`**: color-picker
**`data-part`**: trigger
**`data-disabled`**: Present when disabled
**`data-readonly`**: Present when read-only
**`data-invalid`**: Present when invalid
**`data-placement`**: The placement of the trigger
**`data-state`**: "open" | "closed"
**`data-focus`**: Present when focused
**`Content`**
**`data-scope`**: color-picker
**`data-part`**: content
**`data-placement`**: The placement of the content
**`data-nested`**: popover
**`data-has-nested`**: popover
**`data-state`**: "open" | "closed"
**`ValueText`**
**`data-scope`**: color-picker
**`data-part`**: value-text
**`data-disabled`**: Present when disabled
**`data-focus`**: Present when focused
**`Area`**
**`data-scope`**: color-picker
**`data-part`**: area
**`data-invalid`**: Present when invalid
**`data-disabled`**: Present when disabled
**`data-readonly`**: Present when read-only
**`AreaBackground`**
**`data-scope`**: color-picker
**`data-part`**: area-background
**`data-invalid`**: Present when invalid
**`data-disabled`**: Present when disabled
**`data-readonly`**: Present when read-only
**`AreaThumb`**
**`data-scope`**: color-picker
**`data-part`**: area-thumb
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-readonly`**: Present when read-only
**`ChannelSlider`**
**`data-scope`**: color-picker
**`data-part`**: channel-slider
**`data-channel`**: The color channel of the channelslider
**`data-orientation`**: The orientation of the channelslider
**`ChannelSliderTrack`**
**`data-scope`**: color-picker
**`data-part`**: channel-slider-track
**`data-channel`**: The color channel of the channelslidertrack
**`data-orientation`**: The orientation of the channelslidertrack
**`ChannelSliderLabel`**
**`data-scope`**: color-picker
**`data-part`**: channel-slider-label
**`data-channel`**: The color channel of the channelsliderlabel
**`ChannelSliderValueText`**
**`data-scope`**: color-picker
**`data-part`**: channel-slider-value-text
**`data-channel`**: The color channel of the channelslidervaluetext
**`ChannelSliderThumb`**
**`data-scope`**: color-picker
**`data-part`**: channel-slider-thumb
**`data-channel`**: The color channel of the channelsliderthumb
**`data-disabled`**: Present when disabled
**`data-orientation`**: The orientation of the channelsliderthumb
**`ChannelInput`**
**`data-scope`**: color-picker
**`data-part`**: channel-input
**`data-channel`**: The color channel of the channelinput
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-readonly`**: Present when read-only
**`EyeDropperTrigger`**
**`data-scope`**: color-picker
**`data-part`**: eye-dropper-trigger
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-readonly`**: Present when read-only
**`SwatchTrigger`**
**`data-scope`**: color-picker
**`data-part`**: swatch-trigger
**`data-state`**: "checked" | "unchecked"
**`data-value`**: The value of the item
**`data-disabled`**: Present when disabled
**`Swatch`**
**`data-scope`**: color-picker
**`data-part`**: swatch
**`data-state`**: "checked" | "unchecked"
**`data-value`**: The value of the item
### CSS Variables
## Accessibility
### Keyboard Interactions
**`Enter`**
Description: When focus is on the trigger, opens the color picker When focus is on a trigger of a swatch, selects the color (and closes the color picker) When focus is on the input or channel inputs, selects the color
**`ArrowLeft`**
Description: When focus is on the color area, decreases the hue value of the color When focus is on the channel sliders, decreases the value of the channel
**`ArrowRight`**
Description: When focus is on the color area, increases the hue value of the color When focus is on the channel sliders, increases the value of the channel
**`ArrowUp`**
Description: When focus is on the color area, increases the saturation value of the color When focus is on the channel sliders, increases the value of the channel
**`ArrowDown`**
Description: When focus is on the color area, decreases the saturation value of the color When focus is on the channel sliders, decreases the value of the channel
**`Esc`**
Description: Closes the color picker and moves focus to the trigger
A combobox is an input with a popup that lets you select a value from a
collection.
## Resources
[Latest version: v1.35.2](https://www.npmjs.com/package/@zag-js/combobox)
[Logic Visualizer](https://zag-visualizer.vercel.app/combobox)
[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/combobox)
**Features**
- Supports selecting multiple values
- Supports disabled options
- Supports custom user input values
- Supports mouse, touch, and keyboard interactions
- Supports opening the combobox listbox with arrow keys, including automatically
focusing the first or last item accordingly
## Installation
Install the combobox package:
```bash
npm install @zag-js/combobox @zag-js/react
# or
yarn add @zag-js/combobox @zag-js/react
```
## Anatomy
Check the combobox anatomy and part names.
> Each part includes a `data-part` attribute to help identify them in the DOM.
## Usage
Import the combobox package:
```jsx
import * as combobox from "@zag-js/combobox"
```
These are the key exports:
- `machine` - Behavior logic.
- `connect` - Maps behavior to JSX props and event handlers.
- `collection` - Creates a [collection interface](/overview/collection) from an
array of items.
Then use the framework integration helpers:
```jsx
import * as combobox from "@zag-js/combobox"
import { useMachine, normalizeProps } from "@zag-js/react"
import { useState, useId } from "react"
const comboboxData = [
{ label: "Zambia", code: "ZA" },
{ label: "Benin", code: "BN" },
//...
]
export function Combobox() {
const [options, setOptions] = useState(comboboxData)
const collection = combobox.collection({
items: options,
itemToValue: (item) => item.code,
itemToString: (item) => item.label,
})
const service = useMachine(combobox.machine, {
id: useId(),
collection,
onOpenChange() {
setOptions(comboboxData)
},
onInputValueChange({ inputValue }) {
const filtered = comboboxData.filter((item) =>
item.label.toLowerCase().includes(inputValue.toLowerCase()),
)
setOptions(filtered.length > 0 ? filtered : comboboxData)
},
})
const api = combobox.connect(service, normalizeProps)
return (
{options.length > 0 && (
{options.map((item) => (
{item.label}
))}
)}
)
}
```
### Setting the initial value
Set `defaultValue` to define the initial combobox value.
```jsx {13}
const collection = combobox.collection({
items: [
{ label: "Nigeria", value: "ng" },
{ label: "Ghana", value: "gh" },
{ label: "Kenya", value: "ke" },
//...
],
})
const service = useMachine(combobox.machine, {
id: useId(),
collection,
defaultValue: ["ng"],
})
```
### Controlled combobox
Use `value` and `onValueChange` to control the value programmatically.
```tsx
import { useState } from "react"
export function ControlledCombobox() {
const [value, setValue] = useState(["ng"])
const service = useMachine(combobox.machine, {
value,
onValueChange(details) {
setValue(details.value)
}
})
return (
// ...
)
}
```
### Setting the initial input value
Use `defaultInputValue` to prefill the input on first render.
```jsx
const service = useMachine(combobox.machine, {
id: useId(),
collection,
defaultInputValue: "Nig",
})
```
### Controlling the input value
Use `inputValue` and `onInputValueChange` when you want to filter options as you
type.
```jsx
const service = useMachine(combobox.machine, {
id: useId(),
collection,
inputValue,
onInputValueChange({ inputValue }) {
setInputValue(inputValue)
setOptions(filterItems(inputValue))
},
})
```
### Selecting multiple values
Set `multiple` to `true` to allow selecting multiple values.
```jsx {4}
const service = useMachine(combobox.machine, {
id: useId(),
collection,
multiple: true,
})
```
### Using a custom object format
By default, the combobox collection expects an array of items with `label` and
`value` properties. To use a custom object format, pass the `itemToString` and
`itemToValue` properties to the collection function.
- `itemToString` — A function that returns the string representation of an item.
Used to compare items when filtering.
- `itemToValue` — A function that returns the unique value of an item.
- `itemToDisabled` — A function that returns the disabled state of an item.
```jsx
const collection = combobox.collection({
// custom object format
items: [
{ id: 1, fruit: "Banana", available: true, quantity: 10 },
{ id: 2, fruit: "Apple", available: false, quantity: 5 },
{ id: 3, fruit: "Orange", available: true, quantity: 3 },
//...
],
// convert item to string
itemToString(item) {
return item.fruit
},
// convert item to value
itemToValue(item) {
return item.id
},
// convert item to disabled state
itemToDisabled(item) {
return !item.available || item.quantity === 0
},
})
// use the collection
const service = useMachine(combobox.machine, {
id: useId(),
collection,
})
```
### Rendering the selected values outside the combobox
By default, selected values are shown in the input. For multiple selection, it
is often better to render selected values outside the combobox.
To do that:
- Set the `selectionBehavior` to `clear`, which clears the input value when an
item is selected.
- Set the `multiple` property to `true` to allow selecting multiple values.
- Render the selected values outside the combobox.
```jsx {4-6}
const service = useMachine(combobox.machine, {
id: useId(),
collection,
selectionBehavior: "clear",
multiple: true,
})
```
### Disabling the combobox
Set `disabled` to `true` to disable the combobox.
```jsx {2}
const service = useMachine(combobox.machine, {
disabled: true,
})
```
### Disabling an option
Pass `isItemDisabled` to disable specific options.
```jsx {6-8}
const service = useMachine(combobox.machine, {
id: useId(),
collection: combobox.collection({
items: countries,
isItemDisabled(item) {
return item.disabled
},
}),
})
```
### Close on select
By default, the menu closes when an option is selected with pointer or enter
key. Set `closeOnSelect` to `false` to keep it open.
```jsx {2}
const service = useMachine(combobox.machine, {
closeOnSelect: false,
})
```
### Controlling open state
Use `open` and `onOpenChange` for controlled popup state, or `defaultOpen` for
an uncontrolled initial state.
```jsx
const service = useMachine(combobox.machine, {
id: useId(),
collection,
open,
onOpenChange(details) {
// details => { open: boolean; reason?: string; value: string[] }
setOpen(details.open)
},
})
```
```jsx
const service = useMachine(combobox.machine, {
id: useId(),
collection,
defaultOpen: true,
})
```
### Configuring popup trigger behavior
Use these props to fine-tune when the popup opens:
- `openOnClick` to open when the input is clicked
- `openOnChange` to control opening on input changes
- `openOnKeyPress` to control opening on arrow key press
- `inputBehavior` to set how typing and keyboard navigation affect
highlight/input
```jsx
const service = useMachine(combobox.machine, {
id: useId(),
collection,
openOnClick: true,
openOnChange: false,
openOnKeyPress: false,
inputBehavior: "autohighlight",
})
```
### Positioning the popup
Use `positioning` to control how the popup is placed.
```jsx
const service = useMachine(combobox.machine, {
id: useId(),
collection,
positioning: { placement: "bottom-start" },
})
```
### Submitting forms on Enter
Set `alwaysSubmitOnEnter` to `true` if you want Enter to submit the form even
while the popup is open.
```jsx
const service = useMachine(combobox.machine, {
id: useId(),
collection,
alwaysSubmitOnEnter: true,
})
```
### Making the combobox readonly
Set `readOnly` to `true` to make the combobox read-only.
```jsx {2}
const service = useMachine(combobox.machine, {
readOnly: true,
})
```
### Required and invalid state
Set `required` and `invalid` for form validation and UI state.
```jsx
const service = useMachine(combobox.machine, {
id: useId(),
collection,
required: true,
invalid: false,
})
```
### Listening for highlight changes
Use `onHighlightChange` to listen for highlighted option changes.
```jsx {3-6}
const service = useMachine(combobox.machine, {
id: useId(),
onHighlightChange(details) {
// details => { highlightedValue: string | null; highlightedItem: CollectionItem | null }
console.log(details)
},
})
```
### Setting the initial highlighted option
Use `defaultHighlightedValue` to set which option is highlighted when the popup
opens.
```jsx
const service = useMachine(combobox.machine, {
id: useId(),
collection,
defaultHighlightedValue: "ng",
})
```
### Listening for item selection
Use `onSelect` to react to each selected item.
```jsx
const service = useMachine(combobox.machine, {
id: useId(),
collection,
onSelect(details) {
// details => { value: string[]; itemValue: string }
console.log(details.itemValue)
},
})
```
### Listening for value changes
Use `onValueChange` to listen for selected value changes.
```jsx {3-6}
const service = useMachine(combobox.machine, {
onValueChange(details) {
// details => { value: string[]; items: CollectionItem[] }
console.log(details)
},
})
```
### Usage within forms
The combobox works in forms when you:
- add a `name` so the selected value is included in `FormData`.
Set `name` to enable form submission support.
```jsx {2}
const service = useMachine(combobox.machine, {
name: "countries",
})
```
If the input belongs to a different form element, also set `form`.
```jsx
const service = useMachine(combobox.machine, {
id: useId(),
collection,
name: "countries",
form: "checkout-form",
})
```
### Allowing custom values
By default, the combobox only allows values from the collection. Set
`allowCustomValue` to `true` to allow custom values.
```jsx {2}
const service = useMachine(combobox.machine, {
allowCustomValue: true,
})
```
### Customizing accessibility labels
Use `translations` to customize the trigger and clear button labels.
```jsx
const service = useMachine(combobox.machine, {
id: useId(),
collection,
translations: {
triggerLabel: "Open countries",
clearTriggerLabel: "Clear selection",
},
})
```
## Styling guide
Each combobox part includes a `data-part` attribute you can target in CSS.
### Open and closed state
When the combobox opens or closes, `data-state` is added to content, control,
input, and trigger parts.
```css
[data-part="control"][data-state="open|closed"] {
/* styles for open or closed control state */
}
[data-part="input"][data-state="open|closed"] {
/* styles for open or closed input state */
}
[data-part="trigger"][data-state="open|closed"] {
/* styles for open or closed trigger state */
}
[data-part="content"][data-state="open|closed"] {
/* styles for open or closed content state */
}
```
### Focused State
When the combobox 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 combobox 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="item"][data-disabled] {
/* styles for item disabled state */
}
```
### Invalid State
When the combobox is invalid, the `data-invalid` attribute is added to the root,
label, control and input 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="input"][data-invalid] {
/* styles for input invalid state */
}
```
### Selected State
When a combobox item is selected, the `data-state` attribute is added to the
item part.
```css
[data-part="item"][data-state="checked|unchecked"] {
/* styles for item selected state */
}
```
### Highlighted State
When a combobox item is highlighted, the `data-highlighted` attribute is added
to the item part.
```css
[data-part="item"][data-highlighted] {
/* styles for item highlighted state */
}
```
## Methods and Properties
### Machine Context
The combobox machine exposes the following context properties:
**`open`**
Type: `boolean`
Description: The controlled open state of the combobox
**`defaultOpen`**
Type: `boolean`
Description: The initial open state of the combobox when rendered.
Use when you don't need to control the open state of the combobox.
**`ids`**
Type: `Partial<{ root: string; label: string; control: string; input: string; content: string; trigger: string; clearTrigger: string; item: (id: string, index?: number) => string; positioner: string; itemGroup: (id: string | number) => string; itemGroupLabel: (id: string | number) => string; }>`
Description: The ids of the elements in the combobox. Useful for composition.
**`inputValue`**
Type: `string`
Description: The controlled value of the combobox's input
**`defaultInputValue`**
Type: `string`
Description: The initial value of the combobox's input when rendered.
Use when you don't need to control the value of the combobox's input.
**`name`**
Type: `string`
Description: The `name` attribute of the combobox's input. Useful for form submission
**`form`**
Type: `string`
Description: The associate form of the combobox.
**`disabled`**
Type: `boolean`
Description: Whether the combobox is disabled
**`readOnly`**
Type: `boolean`
Description: Whether the combobox is readonly. This puts the combobox in a "non-editable" mode
but the user can still interact with it
**`invalid`**
Type: `boolean`
Description: Whether the combobox is invalid
**`required`**
Type: `boolean`
Description: Whether the combobox is required
**`placeholder`**
Type: `string`
Description: The placeholder text of the combobox's input
**`defaultHighlightedValue`**
Type: `string`
Description: The initial highlighted value of the combobox when rendered.
Use when you don't need to control the highlighted value of the combobox.
**`highlightedValue`**
Type: `string`
Description: The controlled highlighted value of the combobox
**`value`**
Type: `string[]`
Description: The controlled value of the combobox's selected items
**`defaultValue`**
Type: `string[]`
Description: The initial value of the combobox's selected items when rendered.
Use when you don't need to control the value of the combobox's selected items.
**`inputBehavior`**
Type: `"autohighlight" | "autocomplete" | "none"`
Description: Defines the auto-completion behavior of the combobox.
- `autohighlight`: The first focused item is highlighted as the user types
- `autocomplete`: Navigating the listbox with the arrow keys selects the item and the input is updated
**`selectionBehavior`**
Type: `"clear" | "replace" | "preserve"`
Description: The behavior of the combobox input when an item is selected
- `replace`: The selected item string is set as the input value
- `clear`: The input value is cleared
- `preserve`: The input value is preserved
**`autoFocus`**
Type: `boolean`
Description: Whether to autofocus the input on mount
**`openOnClick`**
Type: `boolean`
Description: Whether to open the combobox popup on initial click on the input
**`openOnChange`**
Type: `boolean | ((details: InputValueChangeDetails) => boolean)`
Description: Whether to show the combobox when the input value changes
**`allowCustomValue`**
Type: `boolean`
Description: Whether to allow typing custom values in the input
**`alwaysSubmitOnEnter`**
Type: `boolean`
Description: Whether to always submit on Enter key press, even if popup is open.
Useful for single-field autocomplete forms where Enter should submit the form.
**`loopFocus`**
Type: `boolean`
Description: Whether to loop the keyboard navigation through the items
**`positioning`**
Type: `PositioningOptions`
Description: The positioning options to dynamically position the menu
**`onInputValueChange`**
Type: `(details: InputValueChangeDetails) => void`
Description: Function called when the input's value changes
**`onValueChange`**
Type: `(details: ValueChangeDetails) => void`
Description: Function called when a new item is selected
**`onHighlightChange`**
Type: `(details: HighlightChangeDetails) => void`
Description: Function called when an item is highlighted using the pointer
or keyboard navigation.
**`onSelect`**
Type: `(details: SelectionDetails) => void`
Description: Function called when an item is selected
**`onOpenChange`**
Type: `(details: OpenChangeDetails) => void`
Description: Function called when the popup is opened
**`translations`**
Type: `IntlTranslations`
Description: Specifies the localized strings that identifies the accessibility elements and their states
**`collection`**
Type: `ListCollection`
Description: The collection of items
**`multiple`**
Type: `boolean`
Description: Whether to allow multiple selection.
**Good to know:** When `multiple` is `true`, the `selectionBehavior` is automatically set to `clear`.
It is recommended to render the selected items in a separate container.
**`closeOnSelect`**
Type: `boolean`
Description: Whether to close the combobox when an item is selected.
**`openOnKeyPress`**
Type: `boolean`
Description: Whether to open the combobox on arrow key press
**`scrollToIndexFn`**
Type: `(details: ScrollToIndexDetails) => void`
Description: Function to scroll to a specific index
**`composite`**
Type: `boolean`
Description: Whether the combobox is a composed with other composite widgets like tabs
**`disableLayer`**
Type: `boolean`
Description: Whether to disable registering this a dismissable layer
**`navigate`**
Type: `(details: NavigateDetails) => void`
Description: Function to navigate to the selected item
**`dir`**
Type: `"ltr" | "rtl"`
Description: The document's text/writing direction.
**`id`**
Type: `string`
Description: The unique identifier of the machine.
**`getRootNode`**
Type: `() => ShadowRoot | Node | Document`
Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
**`onPointerDownOutside`**
Type: `(event: PointerDownOutsideEvent) => void`
Description: Function called when the pointer is pressed down outside the component
**`onFocusOutside`**
Type: `(event: FocusOutsideEvent) => void`
Description: Function called when the focus is moved outside the component
**`onInteractOutside`**
Type: `(event: InteractOutsideEvent) => void`
Description: Function called when an interaction happens outside the component
### Machine API
The combobox `api` exposes the following methods:
**`focused`**
Type: `boolean`
Description: Whether the combobox is focused
**`open`**
Type: `boolean`
Description: Whether the combobox is open
**`inputValue`**
Type: `string`
Description: The value of the combobox input
**`highlightedValue`**
Type: `string`
Description: The value of the highlighted item
**`highlightedItem`**
Type: `V`
Description: The highlighted item
**`setHighlightValue`**
Type: `(value: string) => void`
Description: The value of the combobox input
**`clearHighlightValue`**
Type: `VoidFunction`
Description: Function to clear the highlighted value
**`syncSelectedItems`**
Type: `VoidFunction`
Description: Function to sync the selected items with the value.
Useful when `value` is updated from async sources.
**`selectedItems`**
Type: `V[]`
Description: The selected items
**`hasSelectedItems`**
Type: `boolean`
Description: Whether there's a selected item
**`value`**
Type: `string[]`
Description: The selected item keys
**`valueAsString`**
Type: `string`
Description: The string representation of the selected items
**`selectValue`**
Type: `(value: string) => void`
Description: Function to select a value
**`setValue`**
Type: `(value: string[]) => void`
Description: Function to set the value of the combobox
**`clearValue`**
Type: `(value?: string) => void`
Description: Function to clear the value of the combobox
**`focus`**
Type: `VoidFunction`
Description: Function to focus on the combobox input
**`setInputValue`**
Type: `(value: string, reason?: InputValueChangeReason) => void`
Description: Function to set the input value of the combobox
**`getItemState`**
Type: `(props: ItemProps) => ItemState`
Description: Returns the state of a combobox item
**`setOpen`**
Type: `(open: boolean, reason?: OpenChangeReason) => void`
Description: Function to open or close the combobox
**`collection`**
Type: `ListCollection`
Description: Function to toggle the combobox
**`reposition`**
Type: `(options?: Partial) => void`
Description: Function to set the positioning options
**`multiple`**
Type: `boolean`
Description: Whether the combobox allows multiple selections
**`disabled`**
Type: `boolean`
Description: Whether the combobox is disabled
### Data Attributes
**`Root`**
**`data-scope`**: combobox
**`data-part`**: root
**`data-invalid`**: Present when invalid
**`data-readonly`**: Present when read-only
**`Label`**
**`data-scope`**: combobox
**`data-part`**: label
**`data-readonly`**: Present when read-only
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`data-required`**: Present when required
**`data-focus`**: Present when focused
**`Control`**
**`data-scope`**: combobox
**`data-part`**: control
**`data-state`**: "open" | "closed"
**`data-focus`**: Present when focused
**`data-disabled`**: Present when disabled
**`data-invalid`**: Present when invalid
**`Input`**
**`data-scope`**: combobox
**`data-part`**: input
**`data-invalid`**: Present when invalid
**`data-autofocus`**:
**`data-state`**: "open" | "closed"
**`Trigger`**
**`data-scope`**: combobox
**`data-part`**: trigger
**`data-state`**: "open" | "closed"
**`data-invalid`**: Present when invalid
**`data-focusable`**:
**`data-readonly`**: Present when read-only
**`data-disabled`**: Present when disabled
**`Content`**
**`data-scope`**: combobox
**`data-part`**: content
**`data-state`**: "open" | "closed"
**`data-nested`**: listbox
**`data-has-nested`**: listbox
**`data-placement`**: The placement of the content
**`data-empty`**: Present when the content is empty
**`List`**
**`data-scope`**: combobox
**`data-part`**: list
**`data-empty`**: Present when the content is empty
**`ClearTrigger`**
**`data-scope`**: combobox
**`data-part`**: clear-trigger
**`data-invalid`**: Present when invalid
**`Item`**
**`data-scope`**: combobox
**`data-part`**: item
**`data-highlighted`**: Present when highlighted
**`data-state`**: "checked" | "unchecked"
**`data-disabled`**: Present when disabled
**`data-value`**: The value of the item
**`ItemText`**
**`data-scope`**: combobox
**`data-part`**: item-text
**`data-state`**: "checked" | "unchecked"
**`data-disabled`**: Present when disabled
**`data-highlighted`**: Present when highlighted
**`ItemIndicator`**
**`data-scope`**: combobox
**`data-part`**: item-indicator
**`data-state`**: "checked" | "unchecked"
**`ItemGroup`**
**`data-scope`**: combobox
**`data-part`**: item-group
**`data-empty`**: Present when the content is empty
### CSS Variables
## Accessibility
Adheres to the
[Combobox WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/).
### Keyboard Interactions
**`ArrowDown`**
Description: When the combobox is closed, opens the listbox and highlights to the first option.
When the combobox is open, moves focus to the next option.
**`ArrowUp`**
Description: When the combobox is closed, opens the listbox and highlights to the last option.
When the combobox is open, moves focus to the previous option.
**`Home`**
Description: When the combobox is open, moves focus to the first option.
**`End`**
Description: When the combobox is open, moves focus to the last option.
**`Escape`**
Description: Closes the listbox.
**`Enter`**
Description: Selects the highlighted option and closes the combobox.
**`Esc`**
Description: Closes the combobox
A date picker lets you enter a date through text input or pick one from a
calendar.
> **Good to know**: The date picker is built on top of the
> [`@internationalized/date`](https://react-spectrum.adobe.com/internationalized/date/CalendarDate.html)
> library.
## Resources
[Latest version: v1.35.2](https://www.npmjs.com/package/@zag-js/date-picker)
[Logic Visualizer](https://zag-visualizer.vercel.app/date-picker)
[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/date-picker)
**Features**
- Displays a calendar view for date selection
- Supports `single`, `multiple`, and `range` selection modes
- Supports disabling specific dates
- Supports date range presets
- Supports week numbers
- Supports custom format and parse logic
- Works with localization, timezone, and custom calendar systems
- Provides keyboard accessibility for navigating the calendar.
## Installation
Install the date-picker package:
```bash
npm install @zag-js/date-picker @zag-js/react
# or
yarn add @zag-js/date-picker @zag-js/react
```
## Anatomy
Check the date-picker anatomy and part names.
> Each part includes a `data-part` attribute to help identify them in the DOM.
## Usage
Import the package:
```tsx
import * as datepicker from "@zag-js/date-picker"
```
These are the key exports:
- `machine` - Behavior logic.
- `connect` - Maps behavior to JSX props and event handlers.
- `parse` - Parses an ISO 8601 date string.
> You'll also need to provide a unique `id` to the `useMachine` hook. This is
> used to ensure that every part has a unique identifier.
Then use the framework integration helpers:
```jsx
import * as datepicker from "@zag-js/date-picker"
import { useMachine, normalizeProps, Portal } from "@zag-js/react"
import { useId } from "react"
function DatePicker() {
const service = useMachine(datepicker.machine, { id: useId() })
const api = datepicker.connect(service, normalizeProps)
return (
<>
>
)
}
```
### Setting the initial date
To set the initial value rendered by the date picker, set `defaultValue`.
```tsx
const service = useMachine(datepicker.machine, {
defaultValue: [datepicker.parse("2022-01-01")],
})
```
### Controlling the selected date
Use the `value` and `onValueChange` properties to programmatically control the
selected date.
```tsx
const service = useMachine(datepicker.machine, {
value: [datepicker.parse("2022-01-01")],
onValueChange(details) {
// details => { value: DateValue[], valueAsString: string[], view: "day" | "month" | "year" }
console.log("selected date:", details.valueAsString)
},
})
```
You can also set it with `api.setValue`.
```tsx
const nextValue = datepicker.parse("2022-01-01")
api.setValue([nextValue])
```
### Controlling the open state
Use `open` and `onOpenChange` to control the popup state.
```tsx
const service = useMachine(datepicker.machine, {
open: true,
onOpenChange(details) {
// details => { open: boolean, value: DateValue[] }
console.log("open state changed to:", details.open)
},
})
```
You can also manage it with `api.setOpen`.
```tsx
// open the date picker
api.setOpen(true)
// close the date picker
api.setOpen(false)
```
### Setting the min and max dates
To constrain the date range that can be selected by the user, set the `min` and
`max` properties.
```tsx
const service = useMachine(datepicker.machine, {
min: datepicker.parse("2022-01-01"),
max: datepicker.parse("2022-12-31"),
})
```
When min or max is reached, previous/next triggers are disabled.
### Changing the start of the week
Set `startOfWeek` from `0` to `6` (`0` is Sunday, `6` is Saturday).
```tsx
const service = useMachine(datepicker.machine, {
startOfWeek: 1, // Monday
})
```
### Disabling the date picker
Set `disabled` to `true` to disable the picker.
```tsx
const service = useMachine(datepicker.machine, {
disabled: true,
})
```
### Rendering month and year pickers
Render month and year selects with `api.getMonthSelectProps` and
`api.getYearSelectProps`.
```tsx
```
### Marking unavailable dates
Set `isDateUnavailable` and return `true` for blocked dates.
```tsx
import { isWeekend } from "@internationalized/date"
const service = useMachine(datepicker.machine, {
isDateUnavailable: (date, locale) => {
// mark weekends as unavailable
return isWeekend(date, locale)
},
})
```
### Setting the calendar starting view
The default view is `day`. Set `defaultView` to `day`, `month`, or `year` to
change it.
```tsx
const service = useMachine(datepicker.machine, {
defaultView: "month",
})
```
### Setting the read-only mode
Set `readOnly` to `true` to prevent value changes.
```tsx
const service = useMachine(datepicker.machine, {
readOnly: true,
})
```
### Required and invalid state
Use `required` and `invalid` for form validation and UI state.
```tsx
const service = useMachine(datepicker.machine, {
required: true,
invalid: false,
})
```
### Setting the focused date
By default, focused date is the first selected date or today. Set
`defaultFocusedValue` to override it.
```tsx
const service = useMachine(datepicker.machine, {
defaultFocusedValue: datepicker.parse("2022-01-01"),
})
```
### Rendering the calendar inline
To render the calendar inline, set `inline` to `true`.
```tsx
const service = useMachine(datepicker.machine, {
inline: true,
})
```
### Usage within a form
Set `name` to include the picker in form data.
```tsx
const service = useMachine(datepicker.machine, {
name: "date",
})
```
### Rendering fixed number of weeks
Set `fixedWeeks` to `true` to always render 6 weeks and avoid layout jumps
between months.
```tsx
const service = useMachine(datepicker.machine, {
fixedWeeks: true,
})
```
### Showing week numbers
Set `showWeekNumbers` to `true` to enable a week-number column in day view.
```tsx
const service = useMachine(datepicker.machine, {
showWeekNumbers: true,
})
```
Then render the week-number header and cells in your day table.
```tsx
{api.showWeekNumbers && (
Wk
)}
{api.weekDays.map((day, i) => (
{day.narrow}
))}
{api.weeks.map((week, weekIndex) => (
{api.showWeekNumbers && (
{api.getWeekNumber(week)}
)}
{week.map((value, i) => (
{value.day}
))}
))}
```
### Choosing a selection mode
Use `selectionMode` to switch between `single`, `multiple`, and `range`.
```tsx
const service = useMachine(datepicker.machine, {
selectionMode: "multiple",
})
```
If you use `multiple`, you can cap selections with `maxSelectedDates`.
```tsx
const service = useMachine(datepicker.machine, {
selectionMode: "multiple",
maxSelectedDates: 3,
})
```
### Using range presets
In range mode, `api.getPresetTriggerProps` accepts a preset key like
`"last7Days"` or a custom `DateValue[]`.
```tsx
const service = useMachine(datepicker.machine, {
selectionMode: "range",
})
```
```tsx
```
```tsx
```
### Customizing format and parse behavior
Use `format` and `parse` to control how input text is displayed and parsed.
```tsx
const service = useMachine(datepicker.machine, {
format: (date, details) =>
date
.toDate(details.timeZone)
.toLocaleDateString(details.locale, { dateStyle: "short" }),
parse: (value) => datepicker.parse(value),
})
```
You can also build specialized pickers:
- Month-year picker: use `minView: "month"` and `placeholder: "mm/yyyy"`
- Year picker: use `minView: "year"`, `defaultView: "year"`, and
`placeholder: "yyyy"`
```tsx
import { CalendarDate } from "@internationalized/date"
const service = useMachine(datepicker.machine, {
minView: "month",
view: "month",
placeholder: "mm/yyyy",
format: (date) => `${String(date.month).padStart(2, "0")}/${date.year}`,
parse: (value) => {
const match = value.match(/^(\d{1,2})\/(\d{4})$/)
if (!match) return undefined
const [_, month, year] = match.map(Number)
return new CalendarDate(year, month, 1)
},
})
```
```tsx
import { CalendarDate } from "@internationalized/date"
const service = useMachine(datepicker.machine, {
selectionMode: "range",
minView: "year",
defaultView: "year",
placeholder: "yyyy",
format: (date) => String(date.year),
parse: (value) => {
const match = value.match(/^(\d{4})$/)
if (!match) return undefined
const [_, year] = match.map(Number)
return new CalendarDate(year, 1, 1)
},
})
```
### Setting locale and timezone
Set `locale` and `timeZone` to control date rendering.
```tsx
const service = useMachine(datepicker.machine, {
locale: "en-GB",
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
})
```
### Using custom calendar systems
Pass a dedicated `createCalendar` function so you only ship the calendar your
locale needs.
```tsx
import { PersianCalendar } from "@internationalized/date"
function createPersianCalendar(identifier: string) {
if (identifier === "persian") return new PersianCalendar()
throw new Error(`Unsupported calendar: ${identifier}`)
}
const service = useMachine(datepicker.machine, {
locale: "fa-IR",
createCalendar: createPersianCalendar,
})
```
```tsx
import { BuddhistCalendar } from "@internationalized/date"
function createBuddhistCalendar(identifier: string) {
if (identifier === "buddhist") return new BuddhistCalendar()
throw new Error(`Unsupported calendar: ${identifier}`)
}
const service = useMachine(datepicker.machine, {
locale: "th-TH",
createCalendar: createBuddhistCalendar,
})
```
### Restricting available views
Use `minView` and `maxView` to limit view navigation.
```tsx
const service = useMachine(datepicker.machine, {
minView: "month",
maxView: "year",
})
```
### Controlling open behavior
By default, the picker closes after selection and does not open on input click.
You can change both behaviors.
```tsx
const service = useMachine(datepicker.machine, {
openOnClick: true,
closeOnSelect: false,
})
```
Use `defaultOpen` if you want the calendar to start open in uncontrolled mode.
```tsx
const service = useMachine(datepicker.machine, {
defaultOpen: true,
})
```
### Allowing outside-day selection
Days outside the visible month are disabled by default. Set
`outsideDaySelectable` to `true` if you want them selectable.
```tsx
const service = useMachine(datepicker.machine, {
outsideDaySelectable: true,
})
```
### Listening to date changes
Use `onValueChange` to listen for date changes.
```tsx
const service = useMachine(datepicker.machine, {
onValueChange(details) {
// details => { value: DateValue[], valueAsString: string[], view: "day" | "month" | "year" }
console.log("selected date:", details.valueAsString)
},
})
```
### Listening to view changes
Use `onViewChange` to listen for view changes.
```tsx
const service = useMachine(datepicker.machine, {
onViewChange(details) {
// details => { view: "day" | "month" | "year" }
console.log("view changed to:", details.view)
},
})
```
### Controlling the focused date
Use `focusedValue` and `onFocusChange` to control keyboard focus in the
calendar.
```tsx
const service = useMachine(datepicker.machine, {
focusedValue: datepicker.parse("2022-01-01"),
onFocusChange(details) {
console.log("focused date:", details.focusedValue)
},
})
```
### Listening to focus and visible range changes
Use `onFocusChange` for focused-date changes and `onVisibleRangeChange` for
visible-range changes.
```tsx
const service = useMachine(datepicker.machine, {
onFocusChange(details) {
console.log("focused date:", details.focusedValue)
},
onVisibleRangeChange(details) {
console.log("visible range:", details.visibleRange)
},
})
```
### Rendering multiple months
To display multiple months:
- set the `numOfMonths` prop to the desired number of months
- generate the weeks for the offset months using `api.getOffset({ months: 1 })`
```tsx
const service = useMachine(datepicker.machine, {
// ...
numOfMonths: 2,
})
const offset = api.getOffset({ months: 1 })
```
Then render the offset months.
```tsx
{offset.weeks.map((week, i) => (
{week.map((value, i) => (
{value.day}
))}
))}
```
### Positioning
Use `positioning` to customize popup placement.
```tsx
const service = useMachine(datepicker.machine, {
positioning: { placement: "bottom-start" },
})
```
### Localization
Use `translations` to customize accessibility labels and messages.
```tsx
const service = useMachine(datepicker.machine, {
translations: {
clearTrigger: "Clear date",
},
})
```
## Styling guide
Each date-picker part includes a `data-part` attribute you can target in CSS.
```css
[data-scope="date-picker"][data-part="root"] {
/* styles for the root part */
}
[data-scope="date-picker"][data-part="input"] {
/* styles for the input part */
}
[data-scope="date-picker"][data-part="trigger"] {
/* styles for the trigger part */
}
[data-scope="date-picker"][data-part="content"] {
/* styles for the input part */
}
```
### Open State
```css
[data-scope="date-picker"][data-part="trigger"] {
&[data-state="open"] {
/* styles for the open state */
}
&[data-state="closed"] {
/* styles for the closed state */
}
}
```
### Cell States
```css
[data-scope="date-picker"][data-part="table-cell-trigger"] {
/* styles for the cell */
&[data-selected] {
/* styles for the selected date */
}
&[data-focus] {
/* styles for the focused date */
}
&[data-disabled] {
/* styles for the disabled date */
}
&[data-unavailable] {
/* styles for the unavailable date */
}
&[data-today] {
/* styles for the today date */
}
&[data-weekend] {
/* styles for the weekend date */
}
}
```
## Methods and Properties
### Machine Context
The date picker machine exposes the following context properties:
**`locale`**
Type: `string`
Description: The locale (BCP 47 language tag) to use when formatting the date.
**`createCalendar`**
Type: `(identifier: CalendarIdentifier) => Calendar`
Description: A function that creates a Calendar object for a given calendar identifier.
Enables non-Gregorian calendar support (Persian, Buddhist, Islamic, etc.)
without bundling all calendars by default.
**`translations`**
Type: `IntlTranslations`
Description: The localized messages to use.
**`ids`**
Type: `Partial<{ root: string; label: (index: number) => string; table: (id: string) => string; tableHeader: (id: string) => string; tableBody: (id: string) => string; tableRow: (id: string) => string; content: string; cellTrigger: (id: string) => string; prevTrigger: (view: DateView) => string; nextTrigger: (view: DateView) => string; viewTrigger: (view: DateView) => string; clearTrigger: string; control: string; input: (index: number) => string; trigger: string; monthSelect: string; yearSelect: string; positioner: string; }>`
Description: The ids of the elements in the date picker. Useful for composition.
**`name`**
Type: `string`
Description: The `name` attribute of the input element.
**`timeZone`**
Type: `string`
Description: The time zone to use
**`disabled`**
Type: `boolean`
Description: Whether the calendar is disabled.
**`readOnly`**
Type: `boolean`
Description: Whether the calendar is read-only.
**`required`**
Type: `boolean`
Description: Whether the date picker is required
**`invalid`**
Type: `boolean`
Description: Whether the date picker is invalid
**`outsideDaySelectable`**
Type: `boolean`
Description: Whether day outside the visible range can be selected.
**`min`**
Type: `DateValue`
Description: The minimum date that can be selected.
**`max`**
Type: `DateValue`
Description: The maximum date that can be selected.
**`closeOnSelect`**
Type: `boolean`
Description: Whether the calendar should close after the date selection is complete.
This is ignored when the selection mode is `multiple`.
**`openOnClick`**
Type: `boolean`
Description: Whether to open the calendar when the input is clicked.
**`value`**
Type: `DateValue[]`
Description: The controlled selected date(s).
**`defaultValue`**
Type: `DateValue[]`
Description: The initial selected date(s) when rendered.
Use when you don't need to control the selected date(s) of the date picker.
**`focusedValue`**
Type: `DateValue`
Description: The controlled focused date.
**`defaultFocusedValue`**
Type: `DateValue`
Description: The initial focused date when rendered.
Use when you don't need to control the focused date of the date picker.
**`numOfMonths`**
Type: `number`
Description: The number of months to display.
**`startOfWeek`**
Type: `number`
Description: The first day of the week.
`0` - Sunday
`1` - Monday
`2` - Tuesday
`3` - Wednesday
`4` - Thursday
`5` - Friday
`6` - Saturday
**`fixedWeeks`**
Type: `boolean`
Description: Whether the calendar should have a fixed number of weeks.
This renders the calendar with 6 weeks instead of 5 or 6.
**`showWeekNumbers`**
Type: `boolean`
Description: Whether to show the week number column in the day view.
**`onValueChange`**
Type: `(details: ValueChangeDetails) => void`
Description: Function called when the value changes.
**`onFocusChange`**
Type: `(details: FocusChangeDetails) => void`
Description: Function called when the focused date changes.
**`onViewChange`**
Type: `(details: ViewChangeDetails) => void`
Description: Function called when the view changes.
**`onVisibleRangeChange`**
Type: `(details: VisibleRangeChangeDetails) => void`
Description: Function called when the visible range changes.
**`onOpenChange`**
Type: `(details: OpenChangeDetails) => void`
Description: Function called when the calendar opens or closes.
**`isDateUnavailable`**
Type: `(date: DateValue, locale: string) => boolean`
Description: Returns whether a date of the calendar is available.
**`selectionMode`**
Type: `SelectionMode`
Description: The selection mode of the calendar.
- `single` - only one date can be selected
- `multiple` - multiple dates can be selected
- `range` - a range of dates can be selected
**`maxSelectedDates`**
Type: `number`
Description: The maximum number of dates that can be selected.
This is only applicable when `selectionMode` is `multiple`.
**`format`**
Type: `(date: LocaleDetails) => string`
Description: The format of the date to display in the input.
**`parse`**
Type: `(value: string, details: LocaleDetails) => DateValue`
Description: Function to parse the date from the input back to a DateValue.
**`placeholder`**
Type: `string`
Description: The placeholder text to display in the input.
**`view`**
Type: `DateView`
Description: The view of the calendar
**`defaultView`**
Type: `DateView`
Description: The default view of the calendar
**`minView`**
Type: `DateView`
Description: The minimum view of the calendar
**`maxView`**
Type: `DateView`
Description: The maximum view of the calendar
**`positioning`**
Type: `PositioningOptions`
Description: The user provided options used to position the date picker content
**`open`**
Type: `boolean`
Description: The controlled open state of the date picker
**`defaultOpen`**
Type: `boolean`
Description: The initial open state of the date picker when rendered.
Use when you don't need to control the open state of the date picker.
**`inline`**
Type: `boolean`
Description: Whether to render the date picker inline
**`dir`**
Type: `"ltr" | "rtl"`
Description: The document's text/writing direction.
**`id`**
Type: `string`
Description: The unique identifier of the machine.
**`getRootNode`**
Type: `() => ShadowRoot | Node | Document`
Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
### Machine API
The date picker `api` exposes the following methods:
**`focused`**
Type: `boolean`
Description: Whether the input is focused
**`open`**
Type: `boolean`
Description: Whether the date picker is open
**`disabled`**
Type: `boolean`
Description: Whether the date picker is disabled
**`invalid`**
Type: `boolean`
Description: Whether the date picker is invalid
**`readOnly`**
Type: `boolean`
Description: Whether the date picker is read-only
**`inline`**
Type: `boolean`
Description: Whether the date picker is rendered inline
**`numOfMonths`**
Type: `number`
Description: The number of months to display
**`showWeekNumbers`**
Type: `boolean`
Description: Whether the week number column is shown in the day view
**`selectionMode`**
Type: `SelectionMode`
Description: The selection mode (single, multiple, or range)
**`maxSelectedDates`**
Type: `number`
Description: The maximum number of dates that can be selected (only for multiple selection mode).
**`isMaxSelected`**
Type: `boolean`
Description: Whether the maximum number of selected dates has been reached.
**`view`**
Type: `DateView`
Description: The current view of the date picker
**`getWeekNumber`**
Type: `(week: DateValue[]) => number`
Description: Returns the ISO 8601 week number (1-53) for the given week (array of dates).
**`getDaysInWeek`**
Type: `(week: number, from?: DateValue) => DateValue[]`
Description: Returns an array of days in the week index counted from the provided start date, or the first visible date if not given.
**`getOffset`**
Type: `(duration: DateDuration) => DateValueOffset`
Description: Returns the offset of the month based on the provided number of months.
**`getRangePresetValue`**
Type: `(value: DateRangePreset) => DateValue[]`
Description: Returns the range of dates based on the provided date range preset.
**`getMonthWeeks`**
Type: `(from?: DateValue) => DateValue[][]`
Description: Returns the weeks of the month from the provided date. Represented as an array of arrays of dates.
**`isUnavailable`**
Type: `(date: DateValue) => boolean`
Description: Returns whether the provided date is available (or can be selected)
**`weeks`**
Type: `DateValue[][]`
Description: The weeks of the month. Represented as an array of arrays of dates.
**`weekDays`**
Type: `WeekDay[]`
Description: The days of the week. Represented as an array of strings.
**`visibleRange`**
Type: `VisibleRange`
Description: The visible range of dates.
**`visibleRangeText`**
Type: `VisibleRangeText`
Description: The human readable text for the visible range of dates.
**`value`**
Type: `DateValue[]`
Description: The selected date.
**`valueAsDate`**
Type: `Date[]`
Description: The selected date as a Date object.
**`valueAsString`**
Type: `string[]`
Description: The selected date as a string.
**`focusedValue`**
Type: `DateValue`
Description: The focused date.
**`focusedValueAsDate`**
Type: `Date`
Description: The focused date as a Date object.
**`focusedValueAsString`**
Type: `string`
Description: The focused date as a string.
**`selectToday`**
Type: `VoidFunction`
Description: Sets the selected date to today.
**`setValue`**
Type: `(values: DateValue[]) => void`
Description: Sets the selected date to the given date.
**`setTime`**
Type: `(time: Time, index?: number) => void`
Description: Sets the time for a specific date value.
Converts CalendarDate to CalendarDateTime if needed.
**`setFocusedValue`**
Type: `(value: DateValue) => void`
Description: Sets the focused date to the given date.
**`clearValue`**
Type: `(options?: { focus?: boolean; }) => void`
Description: Clears the selected date(s).
**`setOpen`**
Type: `(open: boolean) => void`
Description: Function to open or close the calendar.
**`focusMonth`**
Type: `(month: number) => void`
Description: Function to set the selected month.
**`focusYear`**
Type: `(year: number) => void`
Description: Function to set the selected year.
**`getYears`**
Type: `() => Cell[]`
Description: Returns the months of the year
**`getYearsGrid`**
Type: `(props?: YearGridProps) => YearGridValue`
Description: Returns the years of the decade based on the columns.
Represented as an array of arrays of years.
**`getDecade`**
Type: `() => Range`
Description: Returns the start and end years of the decade.
**`getMonths`**
Type: `(props?: MonthFormatOptions) => Cell[]`
Description: Returns the months of the year
**`getMonthsGrid`**
Type: `(props?: MonthGridProps) => MonthGridValue`
Description: Returns the months of the year based on the columns.
Represented as an array of arrays of months.
**`format`**
Type: `(value: DateValue, opts?: Intl.DateTimeFormatOptions) => string`
Description: Formats the given date value based on the provided options.
**`setView`**
Type: `(view: DateView) => void`
Description: Sets the view of the date picker.
**`goToNext`**
Type: `VoidFunction`
Description: Goes to the next month/year/decade.
**`goToPrev`**
Type: `VoidFunction`
Description: Goes to the previous month/year/decade.
**`getDayTableCellState`**
Type: `(props: DayTableCellProps) => DayTableCellState`
Description: Returns the state details for a given cell.
**`getMonthTableCellState`**
Type: `(props: TableCellProps) => TableCellState`
Description: Returns the state details for a given month cell.
**`getYearTableCellState`**
Type: `(props: TableCellProps) => TableCellState`
Description: Returns the state details for a given year cell.
### Data Attributes
**`Root`**
**`data-scope`**: date-picker
**`data-part`**: root
**`data-state`**: "open" | "closed"
**`data-disabled`**: Present when disabled
**`data-readonly`**: Present when read-only
**`data-empty`**: Present when the content is empty
**`Label`**
**`data-scope`**: date-picker
**`data-part`**: label
**`data-state`**: "open" | "closed"
**`data-index`**: The index of the item
**`data-disabled`**: Present when disabled
**`data-readonly`**: Present when read-only
**`Control`**
**`data-scope`**: date-picker
**`data-part`**: control
**`data-disabled`**: Present when disabled
**`data-placeholder-shown`**: Present when placeholder is shown
**`Content`**
**`data-scope`**: date-picker
**`data-part`**: content
**`data-state`**: "open" | "closed"
**`data-nested`**: popover
**`data-has-nested`**: popover
**`data-placement`**: The placement of the content
**`data-inline`**: Present when the content is inline
**`Table`**
**`data-scope`**: date-picker
**`data-part`**: table
**`data-columns`**:
**`data-view`**: The view of the table
**`TableHead`**
**`data-scope`**: date-picker
**`data-part`**: table-head
**`data-view`**: The view of the tablehead
**`data-disabled`**: Present when disabled
**`TableHeader`**
**`data-scope`**: date-picker
**`data-part`**: table-header
**`data-view`**: The view of the tableheader
**`data-disabled`**: Present when disabled
**`TableBody`**
**`data-scope`**: date-picker
**`data-part`**: table-body
**`data-view`**: The view of the tablebody
**`data-disabled`**: Present when disabled
**`TableRow`**
**`data-scope`**: date-picker
**`data-part`**: table-row
**`data-disabled`**: Present when disabled
**`data-view`**: The view of the tablerow
**`WeekNumberHeaderCell`**
**`data-scope`**: date-picker
**`data-part`**: week-number-header-cell
**`data-view`**: The view of the weeknumberheadercell
**`data-type`**: The type of the item
**`data-disabled`**: Present when disabled
**`WeekNumberCell`**
**`data-scope`**: date-picker
**`data-part`**: week-number-cell
**`data-view`**: The view of the weeknumbercell
**`data-week-index`**:
**`data-type`**: The type of the item
**`data-disabled`**: Present when disabled
**`DayTableCell`**
**`data-scope`**: date-picker
**`data-part`**: day-table-cell
**`data-value`**: The value of the item
**`DayTableCellTrigger`**
**`data-scope`**: date-picker
**`data-part`**: day-table-cell-trigger
**`data-disabled`**: Present when disabled
**`data-selected`**: Present when selected
**`data-value`**: The value of the item
**`data-view`**: The view of the daytablecelltrigger
**`data-today`**: Present when the date represents today
**`data-focus`**: Present when focused
**`data-unavailable`**: Present when the date is unavailable based on the min and max date
**`data-range-start`**: Present when is the start of a range
**`data-range-end`**: Present when is the end of a range
**`data-in-range`**: Present when is within the range
**`data-outside-range`**: Present when is outside the range
**`data-weekend`**: Present when is a weekend day
**`data-in-hover-range`**: Present when in hovered range
**`data-hover-range-start`**: Present when is the start of the hovered range
**`data-hover-range-end`**: Present when is the end of the hovered range
**`MonthTableCell`**
**`data-scope`**: date-picker
**`data-part`**: month-table-cell
**`data-selected`**: Present when selected
**`data-value`**: The value of the item
**`MonthTableCellTrigger`**
**`data-scope`**: date-picker
**`data-part`**: month-table-cell-trigger
**`data-selected`**: Present when selected
**`data-disabled`**: Present when disabled
**`data-focus`**: Present when focused
**`data-in-range`**: Present when is within the range
**`data-outside-range`**: Present when is outside the range
**`data-view`**: The view of the monthtablecelltrigger
**`data-value`**: The value of the item
**`YearTableCell`**
**`data-scope`**: date-picker
**`data-part`**: year-table-cell
**`data-selected`**: Present when selected
**`data-value`**: The value of the item
**`YearTableCellTrigger`**
**`data-scope`**: date-picker
**`data-part`**: year-table-cell-trigger
**`data-selected`**: Present when selected
**`data-focus`**: Present when focused
**`data-in-range`**: Present when is within the range
**`data-disabled`**: Present when disabled
**`data-outside-range`**: Present when is outside the range
**`data-value`**: The value of the item
**`data-view`**: The view of the yeartablecelltrigger
**`NextTrigger`**
**`data-scope`**: date-picker
**`data-part`**: next-trigger
**`data-disabled`**: Present when disabled
**`PrevTrigger`**
**`data-scope`**: date-picker
**`data-part`**: prev-trigger
**`data-disabled`**: Present when disabled
**`Trigger`**
**`data-scope`**: date-picker
**`data-part`**: trigger
**`data-placement`**: The placement of the trigger
**`data-state`**: "open" | "closed"
**`data-placeholder-shown`**: Present when placeholder is shown
**`View`**
**`data-scope`**: date-picker
**`data-part`**: view
**`data-view`**: The view of the view
**`ViewTrigger`**
**`data-scope`**: date-picker
**`data-part`**: view-trigger
**`data-view`**: The view of the viewtrigger
**`ViewControl`**
**`data-scope`**: date-picker
**`data-part`**: view-control
**`data-view`**: The view of the viewcontrol
**`Input`**
**`data-scope`**: date-picker
**`data-part`**: input
**`data-index`**: The index of the item
**`data-state`**: "open" | "closed"
**`data-placeholder-shown`**: Present when placeholder is shown
**`data-invalid`**: Present when invalid
### CSS Variables
A dialog is a window overlaid on either the primary window or another dialog
window. Content behind a modal dialog is inert, meaning that users cannot
interact with it.
## Resources
[Latest version: v1.35.2](https://www.npmjs.com/package/@zag-js/dialog)
[Logic Visualizer](https://zag-visualizer.vercel.app/dialog)
[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/dialog)
**Features**
- Supports modal and non-modal modes
- Focus is trapped and scrolling is blocked in the modal mode
- Provides screen reader announcements via rendered title and description
- Pressing `Esc` closes the dialog
## Installation
Install the dialog package:
```bash
npm install @zag-js/dialog @zag-js/react
# or
yarn add @zag-js/dialog @zag-js/react
```
## Anatomy
To use the dialog component 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 dialog package:
```jsx
import * as dialog from "@zag-js/dialog"
```
The dialog package exports two key functions:
- `machine` - Behavior logic for the dialog.
- `connect` - Maps behavior 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 dialog from "@zag-js/dialog"
import { useMachine, normalizeProps, Portal } from "@zag-js/react"
export function Dialog() {
const service = useMachine(dialog.machine, { id: "1" })
const api = dialog.connect(service, normalizeProps)
return (
<>
{api.open && (
Edit profile
Make changes to your profile here. Click save when you are done.
)}
>
)
}
```
### Managing focus within the dialog
When the dialog opens, it focuses the first focusable element and keeps keyboard
focus inside the dialog.
To control what receives focus on open, pass `initialFocusEl`.
```jsx {3,6,13}
export function Dialog() {
// initial focused element ref
const inputRef = useRef(null)
const service = useMachine(dialog.machine, {
initialFocusEl: () => inputRef.current,
})
// ...
return (
//...
// ...
)
}
```
To control what receives focus when the dialog closes, pass `finalFocusEl`.
### Dialog vs non-modal dialog
Set `modal` to `false` to allow interaction with content behind the dialog.
```jsx
const service = useMachine(dialog.machine, {
modal: false,
})
```
### Closing the dialog on interaction outside
By default, the dialog closes when you click its overlay. You can set
`closeOnInteractOutside` to `false` if you want the modal to stay visible.
```jsx {2}
const service = useMachine(dialog.machine, {
closeOnInteractOutside: false,
})
```
You can also customize the behavior by passing a function to the
`onInteractOutside` callback and calling `event.preventDefault()`.
```jsx {2-7}
const service = useMachine(dialog.machine, {
onInteractOutside(event) {
const target = event.target
if (target?.closest("")) {
return event.preventDefault()
}
},
})
```
### Listening for open state changes
When the dialog is opened or closed, the `onOpenChange` callback is invoked.
```jsx {2-7}
const service = useMachine(dialog.machine, {
onOpenChange(details) {
// details => { open: boolean }
console.log("open:", details.open)
},
})
```
### Closing with Escape
Set `closeOnEscape` to `false` if the dialog should not close on `Esc`.
```jsx
const service = useMachine(dialog.machine, {
closeOnEscape: false,
})
```
### Controlled dialog
To control the dialog's open state, pass the `open` and `onOpenChange`
properties.
```tsx
import { useState } from "react"
export function ControlledDialog() {
const [open, setOpen] = useState(false)
const service = useMachine(dialog.machine, {
open,
onOpenChange(details) {
setOpen(details.open)
},
})
return (
// ...
)
}
```
### Controlling the scroll behavior
When the dialog is open, it prevents scrolling on the `body` element. To disable
this behavior, set `preventScroll` to `false`.
```jsx {2}
const service = useMachine(dialog.machine, {
preventScroll: false,
})
```
### Creating an alert dialog
The dialog supports both `dialog` and `alertdialog` roles. It uses `dialog` by
default. Set `role` to `alertdialog` for urgent actions.
That's it! Now you have an alert dialog.
```jsx {2}
const service = useMachine(dialog.machine, {
role: "alertdialog",
})
```
> By definition, an alert dialog will contain two or more action buttons. We
> recommend setting focus to the least destructive action via `initialFocusEl`.
### Labeling without a visible title
If you do not render a title element, provide `aria-label`.
```jsx
const service = useMachine(dialog.machine, {
"aria-label": "Delete project",
})
```
## Styling guide
Each part includes a `data-part` attribute you can target in CSS.
```css
[data-part="trigger"] {
/* styles for the trigger element */
}
[data-part="backdrop"] {
/* styles for the backdrop element */
}
[data-part="positioner"] {
/* styles for the positioner element */
}
[data-part="content"] {
/* styles for the content element */
}
[data-part="title"] {
/* styles for the title element */
}
[data-part="description"] {
/* styles for the description element */
}
[data-part="close-trigger"] {
/* styles for the close trigger element */
}
```
### Open and closed state
The dialog has two states: `open` and `closed`. You can use the `data-state`
attribute to style the dialog or trigger based on its state.
```css
[data-part="content"][data-state="open|closed"] {
/* styles for the open state */
}
[data-part="trigger"][data-state="open|closed"] {
/* styles for the open state */
}
```
### Nested dialogs
When dialogs are nested (a dialog opened from within another dialog), the layer
stack automatically applies data attributes to help create visual hierarchy.
- `data-nested` - Applied to nested dialogs
- `data-has-nested` - Applied to dialogs that have nested dialogs open
- `--nested-layer-count` - CSS variable indicating the number of nested dialogs
```css
/* Scale down parent dialogs when they have nested children */
[data-part="content"][data-has-nested] {
transform: scale(calc(1 - var(--nested-layer-count) * 0.05));
transition: transform 0.2s ease-in-out;
}
/* Style nested dialogs differently */
[data-part="content"][data-nested] {
border: 2px solid var(--accent-color);
}
/* Create depth effect using backdrop opacity */
[data-part="backdrop"][data-has-nested] {
opacity: calc(0.4 + var(--nested-layer-count) * 0.1);
}
```
## Methods and Properties
### Machine Context
The dialog machine exposes the following context properties:
**`ids`**
Type: `Partial<{ trigger: string; positioner: string; backdrop: string; content: string; closeTrigger: string; title: string; description: string; }>`
Description: The ids of the elements in the dialog. Useful for composition.
**`trapFocus`**
Type: `boolean`
Description: Whether to trap focus inside the dialog when it's opened
**`preventScroll`**
Type: `boolean`
Description: Whether to prevent scrolling behind the dialog when it's opened
**`modal`**
Type: `boolean`
Description: Whether to prevent pointer interaction outside the element and hide all content below it
**`initialFocusEl`**
Type: `() => HTMLElement`
Description: Element to receive focus when the dialog is opened
**`finalFocusEl`**
Type: `() => HTMLElement`
Description: Element to receive focus when the dialog is closed
**`restoreFocus`**
Type: `boolean`
Description: Whether to restore focus to the element that had focus before the dialog was opened
**`closeOnInteractOutside`**
Type: `boolean`
Description: Whether to close the dialog when the outside is clicked
**`closeOnEscape`**
Type: `boolean`
Description: Whether to close the dialog when the escape key is pressed
**`aria-label`**
Type: `string`
Description: Human readable label for the dialog, in event the dialog title is not rendered
**`role`**
Type: `"dialog" | "alertdialog"`
Description: The dialog's role
**`open`**
Type: `boolean`
Description: The controlled open state of the dialog
**`defaultOpen`**
Type: `boolean`
Description: The initial open state of the dialog when rendered.
Use when you don't need to control the open state of the dialog.
**`onOpenChange`**
Type: `(details: OpenChangeDetails) => void`
Description: Function to call when the dialog's open state changes
**`dir`**
Type: `"ltr" | "rtl"`
Description: The document's text/writing direction.
**`id`**
Type: `string`
Description: The unique identifier of the machine.
**`getRootNode`**
Type: `() => Node | ShadowRoot | Document`
Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
**`onEscapeKeyDown`**
Type: `(event: KeyboardEvent) => void`
Description: Function called when the escape key is pressed
**`onRequestDismiss`**
Type: `(event: LayerDismissEvent) => void`
Description: Function called when this layer is closed due to a parent layer being closed
**`onPointerDownOutside`**
Type: `(event: PointerDownOutsideEvent) => void`
Description: Function called when the pointer is pressed down outside the component
**`onFocusOutside`**
Type: `(event: FocusOutsideEvent) => void`
Description: Function called when the focus is moved outside the component
**`onInteractOutside`**
Type: `(event: InteractOutsideEvent) => void`
Description: Function called when an interaction happens outside the component
**`persistentElements`**
Type: `(() => Element)[]`
Description: Returns the persistent elements that:
- should not have pointer-events disabled
- should not trigger the dismiss event
### Machine API
The dialog `api` exposes the following methods:
**`open`**
Type: `boolean`
Description: Whether the dialog is open
**`setOpen`**
Type: `(open: boolean) => void`
Description: Function to open or close the dialog
### Data Attributes
**`Trigger`**
**`data-scope`**: dialog
**`data-part`**: trigger
**`data-state`**: "open" | "closed"
**`Backdrop`**
**`data-scope`**: dialog
**`data-part`**: backdrop
**`data-state`**: "open" | "closed"
**`Content`**
**`data-scope`**: dialog
**`data-part`**: content
**`data-state`**: "open" | "closed"
**`data-nested`**: dialog
**`data-has-nested`**: dialog
### CSS Variables
## Accessibility
Adheres to the
[Alert and Message Dialogs WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog).
### Keyboard Interactions
**`Enter`**
Description: When focus is on the trigger, opens the dialog.
**`Tab`**
Description: Moves focus to the next focusable element within the content. Focus is trapped within the dialog.
**`Shift + Tab`**
Description: Moves focus to the previous focusable element. Focus is trapped within the dialog.
**`Esc`**
Description: Closes the dialog and moves focus to trigger or the defined final focus element
Editable is an input field used for editing a single line of text. It renders as
static text and transforms into a text input field when the edit interaction is
triggered (click, focus, or double-click).
## Resources
[Latest version: v1.35.2](https://www.npmjs.com/package/@zag-js/editable)
[Logic Visualizer](https://zag-visualizer.vercel.app/editable)
[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/editable)
**Features**
- Use custom controls for the editable
- Pressing `Enter` commits the input value
- Pressing `Esc` reverts the value
- Activate edit mode by double-clicking or focusing on the preview text
- Auto-resize input to fit content
## Installation
Install the editable package:
```bash
npm install @zag-js/editable @zag-js/react
# or
yarn add @zag-js/editable @zag-js/react
```
## Anatomy
Check the editable anatomy and part names.
> Each part includes a `data-part` attribute to help identify them in the DOM.
## Usage
Import the editable package:
```jsx
import * as editable from "@zag-js/editable"
```
The editable package exports two key functions:
- `machine` - Behavior logic for the editable.
- `connect` - Maps behavior to JSX props and event handlers.
> Pass a unique `id` to `useMachine` so generated element ids stay predictable.
Then use the framework integration helpers:
```jsx
import * as editable from "@zag-js/editable"
import { useMachine, normalizeProps } from "@zag-js/react"
export default function Editable() {
const service = useMachine(editable.machine, { id: "1" })
const api = editable.connect(service, normalizeProps)
return (