AdminKitDocs
Components

Next.js Bootstrap Accordion

A Next.js Bootstrap accordion for the App Router that a server component page can render as is: flush and always-open variants, no Bootstrap JavaScript.

Pro

This component is only available in AdminKit PRO. Learn more.

An accordion keeps a long FAQ or help page short: the reader sees the headers and opens the part they need. AdminKit builds it from four components, Accordion, AccordionItem, AccordionHeader and AccordionBody. They render Bootstrap's accordion markup and open items from React state, without Bootstrap's JavaScript. The accordion is part of the PRO version of AdminKit for Next.js.

In the App Router, pages are server components: they render on the server and have no state. The accordion needs state to know which items are open, so its components are client components, from a file that starts with "use client". You don't need a client component of your own, though. Accordion holds the open items itself, and its props are only strings and booleans, which a server component can pass. Your page renders it directly, needs no "use client" and keeps its metadata export.

Example

Every item has an eventKey. Pass one to defaultActiveKey to open that item on the first render. Opening another item then closes it.

src/app/(dashboard)/help/page.tsx
import {
  Accordion,
  AccordionBody,
  AccordionHeader,
  AccordionItem,
} from "@/components/bootstrap/accordion";
import { pageMetadata } from "@/lib/site";

export const metadata = pageMetadata("/help");

export default function HelpPage() {
  return (
    <>
      <h1 className="h3 mb-3">Help</h1>
      <Accordion defaultActiveKey="shipping">
        <AccordionItem eventKey="shipping">
          <AccordionHeader>When do orders ship?</AccordionHeader>
          <AccordionBody>Orders ship within two business days.</AccordionBody>
        </AccordionItem>
        <AccordionItem eventKey="returns">
          <AccordionHeader>Can I return an order?</AccordionHeader>
          <AccordionBody>Return any order within 30 days for a full refund.</AccordionBody>
        </AccordionItem>
        <AccordionItem eventKey="payment">
          <AccordionHeader>Which payment methods do you accept?</AccordionHeader>
          <AccordionBody>Credit cards, PayPal and bank transfer.</AccordionBody>
        </AccordionItem>
      </Accordion>
    </>
  );
}

pageMetadata() reads the page's title and description from src/data/pages.json, so a new page needs an entry there (see Add a page).

The HTML that Next.js builds already has the items of defaultActiveKey open (.collapse.show) and the others closed. The ids come from useId, so they are the same on the server and in the browser. When the browser takes over that HTML (hydration), its markup matches the server's: nothing shifts, and there is no hydration mismatch.

Props

PropTypeDefaultDescription
defaultActiveKeystring | string[]The item or items open on the first render; all closed without it
alwaysOpenbooleanfalseItems stay open when another item opens

Other props of Accordion, such as className and id, go to the .accordion element. The four components are in src/components/bootstrap/accordion.tsx.

  • AccordionItem: a required eventKey (string), unique in its accordion. Other props go to .accordion-item.
  • AccordionHeader: as ("h2" to "h6", "h2" by default) sets the heading around the button, className styles that heading, children is the button text.
  • AccordionBody: its props, className included, go to .accordion-body, not to the collapsing wrapper around it.

Once the accordion is on the page, no prop opens an item from outside. To drive a panel from your own state, use Collapse in a client component. The parts throw an error when they are used outside an Accordion.

Flush

To fit the accordion edge to edge in a card, add accordion-flush. It drops the outer borders and the rounded corners:

<Accordion className="accordion-flush">
  <AccordionItem eventKey="account">
    <AccordionHeader>Account</AccordionHeader>
    <AccordionBody>Change your name, email and password.</AccordionBody>
  </AccordionItem>
  <AccordionItem eventKey="billing">
    <AccordionHeader>Billing</AccordionHeader>
    <AccordionBody>Update your payment method and download invoices.</AccordionBody>
  </AccordionItem>
</Accordion>

Always open

When readers need several sections open at once, add alwaysOpen. Pass a list to defaultActiveKey to open more than one at the start:

<Accordion alwaysOpen defaultActiveKey={["profile", "notifications"]}>
  <AccordionItem eventKey="profile">
    <AccordionHeader>Profile</AccordionHeader>
    <AccordionBody>Your photo and display name.</AccordionBody>
  </AccordionItem>
  <AccordionItem eventKey="notifications">
    <AccordionHeader>Notifications</AccordionHeader>
    <AccordionBody>Choose which emails you receive.</AccordionBody>
  </AccordionItem>
</Accordion>

Accessibility

  • Each header is a <button> inside a heading, with aria-expanded and aria-controls set to the body's id.
  • Use as to match the page outline, for example as="h3" when the accordion sits under an h2.
  • Tab moves between the header buttons. Enter and Space open and close an item.
  • A closed body is display: none (.collapse). Its links and fields leave the tab order, and screen readers skip it.

Last updated on

On this page