Skip to main contentSkip to docs navigation

Build vertically collapsing accordions using the native HTML details and summary elements.

Examples

Click the accordions below to expand/collapse the accordion content. Items sharing the same name form a group where only one item can be open at a time — set name on Accordion to apply it to the whole group.

Accordion Item #1
This is the first item's accordion body. It is hidden by default. Just about any HTML can go within the .accordion-body.
Accordion Item #2
This is the second item's accordion body. It is shown by default via the open prop.
Accordion Item #3
This is the third item's accordion body. It is hidden by default.
JSX
import { Accordion, AccordionBody, AccordionHeader, AccordionItem } from '@chassis-ui/react'

export const Example = () => {
  return (
    <Accordion name="basic-example">
      <AccordionItem open>
        <AccordionHeader>Accordion Item #1</AccordionHeader>
        <AccordionBody>
          <strong>This is the first item's accordion body.</strong> It is hidden by default. Just
          about any HTML can go within the <code>.accordion-body</code>.
        </AccordionBody>
      </AccordionItem>
      <AccordionItem>
        <AccordionHeader>Accordion Item #2</AccordionHeader>
        <AccordionBody>
          <strong>This is the second item's accordion body.</strong> It is shown by default via the{' '}
          <code>open</code> prop.
        </AccordionBody>
      </AccordionItem>
      <AccordionItem>
        <AccordionHeader>Accordion Item #3</AccordionHeader>
        <AccordionBody>
          <strong>This is the third item's accordion body.</strong> It is hidden by default.
        </AccordionBody>
      </AccordionItem>
    </Accordion>
  )
}

Flush

Add flush to render accordions edge-to-edge with their parent container.

Accordion Item #1
This is the first item's accordion body.
Accordion Item #2
This is the second item's accordion body.
Accordion Item #3
This is the third item's accordion body.
JSX
import { Accordion, AccordionBody, AccordionHeader, AccordionItem } from '@chassis-ui/react'

export const Example = () => {
  return (
    <Accordion flush>
      <AccordionItem>
        <AccordionHeader>Accordion Item #1</AccordionHeader>
        <AccordionBody>
          <strong>This is the first item's accordion body.</strong>
        </AccordionBody>
      </AccordionItem>
      <AccordionItem>
        <AccordionHeader>Accordion Item #2</AccordionHeader>
        <AccordionBody>
          <strong>This is the second item's accordion body.</strong>
        </AccordionBody>
      </AccordionItem>
      <AccordionItem>
        <AccordionHeader>Accordion Item #3</AccordionHeader>
        <AccordionBody>
          <strong>This is the third item's accordion body.</strong>
        </AccordionBody>
      </AccordionItem>
    </Accordion>
  )
}

Always open

Add alwaysOpen to let multiple items stay open at the same time.

Accordion Item #1
This is the first item's accordion body.
Accordion Item #2
This is the second item's accordion body.
Accordion Item #3
This is the third item's accordion body.
JSX
import { Accordion, AccordionBody, AccordionHeader, AccordionItem } from '@chassis-ui/react'

export const Example = () => {
  return (
    <Accordion alwaysOpen>
      <AccordionItem open>
        <AccordionHeader>Accordion Item #1</AccordionHeader>
        <AccordionBody>
          <strong>This is the first item's accordion body.</strong>
        </AccordionBody>
      </AccordionItem>
      <AccordionItem open>
        <AccordionHeader>Accordion Item #2</AccordionHeader>
        <AccordionBody>
          <strong>This is the second item's accordion body.</strong>
        </AccordionBody>
      </AccordionItem>
      <AccordionItem>
        <AccordionHeader>Accordion Item #3</AccordionHeader>
        <AccordionBody>
          <strong>This is the third item's accordion body.</strong>
        </AccordionBody>
      </AccordionItem>
    </Accordion>
  )
}

Sizes

Add size="lg" or size="sm" for additional sizes.

Large Accordion Item #1
This is the first item's accordion body.
Large Accordion Item #2
This is the second item's accordion body.
Small Accordion Item #1
This is the first item's accordion body.
Small Accordion Item #2
This is the second item's accordion body.
JSX
import { Accordion, AccordionBody, AccordionHeader, AccordionItem } from '@chassis-ui/react'

