Skip to Content
ComponentsAccordion

Accordion

Single or multiple, with optional collapsible mode when type="single".

Anatomy

import { Accordion } from "@spacing-ui/core"; <Accordion type="single" collapsible defaultValue="item-1"> <Accordion.Item value="item-1"> <Accordion.Trigger>Question 1</Accordion.Trigger> <Accordion.Content>Answer 1</Accordion.Content> </Accordion.Item> <Accordion.Item value="item-2"> <Accordion.Trigger>Question 2</Accordion.Trigger> <Accordion.Content>Answer 2</Accordion.Content> </Accordion.Item> </Accordion>;

API

Accordion (Root)

PropTypeDescription
type"single" | "multiple"Required
value / defaultValuestring (single) or string[] (multiple)
onValueChangecallback matching value type
collapsibleboolean (single only)Allow closing the open item
disabledboolean

Accessibility

  • Trigger has aria-expanded, aria-controls
  • Content has role="region" and aria-labelledby pointing to its trigger
  • Keyboard: ArrowUp/Down, Home, End between triggers

Examples

Multiple open at once

<Accordion type="multiple" defaultValue={["a", "c"]}> <Accordion.Item value="a"> <Accordion.Trigger>Section A</Accordion.Trigger> <Accordion.Content>...</Accordion.Content> </Accordion.Item> <Accordion.Item value="b"> <Accordion.Trigger>Section B</Accordion.Trigger> <Accordion.Content>...</Accordion.Content> </Accordion.Item> <Accordion.Item value="c"> <Accordion.Trigger>Section C</Accordion.Trigger> <Accordion.Content>...</Accordion.Content> </Accordion.Item> </Accordion>

Composing an FAQ list

Wrap Accordion.Item in your own component to standardize the trigger and content shape:

function Faq({ q, a, value }: { q: string; a: string; value: string }) { return ( <Accordion.Item value={value}> <Accordion.Trigger>{q}</Accordion.Trigger> <Accordion.Content>{a}</Accordion.Content> </Accordion.Item> ); }

Live demo

Yes — behavior and a11y only. You control the look.

Testing

With @testing-library/react, target triggers by role and assert on aria-expanded:

import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; test("expands the clicked item", async () => { render(<MyAccordion />); const trigger = screen.getByRole("button", { name: /section a/i }); await userEvent.click(trigger); expect(trigger).toHaveAttribute("aria-expanded", "true"); });
Last updated on