Accordion
Expandable sections for progressively revealing related content.
Accordion groups related content into expandable sections. It is useful for FAQs, settings, filters, summaries, and any interface where users should control how much detail is visible at once.
Basic usage
Pass an array of items. Each item needs a unique key, a visible label, and the content rendered when it is open.
import { Accordion } from '@mosmox/ui';
const items = [
{ key: 'shipping', label: 'Shipping', content: <p>Orders ship within two business days.</p> },
{ key: 'returns', label: 'Returns', content: <p>Returns are accepted within 30 days.</p> },
];
<Accordion items={items} defaultValue="shipping" />Single and multiple panels
By default, opening one item closes the previous item. Set multiple when several sections may remain open simultaneously.
<Accordion items={items} />
<Accordion items={items} multiple defaultValue={['shipping', 'returns']} />collapsible controls whether the active item can be closed in single mode. Set it to false when one section must always remain visible.
Controlled state
Use value and onChange when the parent owns the open state. The callback receives a string in single mode and a string array in multiple mode, followed by the item that changed.
const [open, setOpen] = useState<string[]>(['shipping']);
<Accordion
items={items}
multiple
value={open}
onChange={(next) => setOpen(next as string[])}
/>Use defaultValue for an uncontrolled initial state. Use an empty string or array to start collapsed.
Rich item headers
Items support an optional description, icon, badge, and disabled state.
const items = [
{ key: 'security', label: 'Security', description: 'Encryption and access controls', icon: <LockKeyhole size={16} />, badge: 'Verified', content: <SecurityDetails /> },
{ key: 'billing', label: 'Billing', disabled: true, content: <BillingDetails /> },
];
<Accordion items={items} iconPosition="start" />Disabled items cannot be focused or opened and remain visually muted.
Appearance and sizing
variant accepts default, separated, or plain:
defaultrenders rounded, separated rows with a subtle open-state border andlightBackgroundopen/hover surface.separatedgives each item its own rounded card.plainuses lightweight rows and dividers.
size accepts sm, default, and lg. The default header uses a 15px medium-weight label; sm and lg change density and typography. Use iconPosition="start" to place the Mosmox UI CaretDown control before the optional item icon, keeping expand controls aligned in one column.
<Accordion items={items} variant="separated" size="lg" />
<Accordion items={items} variant="plain" size="sm" iconPosition="start" />Tokens and states
The component uses surface for closed headers, lightBackground for hover and open headers, and controlBorder for the regular border. An open default item uses a slightly darker blend of controlBorder and text, while keeping the same neutral tone. The radius comes from --mm-radius-control. Override these values through ConfigProvider.theme.colors and ConfigProvider.theme.radius; no component-specific color values are required.
The open state keeps a complete border on all four sides, including items after the first one. This is useful when the accordion is displayed as a list of independent cards rather than as a single bordered panel.
Lazy content
Set lazy to avoid rendering closed panels until they are opened. This is useful when panel content is expensive to construct.
<Accordion items={items} lazy />Keyboard and accessibility
The component uses native buttons and exposes expanded, control, and region relationships. Users can press Enter or Space to toggle, use arrow keys to move between headings, and use Home/End to jump to the first or last enabled heading.
<Accordion aria-label="Account settings" items={settingsItems} />API
Accordion props
| Prop | Type | Default | Description |
|---|---|---|---|
items | AccordionItem[] | — | Sections to render. |
value | string | string[] | — | Controlled open key or keys. |
defaultValue | string | string[] | — | Initial open key or keys. |
onChange | (value, item) => void | — | Called after an item toggles. |
multiple | boolean | false | Allows multiple sections to stay open. |
collapsible | boolean | true | Allows the open section to close. |
size | sm | default | lg | default | Header and content density. |
variant | default | separated | plain | default | Visual treatment. |
iconPosition | start | end | end | Position of the expand control. |
lazy | boolean | false | Defers closed panel content until opened. |
aria-label | string | Accordion | Accessible label for the group. |
className | string | — | Additional root class. |
style | CSSProperties | — | Inline root styles. |
AccordionItem
| Property | Type | Description |
|---|---|---|
key | string | Unique item identifier. |
label | ReactNode | Header content. |
content | ReactNode | Expandable panel content. |
description | ReactNode | Optional supporting text in the header. |
icon | ReactNode | Optional leading icon. |
badge | ReactNode | Optional trailing metadata. |
disabled | boolean | Prevents interaction. |
label, description, content, icon, and badge accept any ReactNode, so applications can provide localized strings, links, status elements, or custom layout. The built-in expand control is decorative and inherits the component's accessible button name from the item content.
Current showcase examples
The testing showcase demonstrates single-panel behavior, controlled multiple-panel state, lazy rendering, plain and separated variants, all three sizes, disabled items, descriptions, badges, custom icons, and keyboard navigation. The component is now part of the working Navigation & Selection group.