# Accordion

Expand or collapse related content sections.

Frequently asked questions

[Complete TSX file](/examples/accordion/default.tsx)

```tsx
"use client";

import {
  Accordion,
  AccordionItem,
  AccordionPanel,
  AccordionTrigger,
} from "@/siteplane/components/accordion";

const questions = [
  {
    answer:
      "Select “Forgot password” on the sign-in page and enter your email address. We send you a reset link that stays valid for 24 hours.",
    question: "How do I reset my password?",
    value: "password",
  },
  {
    answer:
      "Yes. You can upgrade or downgrade in your billing settings at any time. The change applies from your next billing period.",
    question: "Can I change my subscription plan?",
    value: "plan",
  },
  {
    answer:
      "We accept all major credit cards and bank transfers. Payments are processed securely.",
    question: "Which payment methods do you accept?",
    value: "payment",
  },
];

export default function Example() {
  return (
    <Accordion className="w-full max-w-md" defaultValue={["password"]}>
      {questions.map((item) => (
        <AccordionItem key={item.value} value={item.value}>
          <AccordionTrigger>{item.question}</AccordionTrigger>
          <AccordionPanel>{item.answer}</AccordionPanel>
        </AccordionItem>
      ))}
    </Accordion>
  );
}
```

## Installation

Complete [one-time setup](/docs/installation/) first.

```bash
pnpm dlx shadcn@4.18.0 add @siteplane/accordion
```

With npm: `npx shadcn@4.18.0 add @siteplane/accordion`. [Registry JSON](/registry/accordion.json) lists source files and dependencies.

For this page’s examples, also install:

```bash
pnpm dlx shadcn@4.18.0 add @siteplane/button
```

## Usage

```tsx
import {
  Accordion,
  AccordionItem,
  AccordionTrigger,
  AccordionPanel,
} from "@/siteplane/components/accordion";

<Accordion>
  <AccordionItem value="one">
    <AccordionTrigger>Details</AccordionTrigger>
    <AccordionPanel>Content</AccordionPanel>
  </AccordionItem>
</Accordion>;
```

## Composition

```text
Accordion
└── AccordionItem
    ├── AccordionTrigger (includes Header)
    └── AccordionPanel / AccordionContent
```

## Multiple panels

Set `multiple` to let more than one item stay open. The value is always an array of the open items.

Settings sections with multiple open panels

[Complete TSX file](/examples/accordion/multiple.tsx)

```tsx
"use client";

import {
  Accordion,
  AccordionItem,
  AccordionPanel,
  AccordionTrigger,
} from "@/siteplane/components/accordion";

const sections = [
  {
    content:
      "Choose which project updates reach you by email and which appear as notifications in the app.",
    title: "Notification settings",
    value: "notifications",
  },
  {
    content:
      "Turn on two-factor authentication, review active sessions and sign out of devices you no longer use.",
    title: "Privacy and security",
    value: "privacy",
  },
  {
    content:
      "View your current plan, payment history and upcoming invoices, or update your payment method.",
    title: "Billing and subscription",
    value: "billing",
  },
];

export default function Example() {
  return (
    <Accordion
      className="w-full max-w-md"
      defaultValue={["notifications"]}
      multiple
    >
      {sections.map((section) => (
        <AccordionItem key={section.value} value={section.value}>
          <AccordionTrigger>{section.title}</AccordionTrigger>
          <AccordionPanel>{section.content}</AccordionPanel>
        </AccordionItem>
      ))}
    </Accordion>
  );
}
```

## Disabled items

Set `disabled` on an `AccordionItem` to keep it visible while preventing it from opening.

Accordion with a disabled item

[Complete TSX file](/examples/accordion/disabled.tsx)

