Accordion
A vertically stacked set of interactive headings that each reveal a section of content.
API Reference
Original primitive API
Behavior, accessibility details, and low-level props are documented by Base UI.
Basic
import { Accordion, AccordionItem, AccordionPanel, AccordionTrigger,} from "moduix";export function AccordionDemo() { return ( <Accordion defaultValue={["what-is-base-ui"]}> {items.map((item) => ( <AccordionItem key={item.value} value={item.value}> <AccordionTrigger>{item.title}</AccordionTrigger> <AccordionPanel> <div className={styles.panelContent}>{item.description}</div> </AccordionPanel> </AccordionItem> ))} </Accordion> );}.panelContent { padding: var(--spacing-3);}const items = [ { value: "what-is-base-ui", title: "What is Base UI?", description: "Base UI is a library of high-quality unstyled React components for design systems and web apps.", }, { value: "getting-started", title: "How do I get started?", description: "Head to the Quick start guide in the docs. If you have used unstyled libraries before, you will feel at home.", }, { value: "can-i-use-it", title: "Can I use it for my project?", description: "Of course. Base UI is free and open source.", },];Full list of component variables available for project-level overrides.
| Property | Default | Description |
|---|---|---|
| --accordion-color | var(--color-foreground) | Controls accordion text color. |
| --accordion-disabled-opacity | var(--opacity-disabled) | Controls disabled item opacity. |
| --accordion-focus-ring-color | var(--color-ring) | Controls trigger focus ring color. |
| --accordion-focus-ring-offset | var(--border-width-sm) | Controls trigger focus ring offset from trigger edge. |
| --accordion-focus-ring-width | var(--border-width-md) | Controls trigger focus ring outline width. |
| --accordion-icon-margin-right | var(--spacing-2) | Controls trigger icon right margin. |
| --accordion-icon-open-transform | rotate(45deg) scale(1.1) | Controls trigger icon transform when the panel is open. |
| --accordion-icon-size | 0.75rem | Controls trigger icon size. |
| --accordion-icon-transition | var(--transition-default) | Controls trigger icon transition. |
| --accordion-item-border-color | var(--color-border) | Controls the separator color between accordion items. |
| --accordion-item-border-width | var(--border-width-sm) | Controls the separator width between accordion items. |
| --accordion-max-width | calc(100vw - 8rem) | Controls the root accordion max width. |
| --accordion-panel-color | var(--color-muted-foreground) | Controls panel text color. |
| --accordion-panel-font-size | var(--text-md) | Controls panel text font size. |
| --accordion-panel-line-height | var(--line-height-text-md) | Controls panel text line height. |
| --accordion-panel-transition | var(--transition-default) | Controls panel open and close transition. |
| --accordion-trigger-bg | var(--color-muted) | Controls trigger background color. |
| --accordion-trigger-bg-hover | var(--color-accent) | Controls trigger background color on hover. |
| --accordion-trigger-font-size | var(--text-md) | Controls trigger text font size. |
| --accordion-trigger-gap | var(--spacing-4) | Controls spacing between trigger content and icon. |
| --accordion-trigger-line-height | var(--line-height-text-md) | Controls trigger text line height. |
| --accordion-trigger-padding-x-end | var(--spacing-1) | Controls trigger end-side horizontal padding. |
| --accordion-trigger-padding-x-start | var(--spacing-3) | Controls trigger start-side horizontal padding. |
| --accordion-trigger-padding-y | var(--spacing-2) | Controls trigger vertical padding. |
| --accordion-width | 24rem | Controls the root accordion width. |
Interactive variables scoped for docs preview without changing size scale tokens.
| Property | Value | Default | Description |
|---|---|---|---|
| --accordion-color | var(--color-foreground) | Controls accordion text color. | |
| --accordion-focus-ring-color | var(--color-ring) | Controls trigger focus ring color. | |
| --accordion-icon-size | 0.75rem | Controls trigger icon size. | |
| --accordion-item-border-color | var(--color-border) | Controls separator color. | |
| --accordion-item-border-width | var(--border-width-sm) | Controls separator width. | |
| --accordion-panel-color | var(--color-muted-foreground) | Controls panel text color. | |
| --accordion-trigger-bg | var(--color-muted) | Controls trigger background color. | |
| --accordion-trigger-bg-hover | var(--color-accent) | Controls trigger hover background. | |
| --accordion-trigger-gap | var(--spacing-4) | Controls trigger content and icon spacing. | |
| --accordion-trigger-padding-y | var(--spacing-2) | Controls trigger vertical padding. |
Anatomy
Accordion is composed as a stack of independent items. Keep the trigger and panel inside the same
AccordionItem so their state and accessibility wiring stay paired.
Accordion
└─ AccordionItem[value]
├─ AccordionTrigger
│ ├─ label
│ └─ icon
└─ AccordionPanel
└─ content<Accordion defaultValue={['shipping']}>
<AccordionItem value="shipping">
<AccordionTrigger>Shipping details</AccordionTrigger>
<AccordionPanel>
<div className={styles.panelContent}>Delivery times, prices, and tracking options.</div>
</AccordionPanel>
</AccordionItem>
</Accordion>| Part | Role |
|---|---|
Accordion | Root state machine. Controls open values, keyboard navigation, multiple, disabled, and orientation. |
AccordionItem | One collapsible row. Its value identifies the row for defaultValue, controlled value, and change handlers. |
AccordionTrigger | Semantic heading and interactive button. It opens or closes the matching panel and renders a default icon. |
AccordionPanel | The collapsible content region. Put padding/content wrappers inside it so panel height animations stay clean. |
AccordionTrigger renders the Base UI header internally so the common composition stays compact.
Use classNames.header, classNames.icon, slotProps.header, and slotProps.icon when you need
to style or customize those internal slots. AccordionHeader and AccordionTriggerIcon are still
exported for advanced manual composition, but most projects do not need them.
Composition
Use Accordion for root state and behavior props such as value, defaultValue,
onValueChange, multiple, disabled, orientation, and loopFocus. Use keepMounted
or hiddenUntilFound on the root or panel when closed content must stay in the DOM.
Style the public parts with className on Accordion, AccordionItem, AccordionTrigger,
and AccordionPanel. AccordionTrigger also accepts icon, hideIcon, classNames, and
slotProps for the internal header and icon slots. All Base UI behavior props such as render,
keepMounted, and hiddenUntilFound pass through to the matching primitive part.
Examples
Multiple
Use the multiple prop to allow multiple items to be open at the same time.
import { Accordion, AccordionItem, AccordionPanel, AccordionTrigger,} from "moduix";export function MultipleAccordionDemo() { return ( <Accordion multiple defaultValue={["what-is-base-ui", "can-i-use-it"]}> {items.map((item) => ( <AccordionItem key={item.value} value={item.value}> <AccordionTrigger>{item.title}</AccordionTrigger> <AccordionPanel> <div className={styles.panelContent}>{item.description}</div> </AccordionPanel> </AccordionItem> ))} </Accordion> );}.panelContent { padding: var(--spacing-3);}const items = [ { value: "what-is-base-ui", title: "What is Base UI?", description: "Base UI is a library of high-quality unstyled React components for design systems and web apps.", }, { value: "getting-started", title: "How do I get started?", description: "Head to the Quick start guide in the docs. If you have used unstyled libraries before, you will feel at home.", }, { value: "can-i-use-it", title: "Can I use it for my project?", description: "Of course. Base UI is free and open source.", },];Controlled
Control the open panels from React state when the accordion needs to coordinate with other UI.
import { Accordion, AccordionItem, AccordionPanel, AccordionTrigger,} from "moduix";import { useState } from "react";export function ControlledAccordionDemo() { const [value, setValue] = useState(["getting-started"] as string[]) return ( <Accordion value={value} onValueChange={setValue}> {items.map((item) => ( <AccordionItem key={item.value} value={item.value}> <AccordionTrigger>{item.title}</AccordionTrigger> <AccordionPanel> <div className={styles.panelContent}>{item.description}</div> </AccordionPanel> </AccordionItem> ))} </Accordion> );}.panelContent { padding: var(--spacing-3);}const items = [ { value: "what-is-base-ui", title: "What is Base UI?", description: "Base UI is a library of high-quality unstyled React components for design systems and web apps.", }, { value: "getting-started", title: "How do I get started?", description: "Head to the Quick start guide in the docs. If you have used unstyled libraries before, you will feel at home.", }, { value: "can-i-use-it", title: "Can I use it for my project?", description: "Of course. Base UI is free and open source.", },];Disabled Item
Set disabled on an item to keep it visible while preventing interaction.
import { Accordion, AccordionItem, AccordionPanel, AccordionTrigger,} from "moduix";export function DisabledItemAccordionDemo() { return ( <Accordion defaultValue={["what-is-base-ui"]}> {items.map((item) => ( <AccordionItem key={item.value} value={item.value} disabled={item.value === "getting-started"} > <AccordionTrigger>{item.title}</AccordionTrigger> <AccordionPanel> <div className={styles.panelContent}>{item.description}</div> </AccordionPanel> </AccordionItem> ))} </Accordion> );}.panelContent { padding: var(--spacing-3);}const items = [ { value: "what-is-base-ui", title: "What is Base UI?", description: "Base UI is a library of high-quality unstyled React components for design systems and web apps.", }, { value: "getting-started", title: "How do I get started?", description: "Head to the Quick start guide in the docs. If you have used unstyled libraries before, you will feel at home.", }, { value: "can-i-use-it", title: "Can I use it for my project?", description: "Of course. Base UI is free and open source.", },];Custom Icon
Pass icon to replace the default visual marker and style the icon slot with classNames.icon.
import { Accordion, AccordionItem, AccordionPanel, AccordionTrigger, ChevronDownIcon,} from "moduix";export function CustomStylesAccordionDemo() { return ( <Accordion defaultValue={["what-is-base-ui"]}> {items.map((item) => ( <AccordionItem key={item.value} value={item.value}> <AccordionTrigger icon={<ChevronDownIcon />} classNames={{ icon: styles.customIcon }} > {item.title} </AccordionTrigger> <AccordionPanel> <div className={styles.panelContent}>{item.description}</div> </AccordionPanel> </AccordionItem> ))} </Accordion> );}.panelContent { padding: var(--spacing-3);}.customIcon { --accordion-icon-open-transform: rotate(180deg);}const items = [ { value: "what-is-base-ui", title: "What is Base UI?", description: "Base UI is a library of high-quality unstyled React components for design systems and web apps.", }, { value: "getting-started", title: "How do I get started?", description: "Head to the Quick start guide in the docs. If you have used unstyled libraries before, you will feel at home.", }, { value: "can-i-use-it", title: "Can I use it for my project?", description: "Of course. Base UI is free and open source.", },];