Browse docs
Browse docs
Reveal and hide sections of content. Keyboard-accessible by default.
import { Accordion } from '@dashforge/tw';
<Accordion
type="single"
defaultValue="q1"
items={[
{ value: 'q1', header: 'How long does shipping take?', content: '1 business day.' },
{ value: 'q2', header: 'What is the return policy?', content: '30 days, full refund.' },
{ value: 'q3', header: 'How do I contact support?', content: '[email protected].' },
]}
/><Accordion
type="multiple"
defaultValue={['profile', 'notifications']}
items={[
{ value: 'profile', header: 'Profile', content: <ProfileForm /> },
{ value: 'notifications', header: 'Notifications', content: <NotificationsForm /> },
{ value: 'security', header: 'Security', content: <SecurityForm /> },
{ value: 'billing', header: 'Billing', content: <BillingForm /> },
]}
/>type="single" (default): at most one section open at a time. defaultValue seeds which section is open on mount; collapsible (default true) lets the user close the open section by clicking its header again.
import { Accordion } from '@dashforge/tw';
<Accordion
type="single"
defaultValue="shipping"
items={[
{ value: 'shipping', header: 'How long does shipping take?', content: '…' },
{ value: 'returns', header: 'What is the return policy?', content: '…' },
{ value: 'support', header: 'How do I contact support?', content: '…' },
]}
/>type="multiple": any number of sections open at once. defaultValue is a string[] of item ids to open on mount. Useful for settings groups where the user should see several categories at once.
import { Accordion } from '@dashforge/tw';
<Accordion
type="multiple"
defaultValue={['profile', 'notifications']}
items={[
{ value: 'profile', header: 'Profile', content: '…' },
{ value: 'notifications', header: 'Notifications', content: '…' },
{ value: 'security', header: 'Security', content: '…' },
{ value: 'billing', header: 'Billing', content: '…' },
]}
/>Pass value + onValueChange to own the open state externally. Useful for syncing with URL params, telemetry, or a global state store.
import { useState } from 'react';
import { Accordion, Chip, Stack } from '@dashforge/tw';
function ControlledFAQ() {
const [open, setOpen] = useState<string>('overview');
return (
<Stack gap={3}>
<Chip
variant="soft"
label={<>Open section: <strong>{open || '(none)'}</strong></>}
/>
<Accordion
type="single"
value={open}
onValueChange={setOpen}
collapsible
items={[
{ value: 'overview', header: 'Overview', content: '…' },
{ value: 'when-to-use', header: 'When to use controlled mode', content: '…' },
{ value: 'when-to-avoid', header: 'When to avoid it', content: '…' },
]}
/>
</Stack>
);
}Mark an item disabled: true to render it non-interactive. The trigger stays visible with reduced opacity; keyboard focus skips it.
<Accordion
type="single"
items={[
{ value: 'active', header: 'Active section', content: '…' },
{ value: 'locked', header: 'Locked section', content: 'You cannot open this.', disabled: true },
{ value: 'another', header: 'Another section', content: '…' },
]}
/>Every visual layer (root, item, header, trigger, content, chevron) accepts a className via slotProps. Use for one-off surface tweaks without forking the component.
<Accordion
type="single"
items={items}
slotProps={{
item: { className: 'border-neutral-300' },
trigger: { className: 'text-sm font-semibold uppercase tracking-wide' },
content: { className: 'text-neutral-600' },
chevron: { className: 'text-primary-500' },
}}
/>For a root-level utility shortcut, sx is equivalent to slotProps.root.className.
<Accordion> — single mode| Prop | Type | Default | Description |
|---|---|---|---|
type | 'single' | 'single' | At most one section open at a time. |
items | AccordionItem[] | (required) | The sections to render. See AccordionItem below. |
value | string | — | Controlled open item. Pair with onValueChange. |
defaultValue | string | — | Uncontrolled initial open item. |
onValueChange | (value: string) => void | — | Callback fired when the open item changes. |
collapsible | boolean | true | Allow closing the open section by clicking its header again. |
sx | string | — | Root className shortcut (same as slotProps.root.className). |
slotProps | AccordionSlotProps | — | Per-slot className overrides. See below. |
<Accordion> — multiple mode| Prop | Type | Default | Description |
|---|---|---|---|
type | 'multiple' | (required) | Any number of sections open simultaneously. |
items | AccordionItem[] | (required) | The sections to render. |
value | string[] | — | Controlled open items. Pair with onValueChange. |
defaultValue | string[] | — | Uncontrolled initial open items. |
onValueChange | (value: string[]) => void | — | Callback fired when the open set changes. |
sx | string | — | Root className shortcut. |
slotProps | AccordionSlotProps | — | Per-slot className overrides. |
AccordionItemEach entry in the items array.
| Field | Type | Default | Description |
|---|---|---|---|
value | string | (required) | Canonical id used in value / defaultValue. Must be unique within items. |
header | ReactNode | (required) | Trigger label (clickable to expand / collapse). |
content | ReactNode | (required) | Section body, revealed when this item is expanded. |
disabled | boolean | false | Skip focus and prevent expansion for this item. |
AccordionSlotPropsslotProps accepts a className for each visual slot:
| Slot | Description |
|---|---|
root | The outer accordion container. |
item | Each item wrapper (bordered row). |
header | The row that holds trigger + chevron. |
trigger | The clickable button. |
content | The revealed panel body. |
chevron | The rotating arrow icon at the trigger end. |
role="region"; each trigger is a <button> with aria-expanded and aria-controls linked to its panel.Space / Enter toggle the focused section, ↓ / ↑ move focus between triggers, Home / End jump to the first / last trigger. Tab moves out of the accordion once a trigger is focused.data-state=open — no JS state, no layout thrash. Respects prefers-reduced-motion when the theme's motion tokens do.value is just string or string[]) and makes list rendering from a data source a one-liner. The trade-off is less flexibility for exotic layouts inside a single item — for that, escape to slotProps.item or render a compound child inside content.@radix-ui/react-accordion — no Dashforge-specific behavior to remember. Everything documented in the Radix Accordion spec applies here through the slotProps layer.items array order is the tab / arrow-key order in the rendered accordion — order matters for a11y, not just for visuals.