Skip to main content
View Zag.js on Github
Join the Discord server


An accordion is a vertically stacked set of interactive headings containing a title, content snippet, or thumbnail representing a section of content.

Sample accordion content


  • Full keyboard navigation.
  • Can expand one or multiple items.
  • Collapse each accordion item.


To use the accordion machine in your project, run the following command in your command line:

npm install @zag-js/accordion @zag-js/vue # or yarn add @zag-js/accordion @zag-js/vue

This command will install the framework agnostic accordion logic and the reactive utilities for your framework of choice.


To set up the accordion 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.

On a high level, the accordion consists of:

  • Root: The root container for the accordion
  • Item: The container for each accordion item

For each accordion item, it consists of:

  • Trigger: The trigger for the accordion item
  • Content: The content area that is revealed when the trigger is clicked


First, import the accordion package into your project

import * as accordion from "@zag-js/accordion"

The accordion package exports two key functions:

  • machine — The state machine logic for the accordion widget.
  • connect — The function that translates the machine's state to JSX attributes and event handlers.

You'll also need to provide a unique id to the useMachine hook. This is used to ensure that every part has a unique identifier.

Next, import the required hooks and functions for your framework and use the accordion machine in your project 🔥

<script setup> import * as accordion from "@zag-js/accordion" import { normalizeProps, useMachine } from "@zag-js/vue" import { computed } from "vue" const data = [ { title: "Watercraft", content: "Sample accordion content" }, { title: "Automobiles", content: "Sample accordion content" }, { title: "Aircrafts", content: "Sample accordion content" }, ] const [state, send] = useMachine(accordion.machine({ id: "1" })) const api = computed(() => accordion.connect(state.value, send, normalizeProps)) </script> <template> <div ref="ref" v-bind="api.rootProps"> <div v-for="item in data" :key="" v-bind="api.getItemProps({ value: item.title })" > <h3> <button v-bind="api.getTriggerProps({ value: item.title })"> {{ item.title }} </button> </h3> <div v-bind="api.getContentProps({ value: item.title })"> {{ item.content }} </div> </div> </div> </template>

You may have noticed we wrapped each accordion trigger within an h3. This is recommended by the WAI-ARIA design pattern to ensure the accordion has the appropriate hierarchy on the page.

Opening multiple accordions at once

To allow multiple items to be expanded at once, set multiple to true. This mode implicitly sets collapsible to true and ensures that each accordion can be expanded.

const [state, send] = useMachine( accordion.machine({ multiple: true, }), )

Opening specific accordions

To set the value of the accordion(s) that should be opened initially, pass the value property to the machine function.

// for multiple accordions const [state, send] = useMachine( accordion.machine({ multiple: true, value: ["home"], }), ) // for single accordions const [state, send] = useMachine( accordion.machine({ value: "home", }), )

Toggle each accordion item

To collapse an already expanded accordion item by clicking on it, set the context's collapsible property to true.

Note: If multiple is true, we internally set collapsible to be true.

const [state, send] = useMachine( accordion.machine({ collapsible: true, }), )

Disabling specific accordion items

To disable a specific accordion, pass the disabled: true property to the getItemProps, getTriggerProps and getContentProps.

When an accordion item is disabled, it is skipped from keyboard navigation and can't be interacted with.

//... <div {...api.getItemProps({ value: "item", disabled: true })}> <h3> <button {...api.getTriggerProps({ value: "item", disabled: true })}> Trigger </button> </h3> <div {...api.getContentProps({ value: "item", disabled: true })}>Content</div> </div> //...

You can also disable the entire accordion items by passing disabled to the machine's context.

const [state, send] = useMachine( accordion.machine({ disabled: true, }), )

Styling guide

Earlier, we mentioned that each accordion part has a data-part attribute added to them to select and style them in the DOM.

Selected state

When an accordion item is expanded, a data-expanded attribute is set on the item, trigger and content elements. This attribute is removed when it is closed.

[data-part="item"][data-expanded] { /* styles for the item's expanded state */ } [data-part="trigger"][data-expanded] { /* styles for the trigger's expanded state */ } [data-part="content"][data-expanded] { /* styles for the content's expanded state */ }

Focused state

When an accordion item's trigger is focused, a data-focused attribute is set on the item and content.

[data-part="item"][data-focus] { /* styles for the item's focus state */ } [data-part="trigger"]:focus { /* styles for the trigger's focus state */ } [data-part="content"][data-focus] { /* styles for the content's focus state */ }

Methods and Properties

The accordion's api exposes the following methods and properties:

  • focusedValuestringThe value of the focused accordion item.
  • valuestring | string[]The value of the accordion.
  • setValue(value: string | string[]) => voidSets the value of the accordion.
  • getItemState(props: ItemProps) => { isOpen: boolean; isFocused: boolean; isDisabled: boolean; }Gets the state of an accordion item.

Edit this page on GitHub

On this page