Accordion
Fold questions, settings, and details into sections that open in place, one at a time or all together.
Installation
You can add the accordion using our CLI or manually:
Dark mode ready - this component follows BoardUI semantic tokens automatically. Set up theme switching.
The CLI copies the component source (with its internal dependencies) into your project and installs any required npm packages.
npx boardui@latest add accordionUsing a shared UI package or custom import aliases? Configure your installation paths before running the CLI. Copy-paste examples use the default imports.
Anatomy
Accordion groups the sections and AccordionItem is one section: a heading with a button that opens its panel. Give every item an id so you can open it from defaultExpandedKeys or control it.
The panel eases open while its content fades in, settles a few pixels, and sharpens from a soft blur. A closed panel stays in the page, hidden until found, so the browser's find-in-page still reaches its text and opens it.
Contained
variant="contained" sets each item on a tray, the look of the FAQ on the BoardUI home page. indicator="plus" swaps the chevron for a plus that turns into a close.
<Accordion variant="contained" indicator="plus" defaultExpandedKeys={["plans"]}> <AccordionItem id="billing" title="How does billing work?">…</AccordionItem> <AccordionItem id="plans" title="Can I change plans later?">…</AccordionItem> <AccordionItem id="data" title="What happens to my data if I cancel?">…</AccordionItem></Accordion>Separated, with icons
variant="separated" gives every item its own card. Add an icon and a description for settings groups, and allowsMultipleExpanded to let several stay open. isDisabled locks an item.
import { RiBankCardLine, RiNotification3Line, RiShieldKeyholeLine } from "@remixicon/react";
<Accordion variant="separated" allowsMultipleExpanded defaultExpandedKeys={["notifications"]}> <AccordionItem id="notifications" icon={RiNotification3Line} title="Notifications" description="Email, push, and Slack"> <Switch defaultSelected>Email me about mentions</Switch> … </AccordionItem> <AccordionItem id="security" icon={RiShieldKeyholeLine} title="Security" description="Two-factor sign-in and sessions">…</AccordionItem> <AccordionItem id="billing" icon={RiBankCardLine} title="Billing" description="Managed by your workspace owner" isDisabled>…</AccordionItem></Accordion>Controlled
Keep the open sections in your own state with expandedKeys and onExpandedChange, for example to expand or collapse them all at once.
const [expanded, setExpanded] = useState<Set<Key>>(new Set(["billing"]));
<Button variant="secondary" size="small" onClick={() => setExpanded(new Set(ids))}>Expand all</Button><Button variant="secondary" size="small" onClick={() => setExpanded(new Set())}>Collapse all</Button>
<Accordion allowsMultipleExpanded expandedKeys={expanded} onExpandedChange={setExpanded}> {faq.map((item) => ( <AccordionItem key={item.id} id={item.id} title={item.question}> <p>{item.answer}</p> </AccordionItem> ))}</Accordion>Right-to-left
Wrap the accordion in DirectionProvider. Titles align to the right and the indicator moves to the left edge.
import { DirectionProvider } from "@/components/foundations/direction/direction";
<DirectionProvider locale="ar"> <Accordion defaultExpandedKeys={["billing"]}> <AccordionItem id="billing" title="كيف تعمل الفوترة؟">…</AccordionItem> <AccordionItem id="plans" title="هل يمكنني تغيير الخطة لاحقًا؟">…</AccordionItem> </Accordion></DirectionProvider>Keyboard and accessibility
Each header is a button inside a heading (level 3 by default, set with headingLevel). Tab moves between headers, and Enter or Space opens and closes a section. Screen readers hear whether it is expanded and which panel it controls.
Reduced motion opens and closes sections instantly.