Accordion

A set of stacked sections where each is toggled by its own heading button, from @clerk/headless. It is a headless primitive: it supplies behavior, single/multiple open state, roving-focus keyboard navigation, ARIA wiring, and the expand/collapse animation lifecycle, but ships no styles — you bring your own CSS by targeting the data-* attributes each part emits.

Example

The demo below is intentionally unstyled — it renders the raw primitive so you can see its behavior and ARIA wiring. Click a trigger to open its panel; in single mode opening one closes the others, and arrow keys move focus between triggers.

A headless component provides behavior, state management, and accessibility without imposing any styles — you bring your own classNames and target the data-* state attributes it emits.

Usage

import { Accordion } from '@clerk/headless/accordion';

<Accordion.Root>
  <Accordion.Item value='item-1'>
    <Accordion.Header>
      <Accordion.Trigger>Section 1</Accordion.Trigger>
    </Accordion.Header>
    <Accordion.Panel>Content for section 1</Accordion.Panel>
  </Accordion.Item>
  <Accordion.Item value='item-2'>
    <Accordion.Header>
      <Accordion.Trigger>Section 2</Accordion.Trigger>
    </Accordion.Header>
    <Accordion.Panel>Content for section 2</Accordion.Panel>
  </Accordion.Item>
</Accordion.Root>;

Single (one item open at a time)

<Accordion.Root type='single'>{/* items */}</Accordion.Root>

Controlled

const [value, setValue] = useState<string[]>(['item-1']);

<Accordion.Root
  value={value}
  onValueChange={setValue}
>
  {/* items */}
</Accordion.Root>;

Parts

PartDefault ElementDescription
Accordion.Root<div>Root wrapper; owns open-item state and Home/End/arrow nav
Accordion.Item<div>Wraps one section; provides item context (value, open, IDs)
Accordion.Header<h3>Heading wrapper for the trigger
Accordion.Trigger<button>Clickable toggle for its item's panel
Accordion.Panel<div>Collapsible content region for its item

All parts accept a render prop for polymorphic rendering and standard HTML attributes for their default element. Compound parts throw if used outside their parent (Accordion.* outside Root; Trigger/Header/Panel outside Item).

Props

Accordion.Root

PropTypeDefaultDescription
type'single' | 'multiple''multiple'single keeps at most one item open at a time
valuestring[]Controlled list of open item values
defaultValuestring[][]Initially open item values (uncontrolled)
onValueChange(value: string[]) => voidCalled when the set of open items changes
disabledbooleanfalseDisable every item

Accordion.Item

PropTypeDefaultDescription
valuestring— (required)Unique value identifying this item
disabledbooleaninherits RootDisable just this item

Accordion.Header, Accordion.Trigger, and Accordion.Panel take no additional props beyond standard HTML attributes for their default element.

Styling

Each part emits data-* attributes you can target with any CSS solution:

AttributeApplies ToDescription
data-openItem, Trigger, PanelPresent when the item is open
data-closedItem, Trigger, PanelPresent when the item is closed
data-disabledItem, TriggerPresent when the item is disabled
data-starting-stylePanelPresent on the entering frame of the open animation
data-ending-stylePanelPresent during the exit animation before unmount

Accordion.Panel exposes --cl-accordion-panel-height (its measured content height) for height-based animations:

.cl-accordion-panel {
  overflow: hidden;
  height: var(--cl-accordion-panel-height);
  transition: height 200ms ease;
}
.cl-accordion-panel[data-closed] {
  height: 0;
}