# Accordion

> A React accordion component for FAQs, settings sections and order details, with four variants, icons, descriptions and one or many open.

- Type: React component (Layout)
- Access: Free (the source is open to everyone)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add accordion`
- Docs: https://wingo-ui.com/components/accordion

## Interaction

Click or tap anywhere on a row to open it: the answer grows out on a smooth spring while the rows below slide down, and the chevron flips (or the plus folds into a minus). In single mode opening one row closes the other. With a keyboard, Tab reaches the rows, Up and Down move between them, Home and End jump to the first and last, and Enter or Space toggles. Rows are at least 44px tall on every size and the whole row is the target, so they are easy to hit on a phone. With keepMounted, the browser's find in page opens the row that holds the match. With reduced motion nothing slides: the answer simply fades in and out.

## Installation

```bash
npx wingo-ui@latest add accordion
```

The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add https://wingo-ui.com/r/accordion.json`

Files:

- `components/ui/accordion.tsx`

npm dependencies: `npm i @radix-ui/react-accordion lucide-react motion`

Wingo UI dependencies (installed automatically):

- [useControllableState](https://wingo-ui.com/components/use-controllable-state) ([Markdown](https://wingo-ui.com/components/use-controllable-state.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
import { Accordion, type AccordionItemData } from "@/components/ui/accordion"

const faq: AccordionItemData[] = [
  {
    value: "posting",
    title: "How do I post a listing?",
    content: "Tap Post a listing, pick a category and upload your photos. The listing goes live within minutes.",
  },
  {
    value: "promotion",
    title: "How much does promotion cost?",
    content: "Promotion starts at $9.00 for 7 days, and the listing moves to the top results.",
  },
  {
    value: "returns",
    title: "Can I return an item?",
    content: "Yes, you have 14 days from delivery to return an item bought with delivery through the platform.",
  },
]

export function Faq() {
  return <Accordion items={faq} defaultValue="promotion" variant="separated" />
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `items` | `AccordionItemData[]` |  | The rows: { value, title, description?, icon?, extra?, content, disabled? }. value must be unique (a duplicate replaces the earlier row); extra is a short value before the indicator, like a price or a count. An empty list renders nothing. |
| `type?` | `"single" \| "multiple"` | `"single"` | single keeps one row open at a time (value is a string); multiple lets any number stay open (value is a string[]). |
| `collapsible?` | `boolean` | `true` | single type only: the open row closes again when clicked. Turn it off for sections where one must always stay open. |
| `keepMounted?` | `boolean` | `false` | Keeps closed answers in the DOM (collapsed, hidden until found), so form fields keep their values, search engines read every answer and the browser's find in page opens the row that holds the match. |
| `headingLevel?` | `2 \| 3 \| 4 \| 5 \| 6` | `3` | The heading level that wraps each row, so the accordion fits the page outline for screen readers. |
| `variant?` | `"ghost" \| "outline" \| "soft" \| "separated"` | `"ghost"` | ghost is flush with hairlines between rows, outline and soft wrap the rows in one container, separated turns every row into its own raised card. |
| `size?` | `"sm" \| "md" \| "lg"` | `"md"` | Row height 44 / 56 / 64px with title 14 / 15 / 16px and content 13 / 14 / 15px. Even sm stays a full 44px touch target. |
| `radius?` | `"none" \| "sm" \| "md" \| "lg" \| "full"` | `"md"` | Corners of the outline and soft containers and of separated cards: 12 / 16 / 24px. On ghost rows it shapes the focus ring. |
| `indicator?` | `"chevron" \| "plus" \| "none"` | `"chevron"` | The open / closed mark. The chevron turns, the plus folds into a minus; none hides it. |
| `indicatorPosition?` | `"start" \| "end"` | `"end"` | end puts the mark after the title. start puts it first, as a chevron that points at the title and turns down like a disclosure triangle; the content indents to the title. |
| `tone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | Tints the open row's icon tile and indicator. |
| `color?` | `string` | `""` | Any CSS color for the open row's tint, mixed for contrast in both themes. Leave empty to use the tone. |
| `divided?` | `boolean` | `true` | Hairlines between rows. soft insets them to the row padding like a grouped list; separated cards ignore it. |
| `disabled?` | `boolean` | `false` | Disables every row. Disable a single row with disabled on its item. |
| `value?` | `string \| string[]` |  | The open row (single) or rows (multiple), to control the accordion from outside. |
| `defaultValue?` | `string \| string[]` |  | The open row or rows on first render. Nothing is open by default ("" or []). |
| `onValueChange?` | `(value: string \| string[]) => void` |  | Called with the new value: a string for single ("" when the row closes), a string[] for multiple. |
| `classNames?` | `Partial<Record<"item" \| "trigger" \| "icon" \| "title" \| "description" \| "extra" \| "indicator" \| "content", string>>` |  | Extra classes for the inner parts of every row. content styles the padded inner box, not the animated wrapper. |
| `className?` | `string` |  | Extra classes for the root, merged last. |

## Parts

### AccordionRoot (component)

The parts API, for rows that need custom markup. Takes every prop above except items and shares variant, size, indicator and tone with its parts. Props: AccordionRootProps.

### AccordionItem (component)

One row. Its value is what the root's value speaks. Props: { value: string; disabled?: boolean }.

### AccordionTrigger (component)

The clickable row inside its heading. children is the title; extra is a short value before the indicator. Props: { icon?: ReactNode; description?: ReactNode; extra?: ReactNode; children: ReactNode }.

### AccordionContent (component)

The answer. className and props go to the padded inner box; keepMounted overrides the root's for this row. Props: ComponentProps<"div"> & { keepMounted?: boolean }.

### AccordionItemData (type)

One entry of items: { value, title, description?, icon?, extra?, content, disabled? }.

## Examples

- **Variants**: Flush ghost rows, one outline or soft container, or a card per row.
- **Settings sections**: type="multiple" with icons and descriptions; the open rows' tiles take the tone.
- **Plus at the start**: The plus folds into a minus and the answer indents to the title. Brand tone, soft container.
- **Disabled row**: A row that is coming soon: dimmed, skipped by the arrow keys, not clickable.
- **Controlled**: value and onValueChange drive Open all / Close all from outside.
- **Order history**: Separated cards built from the parts, with the total as an extra value in dollars.
- **Sizes**: 44, 56 and 64px rows; even sm keeps a full touch target.
- **Keep mounted, custom color**: keepMounted keeps closed content in the DOM, so a half-filled field survives. Tinted with color.

## See also

- [Collapsible](https://wingo-ui.com/components/collapsible) ([Markdown](https://wingo-ui.com/components/collapsible.md))
- [Tabs](https://wingo-ui.com/components/tabs) ([Markdown](https://wingo-ui.com/components/tabs.md))
- [FAQ Section](https://wingo-ui.com/components/faq-section) ([Markdown](https://wingo-ui.com/components/faq-section.md))
- [useControllableState](https://wingo-ui.com/components/use-controllable-state) ([Markdown](https://wingo-ui.com/components/use-controllable-state.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))

---

Canonical: https://wingo-ui.com/components/accordion
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
