Accordion
Accordion renders Base UI’s accordion primitives from one items prop. It fills its available width, keeps triggers flush with their bordered panels, adds a panel-style border to each trigger, and smoothly transitions panels when they open or close.
import { useState } from "react";
import { Accordion, View, Label } from "density-base-ui";
export default function AccordionExample() {
const [expandedItems, setExpandedItems] = useState(["overview"]);
return (
<View align="start" gap="md" padding={false} style={{ maxWidth: "500px" }}>
<Accordion
value={expandedItems}
onValueChange={setExpandedItems}
items={[
{
id: "overview",
label: "Overview",
content: (
<Label
as="div"
emphasis="secondary"
size="md"
style={{ padding: "0.5rem 0.75rem" }}
>
Review the project scope and current status.
</Label>
),
},
{
id: "activity",
label: "Activity",
content: (
<Label
as="div"
emphasis="secondary"
size="md"
style={{ padding: "0.5rem 0.75rem" }}
>
Scan recent updates, comments, and handoffs.
</Label>
),
},
]}
/>
</View>
);
}Set headerPadding and panelPadding to false to remove their named insets. Omit them or pass true to use the size-derived default; an item’s panelPadding overrides the Accordion value.
Each item may set chevronPosition to start, end, or none; it defaults to none.
Bounded scrolling
Set fill when an Accordion fills a height-constrained parent. Set scroll on items whose expanded content should scroll independently; do not place the whole Accordion in a scrolling Panel.
import { useState } from "react";
import { Accordion, View, Label } from "density-base-ui";
const changes = [
"Add compact status controls",
"Document responsive table behavior",
"Refine keyboard navigation",
"Update package exports",
"Remove obsolete preview styles",
"Tighten component examples",
"Review release checklist",
];
export default function AccordionScrollExample() {
const [expandedItems, setExpandedItems] = useState(["changes", "history"]);
return (
<View
align="start"
gap="md"
padding={false}
style={{ height: "16rem", maxWidth: "500px" }}
>
<Accordion
value={expandedItems}
onValueChange={setExpandedItems}
fill
size="sm"
items={[
{
id: "changes",
label: "Changes",
chevronPosition: "end",
scroll: true,
content: (
<View gap="md" layout="grid" padding={false}>
{changes.map((change) => (
<Label key={change}>{change}</Label>
))}
</View>
),
},
{
id: "history",
label: "History",
chevronPosition: "end",
scroll: true,
content: (
<View gap="md" layout="grid" padding={false}>
{changes.map((change) => (
<Label key={change}>{change}</Label>
))}
</View>
),
},
]}
/>
</View>
);
}Depth
import { useState } from "react";
import { Accordion, View, Label } from "density-base-ui";
export default function AccordionDepthExample() {
const [depthOneItems, setDepthOneItems] = useState(["1"]);
const [depthTwoItems, setDepthTwoItems] = useState(["2"]);
const [depthThreeItems, setDepthThreeItems] = useState(["3"]);
const [depthFourItems, setDepthFourItems] = useState(["4"]);
return (
<View align="start" gap="md" padding={false} style={{ maxWidth: "500px" }}>
<Accordion
value={depthOneItems}
onValueChange={setDepthOneItems}
headerDepth="1"
panelDepth="1"
items={[
{
id: "1",
label: "Depth 1",
content: (
<Label as="div" size="md" style={{ padding: "0.375rem 0.75rem" }}>
Accordion panel at depth 1.
</Label>
),
},
]}
/>
<Accordion
value={depthTwoItems}
onValueChange={setDepthTwoItems}
headerDepth="2"
panelDepth="2"
items={[
{
id: "2",
label: "Depth 2",
content: (
<Label as="div" size="md" style={{ padding: "0.375rem 0.75rem" }}>
Accordion panel at depth 2.
</Label>
),
},
]}
/>
<Accordion
value={depthThreeItems}
onValueChange={setDepthThreeItems}
headerDepth="3"
panelDepth="3"
items={[
{
id: "3",
label: "Depth 3",
content: (
<Label as="div" size="md" style={{ padding: "0.375rem 0.75rem" }}>
Accordion panel at depth 3.
</Label>
),
},
]}
/>
<Accordion
value={depthFourItems}
onValueChange={setDepthFourItems}
headerDepth="4"
panelDepth="4"
items={[
{
id: "4",
label: "Depth 4",
content: (
<Label as="div" size="md" style={{ padding: "0.375rem 0.75rem" }}>
Accordion panel at depth 4.
</Label>
),
},
]}
/>
</View>
);
}Padding
import { useState } from "react";
import { Accordion, View, Label } from "density-base-ui";
export default function AccordionPaddingExample() {
const [unpaddedItems, setUnpaddedItems] = useState(["unpadded"]);
const [paddedItems, setPaddedItems] = useState(["padded"]);
return (
<View align="start" gap="md" padding={false} style={{ maxWidth: "500px" }}>
<Accordion
value={unpaddedItems}
onValueChange={setUnpaddedItems}
headerPadding={false}
panelPadding={false}
items={[
{
id: "unpadded",
label: "No padding",
content: (
<Label as="div" emphasis="secondary">
Flush disclosure content.
</Label>
),
},
]}
/>
<Accordion
value={paddedItems}
onValueChange={setPaddedItems}
headerPadding
panelPadding
items={[
{
id: "padded",
label: "Default padding",
content: (
<Label as="div" emphasis="secondary">
Default-sized disclosure content.
</Label>
),
},
]}
/>
</View>
);
}Chevron position
import { useState } from "react";
import { Accordion, View } from "density-base-ui";
export default function AccordionChevronPositionExample() {
const [noChevronItems, setNoChevronItems] = useState(["none"]);
const [startChevronItems, setStartChevronItems] = useState(["start"]);
const [endChevronItems, setEndChevronItems] = useState(["end"]);
return (
<View align="start" gap="md" style={{ width: "min(100%, 32rem)" }}>
<Accordion
value={noChevronItems}
onValueChange={setNoChevronItems}
items={[
{
id: "none",
label: "No chevron (default)",
content: "This item does not render a disclosure chevron.",
},
]}
/>
<Accordion
value={startChevronItems}
onValueChange={setStartChevronItems}
items={[
{
id: "start",
label: "Start chevron",
chevronPosition: "start",
content:
"This item renders its disclosure chevron before the label.",
},
]}
/>
<Accordion
value={endChevronItems}
onValueChange={setEndChevronItems}
items={[
{
id: "end",
label: "End chevron",
chevronPosition: "end",
content:
"This item renders its disclosure chevron after the label.",
},
]}
/>
</View>
);
}Size
import { useState } from "react";
import { Accordion, View, Label } from "density-base-ui";
export default function AccordionSizeExample() {
const [smallItems, setSmallItems] = useState(["sm"]);
const [mediumItems, setMediumItems] = useState(["md"]);
const [largeItems, setLargeItems] = useState(["lg"]);
return (
<View align="start" gap="md" padding={false} style={{ maxWidth: "500px" }}>
<Accordion
value={smallItems}
onValueChange={setSmallItems}
size="sm"
items={[
{
id: "sm",
label: "Small",
content: (
<Label as="div" emphasis="secondary">
Compact disclosure.
</Label>
),
},
]}
/>
<Accordion
value={mediumItems}
onValueChange={setMediumItems}
size="md"
items={[
{
id: "md",
label: "Medium",
content: (
<Label as="div" emphasis="secondary">
Default disclosure.
</Label>
),
},
]}
/>
<Accordion
value={largeItems}
onValueChange={setLargeItems}
size="lg"
items={[
{
id: "lg",
label: "Large",
content: (
<Label as="div" emphasis="secondary">
Roomier disclosure.
</Label>
),
},
]}
/>
</View>
);
}| Prop | Values | Required | Default | Behavior | Description |
|---|---|---|---|---|---|
border | false, true | Not required | true | — | — |
fill | false, true | Not required | false | — | — |
headerDepth | 1, 2, 3, 4, 5, 6, 7 | Not required | "1" | Attribute: data-dn-depth | — |
headerPadding | false, true | Not required | true | — | — |
items | AccordionItem[] | Required | — | — | — |
panelDepth | 1, 2, 3, 4, 5, 6, 7 | Not required | "2" | Attribute: data-dn-depth | — |
panelPadding | false, true | Not required | true | — | — |
size | 2xs, xs, sm, md, lg, xl, 2xl, 3xl | Not required | — | Attribute: data-dn-size | — |
This component also accepts props from Base UI APIs. They are passed through and intentionally kept out of this focused Density API table.