Accordion

Use an accordion to show and hide sections of related content on a page, helping users find what they need without overwhelming them. Built with Radix UI primitives for full accessibility.

For a single expandable section, use the Details component instead.

Default Accordion

Only one section can be open at a time. Clicking a new section closes the previously open one.

You will need to provide:

  • Proof of identity (passport or driving licence)
  • Proof of address (utility bill or bank statement)
  • Two passport-sized photographs

import * as Accordion from '@radix-ui/react-accordion';
import { ChevronDown } from 'lucide-react';

<Accordion.Root type="single" defaultValue="item-1" collapsible className="radix-accordion">
  <Accordion.Item value="item-1" className="radix-accordion-item">
    <Accordion.Header className="radix-accordion-header">
      <Accordion.Trigger className="radix-accordion-trigger">
        What documents do I need?
        <ChevronDown className="radix-accordion-chevron" size={20} />
      </Accordion.Trigger>
    </Accordion.Header>
    <Accordion.Content className="radix-accordion-content">
      <div className="radix-accordion-content-inner">
        <p>You will need to provide...</p>
      </div>
    </Accordion.Content>
  </Accordion.Item>
  ...
</Accordion.Root>

Accordion vs Details

Use Accordion whenUse Details when
Multiple related sectionsSingle expandable section
Want consistent visual groupingInline with other content
Need "expand all/collapse all"Simple show/hide
FAQ-style contentAdditional context or help text

Multiple Sections Open

Use type="multiple" to allow multiple sections to be open simultaneously.

import * as Accordion from '@radix-ui/react-accordion';

const [openItems, setOpenItems] = useState<string[]>([]);
const allExpanded = openItems.length === items.length;

<button onClick={() => setOpenItems(allExpanded ? [] : items.map(i => i.id))}>
  {allExpanded ? 'Collapse all' : 'Expand all'}
</button>

<Accordion.Root
  type="multiple"
  value={openItems}
  onValueChange={setOpenItems}
  className="radix-accordion"
>
  ...
</Accordion.Root>

Controlled vs Uncontrolled

// Uncontrolled - uses defaultValue
<Accordion.Root type="single" defaultValue="item-1" collapsible>
  ...
</Accordion.Root>

// Controlled - uses value and onValueChange
const [value, setValue] = useState('item-1');

<Accordion.Root type="single" value={value} onValueChange={setValue} collapsible>
  ...
</Accordion.Root>

Accessibility

  • Built with Radix UI primitives for full WAI-ARIA compliance
  • Keyboard navigation: Enter/Space to toggle, Arrow keys to navigate
  • Automatic ARIA attributes (aria-expanded, aria-controls)
  • Focus management between accordion items
  • Screen reader announces expanded/collapsed state

CSS Classes

/* Radix Accordion */
.radix-accordion { }
.radix-accordion-item { }
.radix-accordion-header { }
.radix-accordion-trigger { }
.radix-accordion-chevron { }
.radix-accordion-content { }
.radix-accordion-content-inner { }

/* Controls */
.accordion-controls { }

Installation

npm install @radix-ui/react-accordion lucide-react