Mosmox UI
Current version
GitHubnpmjs.com
Navigation & Selection

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:

  • default renders rounded, separated rows with a subtle open-state border and lightBackground open/hover surface.
  • separated gives each item its own rounded card.
  • plain uses 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

PropTypeDefaultDescription
itemsAccordionItem[]—Sections to render.
valuestring | string[]—Controlled open key or keys.
defaultValuestring | string[]—Initial open key or keys.
onChange(value, item) => void—Called after an item toggles.
multiplebooleanfalseAllows multiple sections to stay open.
collapsiblebooleantrueAllows the open section to close.
sizesm | default | lgdefaultHeader and content density.
variantdefault | separated | plaindefaultVisual treatment.
iconPositionstart | endendPosition of the expand control.
lazybooleanfalseDefers closed panel content until opened.
aria-labelstringAccordionAccessible label for the group.
classNamestring—Additional root class.
styleCSSProperties—Inline root styles.

AccordionItem

PropertyTypeDescription
keystringUnique item identifier.
labelReactNodeHeader content.
contentReactNodeExpandable panel content.
descriptionReactNodeOptional supporting text in the header.
iconReactNodeOptional leading icon.
badgeReactNodeOptional trailing metadata.
disabledbooleanPrevents 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.