Accordion
StableA stack of headings that each reveal a section of content — progressive disclosure for secondary information.
Anatomy
- 1TriggerA button inside a heading that names the section. The whole row toggles it.
- 2ItemOne section: a trigger and its panel. Hairlines separate the items.
- 3IconPoints down while the section is closed and turns over when it opens.
- 4PanelThe section's content. It animates its height open and closed, and stays out of the tab order while closed.
Installation
$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/accordion.jsonUsage
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion"<Accordion defaultValue={["shipping"]}>
<AccordionItem value="shipping">
<AccordionTrigger>How long does shipping take?</AccordionTrigger>
<AccordionContent>Orders ship within one business day.</AccordionContent>
</AccordionItem>
</Accordion>defaultValue and value are always arrays of item values — even when only one item can be open. Give every item a stable value so its state survives re-renders and can be controlled.
Examples
FAQ
The classic use: many questions, few of them relevant to any one reader. One item opens at a time by default, so the list stays short and scannable as people move between answers.
Frequently asked questions
Everything about plans, billing and your data.
Multiple open
Set multiple when people need to compare or work across sections at the same time — filters, settings, grouped form options. Here two filter groups start open.
With icons and summaries
Triggers accept any inline content. A leading icon and a one-line summary let people decide whether to open a section without opening it. Indent the panel (className on AccordionContent styles its inner wrapper) so body text aligns with the title.
Disabled item
Disable an item that exists but isn't available, and say why in the label. The trigger stays visible, is announced as disabled and can't be toggled.
Controlled
Control value to open sections from elsewhere — Expand all, deep links, or restoring what someone had open. onValueChange receives the next array of open values.
prfct command to your path.const [value, setValue] = React.useState<string[]>(["install"])
<Accordion multiple value={value} onValueChange={setValue}>
…
</Accordion>Motion
Panels animate their real height with the base duration and standard easing, and fade their content in the same beat; the chevron rotates in step. Nothing is scaled or clipped, so text never smears. With prefers-reduced-motion, sections open instantly.
Guidelines
When to use
- To shorten long pages whose sections are relevant to only some readers: FAQs, product details, release notes.
- To group related settings or filters that people revisit selectively.
- On small screens, to keep secondary content one tap away instead of scrolled past.
When not to use
- For content everyone needs. Hiding it costs a click and hurts findability — show it.
- For a single show/hide region — use a Collapsible.
- To switch between views of the same data — use Tabs.
- For navigation — use a Sidebar or Navigation Menu.
Write triggers people can scan
Triggers are headings. Keep them short, front-load the keyword, and make them mutually exclusive so people can tell which one holds their answer. In an FAQ, phrase them as the question the reader would ask.
Keep it flat
Don't nest accordions. A second level of disclosure hides content behind two clicks and makes the open/closed state hard to follow. If the content needs hierarchy, give it its own page.
Never hide what decides the task
Prices, errors, required fields and anything that changes a decision must stay visible. An accordion is for supporting content; if closing a section could make someone miss something important, it shouldn't be in one.
Accessibility
Each trigger is a <button> inside an <h3>, with aria-expanded and aria-controls pointing at its panel. Panels have role="region" and are labelled by their trigger, so screen readers announce which section they're in.
| Key | Behavior |
|---|---|
Tab | Moves focus to the next trigger, or into the open panel's focusable content. |
ShiftTab | Moves focus to the previous trigger or focusable element. |
EnterSpace | Expands or collapses the focused section. |
- No arrow-key navigation. Following the 2025 update to the WAI-ARIA Authoring Practices, accordion headers are ordinary tab stops; arrow keys are not intercepted.
- Heading level. Triggers sit in an
<h3>. If that breaks your page outline, change therenderofAccordionPrimitive.Headerin your copy of the component (for examplerender={<h2 />}). - Find in page. Set
hiddenUntilFoundon the root (or a panel) and the browser's find-in-page can search closed sections and open them on a match. - Disabled items stay in the reading order and are announced as disabled, so people know the section exists.
API reference
Accordion
Groups the items and manages which are open. Renders a <div>. Accepts every prop of Base UI Accordion.Root.
AccordionItem
One heading and its panel. Renders a <div>.
AccordionTrigger
The button that toggles its item, wrapped in an <h3>. Includes the rotating chevron. Accepts every prop of Base UI Accordion.Trigger.
AccordionContent
The collapsible panel. className applies to the inner content wrapper (padding, typography), while the outer panel handles the height animation. Accepts every prop of Base UI Accordion.Panel, including hiddenUntilFound and keepMounted.