Siteplane UI

Accordion

Expand or collapse related content sections.

Markdown

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.

"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

pnpm dlx shadcn@4.18.0 add @siteplane/accordion

Complete the one-time setup first. Registry JSON lists source files and dependencies.

For this page’s examples, also install:

pnpm dlx shadcn@4.18.0 add @siteplane/button

Usage

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

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

Composition

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.

Choose which project updates reach you by email and which appear as notifications in the app.

"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.

"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.

"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

Component properties
PropTypeDefaultDescription
value / defaultValuevalue[]foundation empty selectionControlled or initial open items.
onValueChange(values, eventDetails) => voidunsetExpansion callback.
multiplebooleanfalseAllows simultaneous expansion.
AccordionItem.valueunknownunsetUse stable values for controlled items.
AccordionItem.disabledbooleanfalseDisables the item.
AccordionTrigger.renderReactElement | render functiontext Button, full widthComposed trigger; supplies header, label wrapper and chevron.
AccordionPanel.classNamestringunsetExtra panel classes, merged with the panel motion classes.

Accordion, AccordionItem, AccordionTrigger and AccordionPanel wrap the corresponding Base UI 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.

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.