Skip to main content

Accordion

Accordion coordinates collapsible AccordionItem sections. It owns its state by default and supports caller-owned state.

Open in Storybook

Import​

import { Accordion, AccordionItem } from "@tosui/react";

Basic usage​

Default expanded item​

This section is open by default.

Multiple expanded items​

Pass allowMultiple to use array state and allow more than one expanded item.

This can stay open

While this is also open

Change events​

In single mode, onChange receives the expanded item index or null. With allowMultiple, onChange receives all expanded indexes.

<Accordion onChange={(index) => console.log(index)}>
<AccordionItem title="Account">Account settings</AccordionItem>
<AccordionItem title="Security">Security settings</AccordionItem>
</Accordion>

Controlled state​

Pass index when application state owns the expanded state. A controlled Accordion reports a requested change through onChange, but it does not update until you pass the new value back.

function ControlledAccordion() {
const [expandedIndex, setExpandedIndex] = useState<number | null>(0);

return (
<Accordion index={expandedIndex} onChange={setExpandedIndex}>
<AccordionItem title="Account">Account settings</AccordionItem>
<AccordionItem title="Security">Security settings</AccordionItem>
</Accordion>
);
}

For multiple expanded items, pass an array together with allowMultiple:

const [expandedIndexes, setExpandedIndexes] = useState<number[]>([0]);

<Accordion
allowMultiple
index={expandedIndexes}
onChange={setExpandedIndexes}
>
{/* AccordionItem children */}
</Accordion>

Disabled items​

Item indexes​

Accordion assigns an index from each direct child's position. Pass index to override that value. An AccordionItem that is not a direct child and does not have an explicit index throws an error.

<Accordion>
<AccordionItem title="Automatic index 0">First</AccordionItem>
<AccordionItem index={10} title="Explicit index 10">Second</AccordionItem>
</Accordion>

Common patterns​

FAQ section​

<Accordion>
<AccordionItem index={0} title="What is Tosui?">
Tosui is a themable, orderly, simple UI component library built on
constraint-driven simplicity.
</AccordionItem>
<AccordionItem index={1} title="How do I install it?">
<Text>Run the following command:</Text>
<Code variant="subtle" mt={2} display="block">npm install @tosui/react</Code>
</AccordionItem>
<AccordionItem index={2} title="Is it accessible?">
Accordion headers are native buttons. They expose expanded state and control
their associated panels.
</AccordionItem>
</Accordion>

Settings sections​

<Accordion allowMultiple>
<AccordionItem index={0} title="Account Settings">
<VStack gap={3} align="stretch">
<HStack justify="space-between">
<Text>Email notifications</Text>
<Switch />
</HStack>
<HStack justify="space-between">
<Text>Two-factor authentication</Text>
<Switch />
</HStack>
</VStack>
</AccordionItem>
<AccordionItem index={1} title="Privacy Settings">
<VStack gap={3} align="stretch">
<HStack justify="space-between">
<Text>Profile visibility</Text>
<Select defaultValue="public">
<option value="public">Public</option>
<option value="private">Private</option>
</Select>
</HStack>
</VStack>
</AccordionItem>
</Accordion>

Product features​

<Accordion>
<AccordionItem index={0} title="Free Plan">
<List styleType="disc" spacing={2}>
<ListItem>5 projects</ListItem>
<ListItem>Basic support</ListItem>
<ListItem>1GB storage</ListItem>
</List>
</AccordionItem>
<AccordionItem index={1} title="Pro Plan">
<List styleType="disc" spacing={2}>
<ListItem>Unlimited projects</ListItem>
<ListItem>Priority support</ListItem>
<ListItem>100GB storage</ListItem>
<ListItem>Custom domains</ListItem>
</List>
</AccordionItem>
</Accordion>

Props reference​

Accordion​

PropTypeDefaultDescription
indexnumber | null-Controlled expanded index in single mode
defaultIndexnumber-Initial expanded index in single mode
allowMultiplefalsefalseSelect single mode
onChange(index: number | null) => void-Receive the requested single-mode state
classNamestring-Additional CSS class
childrenReactNode-AccordionItem children

With allowMultiple={true}, the state props use arrays:

PropTypeDefaultDescription
indexnumber[]-Controlled expanded indexes
defaultIndexnumber[][]Initial expanded indexes
allowMultipletrue-Select multiple mode
onChange(index: number[]) => void-Receive the requested multiple-mode state

AccordionItem​

PropTypeDefaultDescription
indexnumberItem positionOptional item index override
titleReactNode-Item header (required)
disabledbooleanfalseWhether item is disabled
classNamestring-Additional CSS class
childrenReactNode-Item content

Accessibility​

  • Each trigger button uses aria-expanded and aria-controls.
  • Each panel uses aria-labelledby and role="region".
  • A collapsed panel stays mounted to preserve its local state and transition.
  • A collapsed panel is both aria-hidden and inert, so its descendants cannot receive focus.
  • A disabled trigger uses aria-disabled and ignores activation.
  • Native button behavior supports Tab, Enter, and Space.

TypeScript​

import { Accordion, AccordionItem, type AccordionProps, type AccordionItemProps } from "@tosui/react";