← Back to site

Accordion

The Accordion component renders collapsible sections with rich content. Item content is written in Markdown and processed server-side to HTML via markdownToHtml().

Import

import Accordion from '@mdx/Accordion.astro';

Props

PropTypeDefaultDescription
itemsArray<{title, subtitle?, content}>Array of accordion items. content accepts Markdown.
mode'single-closed' | 'single-open' | 'always-one' | 'multi'Preset that controls the open/close behaviour. Overrides the three legacy props below. Preferred API.
defaultOpennumber | nullnull(Legacy.) Index of initially open item
allowCloseAllbooleanAuto-derived(Legacy.) Whether an open item can be collapsed. Defaults to true when defaultOpen is null.
allowMultiplebooleanfalse(Legacy.) Allow multiple items open simultaneously
widthstring"100%"CSS width of the accordion container

Mode presets

modedefaultOpenallowCloseAllallowMultipleBehaviour
single-closednulltruefalseAll start closed; clicking one closes any other; can close all.
single-open0truefalseFirst starts open; only one open at a time; can close all.
always-one0falsefalseFirst starts open; only one open at a time; cannot close all (always exactly one open).
multinulltruetrueAll start closed; multiple can be open simultaneously.

The legacy props are kept on the Astro component for backwards compatibility with hand-authored MDX, but the Keystatic schema only exposes mode. New content should use mode exclusively.

Live Examples

Default (all closed)

Content of the first section with Markdown support.

Content with a link and inline code.

  • List item one
  • List item two
  • List item three

With default open and subtitle

Subtitle text

This item starts open.

This item starts closed.

Implementation Notes

Markdown processing: Each item’s content string is processed at build time via markdownToHtml() from src/utils/markdown.ts. This uses the unified/remark/rehype pipeline with GitHub Flavored Markdown support (tables, strikethrough, task lists). Raw HTML is allowed but sanitized via rehype-sanitize<script>, inline event handlers, and javascript: URLs are stripped before render.

Alpine.js state:

  • activeIndex — tracks the single open item (single mode)
  • openIndices — array of open item indices (multiple mode)
  • _reducedMotion — checks prefers-reduced-motion and skips animation if set

Animation:

  • Open: 250ms ease-out height transition
  • Close: 200ms ease-in height transition
  • After opening, height is set to auto (via 260ms timeout) so content can resize naturally

Source: src/components/mdx/Accordion.astro