Lists

Accordion

A stack of titles that each open their own content, for FAQs and long settings pages. One item opens at a time unless you pass multiple. Panels grow to their measured height with CSS, titles sit inside headings, closed content is in the server HTML and found by the page search of browsers that support hidden="until-found", and rows are 44px tall on touch screens.

Three days in the fridge, in a covered container.

Install

Once per app, with Tailwind CSS v4 and an @/* path alias (Ouro HX is built on Base UI, so --base base; skip -t in an existing app):

npx shadcn@latest init --base base --preset nova --yes --no-monorepo
npx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.2.13

Then the component. From v0.8.0 it installs only the helpers it imports and never touches your stylesheet (Installing):

npx shadcn@latest add ourostack/ouro-ui/accordion#v1.2.13

Or, with the @ouro namespace set up:

npx shadcn@latest add @ouro/ouro-base @ouro/accordion

Its tests, stories and Playwright spec are optional. They need Vitest 4.1.11 or later, and from v0.8.0 never change a version your app has (Testing):

npx shadcn@latest add ourostack/ouro-ui/accordion#v1.2.13 ourostack/ouro-ui/accordion-tests#v1.2.13
import { Accordion } from "@/components/ouro/accordion/accordion";

Paths use shadcn's default @/ alias; the CLI rewrites them to yours.

Example

<Accordion
  items={[
    { value: "storage", title: "How long does it keep?", content: "Three days in the fridge." },
    { value: "freeze", title: "Can I freeze it?", content: "Yes, for up to a month." },
  ]}
/>
<Accordion multiple defaultValue={["storage"]} items={items} />

Use instead when

  • Use Collapsible instead for a single section that opens and closes.
  • Use Tabs instead when exactly one section always shows and the titles fit in a row.

Props

The props the modes share. Type: AccordionProps.

className
Type: string
Layout only: margins, width, grid placement (mt-6). Appearance classes warn in development.
disabled
Type: boolean
Keeps every item from opening or closing.
headingLevel
Type: 2 | 3 | 4 | 5 | 6
Default: 3
The outline level of each title, so the items sit in the page's heading structure: headingLevel={2} renders <h2>.
items (required)
Type: readonly AccordionItem[]
The items, in order.
ref
Type: Ref<HTMLDivElement>
Reaches the rendered element.

Modes

AccordionProps is a union: pass the props of exactly one mode. Mixing them is a type error.

Multiple: AccordionMultipleProps

Several items open at once. The value is the list of open items.

defaultValue
Type: readonly string[]
Default: []
The items open first, when the component keeps its own state.
multiple (required)
Type: true
Lets several items be open at once, and makes the value a list.
onValueChange
Type: ((value: string[]) => void)
Called with the open items' values whenever one opens or closes.
value
Type: readonly string[]
The open items' values, when you control them. Pair it with onValueChange.

Single: AccordionSingleProps

One item open at a time (the default). The value is the open item's, or null when all are closed.

defaultValue
Type: string | null
Default: none
The item open first, when the component keeps its own state.
multiple
Type: false
Lets several items be open at once, and makes the value a list.
onValueChange
Type: ((value: string | null) => void)
Called with the open item's value, or null when the person closes it.
value
Type: string | null
The open item's value, or null for none, when you control it. Pair it with onValueChange.

Don't use

These are not props. Passing one fails the type check with the fix in the error, and in development a console message names the fix.

  • collapsible: Not a prop. An open item always closes when its title is pressed again.
  • onChange: Not a prop. Ouro UI uses onValueChange: <Accordion onValueChange={(value) => …}>.
  • type: Not a prop. One item opens at a time by default; for several, pass multiple: <Accordion multiple>.

className is for layout only

Pass margins, width and grid placement (mt-4, w-full, col-span-2). Your classes always win over Ouro UI's: Accordion merges className last with cn (tailwind-merge), so a class that conflicts with its own replaces it.layout-classes

Appearance comes from props, not classes. In development, a class that sets appearance logs one warning per component instance, naming the prop to use instead. A class sets appearance when, after its variants (hover:, sm:) and any ! are removed, it starts with bg-, text-, border-, rounded-, shadow-, font-, ring-, from-, via-, to-, fill- or stroke-, or is border, rounded, shadow or ring, or has a dark: variant. Text wrapping, overflow and table layout classes (text-wrap, text-ellipsis, border-collapse) are layout and do not warn.

Alignment classes (text-center) warn too: set alignment on a parent element.

Types

AccordionItem

One item: a title that opens and closes its content.

interface AccordionItem {
  /**
   * Identifies the item: `value`, `defaultValue` and `onValueChange` use it. Unique within the
   * accordion. @default the item's index, as a string ("0", "1", …)
   */
  value?: string | undefined;
  /**
   * The title, always visible. It is the button that opens the item, inside a heading.
   */
  title: ReactNode;
  /** What shows while the item is open. */
  content: ReactNode;
  /** Shows the item but keeps it from opening or closing. */
  disabled?: boolean | undefined;
}

Claim sources