export const Example = () => {
  return (
    <>
      <Accordion size="lg" name="size-example-lg" className="mb-md">
        <AccordionItem open>
          <AccordionHeader>Large Accordion Item #1</AccordionHeader>
          <AccordionBody>
            <strong>This is the first item's accordion body.</strong>
          </AccordionBody>
        </AccordionItem>
        <AccordionItem>
          <AccordionHeader>Large Accordion Item #2</AccordionHeader>
          <AccordionBody>
            <strong>This is the second item's accordion body.</strong>
          </AccordionBody>
        </AccordionItem>
      </Accordion>
      <Accordion size="sm" name="size-example-sm">
        <AccordionItem open>
          <AccordionHeader>Small Accordion Item #1</AccordionHeader>
          <AccordionBody>
            <strong>This is the first item's accordion body.</strong>
          </AccordionBody>
        </AccordionItem>
        <AccordionItem>
          <AccordionHeader>Small Accordion Item #2</AccordionHeader>
          <AccordionBody>
            <strong>This is the second item's accordion body.</strong>
          </AccordionBody>
        </AccordionItem>
      </Accordion>
    </>
  )
}

Caret position

Add caretEnd to move the caret icon to the end of the header.

Accordion Item #1
This is the first item's accordion body.
Accordion Item #2
This is the second item's accordion body.
Accordion Item #3
This is the third item's accordion body.
JSX
import { Accordion, AccordionBody, AccordionHeader, AccordionItem } from '@chassis-ui/react'

export const Example = () => {
  return (
    <Accordion caretEnd name="caret-end-example">
      <AccordionItem open>
        <AccordionHeader>Accordion Item #1</AccordionHeader>
        <AccordionBody>
          <strong>This is the first item's accordion body.</strong>
        </AccordionBody>
      </AccordionItem>
      <AccordionItem>
        <AccordionHeader>Accordion Item #2</AccordionHeader>
        <AccordionBody>
          <strong>This is the second item's accordion body.</strong>
        </AccordionBody>
      </AccordionItem>
      <AccordionItem>
        <AccordionHeader>Accordion Item #3</AccordionHeader>
        <AccordionBody>
          <strong>This is the third item's accordion body.</strong>
        </AccordionBody>
      </AccordionItem>
    </Accordion>
  )
}

Per-item overrides

name and alwaysOpen can be set on Accordion as shared defaults, and overridden on individual AccordionItems — items without their own value fall back to the accordion's setting.

Accordion Item #1
This item sets its own alwaysOpen, so it stays open independently of the group below.
Accordion Item #2
This item overrides name to join a separate group with Accordion Item #3, so opening either one closes the other without affecting Item #1.
Accordion Item #3
This item shares Item #2's group name.
JSX
import { Accordion, AccordionBody, AccordionHeader, AccordionItem } from '@chassis-ui/react'

export const Example = () => {
  return (
    <Accordion name="mixed-groups-example">
      <AccordionItem open alwaysOpen>
        <AccordionHeader>Accordion Item #1</AccordionHeader>
        <AccordionBody>
          <strong>This item sets its own</strong> <code>alwaysOpen</code>, so it stays open
          independently of the group below.
        </AccordionBody>
      </AccordionItem>
      <AccordionItem name="mixed-groups-example-b" open>
        <AccordionHeader>Accordion Item #2</AccordionHeader>
        <AccordionBody>
          <strong>This item overrides</strong> <code>name</code> to join a separate group with
          Accordion Item #3, so opening either one closes the other without affecting Item #1.
        </AccordionBody>
      </AccordionItem>
      <AccordionItem name="mixed-groups-example-b">
        <AccordionHeader>Accordion Item #3</AccordionHeader>
        <AccordionBody>
          <strong>This item shares Item #2's group name.</strong>
        </AccordionBody>
      </AccordionItem>
    </Accordion>
  )
}

JSON content

Pass an items array to render an accordion from data instead of composed children. Each item accepts header, body, and optional open, name, and alwaysOpen overrides.

Accordion Item #1
This is the first item's accordion body. Rendered from a plain data array instead of composed children.
Accordion Item #2
This is the second item's accordion body.
Accordion Item #3
This is the third item's accordion body.
JSX
import { Accordion } from '@chassis-ui/react'

const items = [
  {
    id: 'item-1',
    header: 'Accordion Item #1',
    body: (
      <>
        <strong>This is the first item's accordion body.</strong> Rendered from a plain data array
        instead of composed children.
      </>
    ),
    open: true
  },
  {
    id: 'item-2',
    header: 'Accordion Item #2',
    body: <strong>This is the second item's accordion body.</strong>
  },
  {
    id: 'item-3',
    header: 'Accordion Item #3',
    body: <strong>This is the third item's accordion body.</strong>,
    alwaysOpen: true
  }
]

export const Example = () => {
  return <Accordion name="json-content-example" items={items} />
}

Controlled

A composed AccordionItem already supports the native controlled <details> pattern directly — pass open and read the new state back from onToggle:

TSX
<AccordionItem open={isOpen} onToggle={(e) => setIsOpen(e.currentTarget.open)}>