```tsx
"use client";

import {
  Accordion,
  AccordionItem,
  AccordionPanel,
  AccordionTrigger,
} from "@/siteplane/components/accordion";

export default function Example() {
  return (
    <Accordion className="w-full max-w-md">
      <AccordionItem value="history">
        <AccordionTrigger>Can I see my account history?</AccordionTrigger>
        <AccordionPanel>
          Yes. Your account history lists every plan change, invoice and support
          request.
        </AccordionPanel>
      </AccordionItem>
      <AccordionItem disabled value="premium">
        <AccordionTrigger>Premium features</AccordionTrigger>
        <AccordionPanel>
          Upgrade your plan to see the features included in Premium.
        </AccordionPanel>
      </AccordionItem>
      <AccordionItem value="email">
        <AccordionTrigger>How do I update my email address?</AccordionTrigger>
        <AccordionPanel>
          Change it in your account settings. We send a confirmation link to the
          new address.
        </AccordionPanel>
      </AccordionItem>
    </Accordion>
  );
}
```

## Controlled state

Use `value` and `onValueChange` together when other parts of the interface open or close items. Here, a button opens every item.

Accordion controlled by a button

[Complete TSX file](/examples/accordion/controlled.tsx)

```tsx
"use client";

import { useState } from "react";
import {
  Accordion,
  AccordionItem,
  AccordionPanel,
  AccordionTrigger,
} from "@/siteplane/components/accordion";
import { Button } from "@/siteplane/components/button";

const questions = [
  {
    answer:
      "A workspace groups your projects, members and billing in one place.",
    question: "What is a workspace?",
    value: "workspace",
  },
  {
    answer:
      "Anyone you invite by email. Owners decide whether new members can create projects.",
    question: "Who can join my workspace?",
    value: "members",
  },
  {
    answer:
      "Yes. Create another workspace from the account menu and switch between them at any time.",
    question: "Can I have more than one workspace?",
    value: "multiple",
  },
];

export default function Example() {
  const [value, setValue] = useState<string[]>([]);
  return (
    <div className="flex w-full max-w-md flex-col items-start gap-4">
      <Accordion
        className="w-full"
        multiple
        onValueChange={setValue}
        value={value}
      >
        {questions.map((item) => (
          <AccordionItem key={item.value} value={item.value}>
            <AccordionTrigger>{item.question}</AccordionTrigger>
            <AccordionPanel>{item.answer}</AccordionPanel>
          </AccordionItem>
        ))}
      </Accordion>
      <Button
        onClick={() => setValue(questions.map((item) => item.value))}
        variant="outline"
      >
        Expand all
      </Button>
    </div>
  );
}
```

## API reference

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| value / defaultValue | value[] | foundation empty selection | Controlled or initial open items. |
| onValueChange | (values, eventDetails) => void | unset | Expansion callback. |
| multiple | boolean | false | Allows simultaneous expansion. |
| AccordionItem.value | unknown | unset | Use stable values for controlled items. |
| AccordionItem.disabled | boolean | false | Disables the item. |
| AccordionTrigger.render | ReactElement \| render function | text Button, full width | Composed trigger; supplies header, label wrapper and chevron. |
| AccordionPanel.className | string | unset | Extra panel classes, merged with the panel motion classes. |
`Accordion`, `AccordionItem`, `AccordionTrigger` and `AccordionPanel` wrap the
corresponding [Base UI
Accordion](https://base-ui.com/react/components/accordion) parts.
`AccordionContent` aliases AccordionPanel; `AccordionPrimitive` is the
foundation namespace. No other owned props are added. Every part takes a string
className; style a state through the part's data attributes. Panel wraps
children in a div and animates foundation panel height. keepMounted and refs
remain foundation contracts. Base UI deprecates its `orientation` and
`loopFocus` props; they no longer change keyboard focus.

[Read the installed source](/source/accordion).

## Usage notes

Enter or Space toggles a focused trigger, and Tab moves between the triggers; arrow keys do not move focus. Avoid putting another interactive control inside the trigger. Use Collapsible for one disclosure rather than a related list.