For the items data-driven API, Accordion offers the same thing at the group level: expandedKeys/defaultExpandedKeys/onExpandedChange track which item keys are open, reflecting the native <details> elements' actual state (including any the browser closes itself as part of a shared name group).

Accordion Item #1
This is the first item's accordion body.
Accordion Item #2
This is the second item's accordion body.
Accordion Item #3
This is the third item's accordion body.
JSX
import { useState } from 'react'
import { Accordion, Button, ButtonGroup } from '@chassis-ui/react'

const items = [
  {
    id: 'item-1',
    header: 'Accordion Item #1',
    body: <strong>This is the first item's accordion body.</strong>
  },
  {
    id: 'item-2',
    header: 'Accordion Item #2',
    body: <strong>This is the second item's accordion body.</strong>
  },
  {
    id: 'item-3',
    header: 'Accordion Item #3',
    body: <strong>This is the third item's accordion body.</strong>
  }
]

export const Example = () => {
  const [expandedKeys, setExpandedKeys] = useState<Array<number | string>>(['item-1'])

  return (
    <>
      <ButtonGroup className="mb-md">
        <Button size="sm" onClick={() => setExpandedKeys(items.map((item) => item.id))}>
          Expand all
        </Button>
        <Button size="sm" onClick={() => setExpandedKeys([])}>
          Collapse all
        </Button>
      </ButtonGroup>
      <Accordion
        alwaysOpen
        items={items}
        expandedKeys={expandedKeys}
        onExpandedChange={setExpandedKeys}
      />
    </>
  )
}

API

Accordion

PropTypeDefault
alwaysOpen?booleanfalse
Make accordion items stay open when another item is opened.
caretEnd?boolean—
Move the caret icon to the end of the header.
className?string—
A string of all className you want applied to the base component.
defaultExpandedKeys?(string | number)[]—
The keys of the initially expanded items (uncontrolled). Only applies to items rendered via the items prop — a composed AccordionItem already supports native controlled open/ onToggle directly.
expandedKeys?(string | number)[]—
The keys of the currently expanded items (controlled). Only applies to items rendered via the items prop — a composed AccordionItem already supports native controlled open/onToggle directly.
flush?boolean—
Removes the default background-context, some borders, and some rounded corners to render accordions edge-to-edge with their parent container.
items?AccordionItemDef[]—
Array of item definitions for data-driven rendering. When provided, children are ignored.
name?string—
The shared group name used by items that don't set their own name. Defaults to an auto-generated id.
onExpandedChange?((keys: (string | number)[]) => void)—
Callback fired with the updated set of expanded item keys, reflecting the native <details> elements' actual open state (including items the browser closed itself via a shared name group). Only fires for items rendered via the items prop.
size?"sm" | "lg"—
Size the component sm or lg.

AccordionItemDef

Shape of each entry in Accordion's items array (data-driven rendering).

PropTypeDefault
alwaysOpen?boolean—
Let this item stay open when another item opens, overriding the accordion's alwaysOpen setting.
bodyReactNode—
Body content, rendered inside a AccordionBody.
className?string—
A string of all className you want applied to this item.
headerReactNode—
Header content, rendered inside a AccordionHeader.
id?string | number—
React key for this item. Falls back to its index when omitted.
name?string—
The group this item shares with other items so only one can be open at a time, overriding the accordion's shared name.
onToggle?((event: SyntheticEvent<HTMLDetailsElement, Event>) => void)—
Callback fired when this item's open state changes. Chained after Accordion's own handling of expandedKeys/onExpandedChange, so it fires regardless of whether those are used.
open?boolean—
Start the item in the open state (uncontrolled). Ignored for items whose key is included in expandedKeys/defaultExpandedKeys on the parent Accordion.

AccordionItem

PropTypeDefault
alwaysOpen?boolean—
Let this item stay open when another item opens, overriding the accordion's alwaysOpen setting.
className?string—
A string of all className you want applied to the base component.
itemKey?string | number—
@deprecated No longer used. The native details element manages its own state.
name?string—
The group this item shares with other items so only one can be open at a time, overriding the accordion's shared name.
open?boolean—
Start the item in the open state.

AccordionHeader

PropTypeDefault
className?string—
A string of all className you want applied to the base component.

AccordionBody

PropTypeDefault
asChild?boolean—
Render the single child element in place of this component's own element, with this component's classes, props and ref merged onto it: <Button asChild><Link href="/login">Log in</Link></Button>. The alternative to component for React Server Components — an element can be passed from the server to this package's client components, a component reference (component={Link}) can't.
className?string—
A string of all className you want applied to the base component.
component?ElementType—
Component used for the root node. Either a string to use a HTML element or a component.
ref?any—