Collapsible
A collapsible is one section with a button that opens and closes it. Use it when a single piece of extra content should stay out of the way until it is wanted.
import { Collapsible, CollapsiblePanel, CollapsibleTrigger } from "@/components/ui/collapsible";
export default function CollapsibleDemo() {
return (
<Collapsible className="max-w-xs">
<CollapsibleTrigger>Recovery keys</CollapsibleTrigger>
<CollapsiblePanel>
<ul className="flex flex-col gap-1 py-2 font-mono text-sm text-text/70">
<li>alien-bean-pasta</li>
<li>wild-irish-burrito</li>
<li>horse-battery-staple</li>
</ul>
</CollapsiblePanel>
</Collapsible>
);
}The code
The component is a single file, read from the repository when the site is built. Press Show code to see all of it.
import type { ComponentProps } from "react";
import { ChevronRight } from "lucide-react";
import { Collapsible as BaseCollapsible } from "@base-ui/react/collapsible";
import { Button } from "@/components/ui/button";
import { cx } from "@/lib/cx";
import type { WithClass } from "@/lib/types";
// One section that opens and closes. For several in a stack, use an Accordion instead.
export function Collapsible({ className, ...props }: WithClass<ComponentProps<typeof BaseCollapsible.Root>>) {
return <BaseCollapsible.Root className={cx("flex w-full flex-col gap-2", className)} {...props} />;
}
// A secondary Button with a chevron that turns down while the panel is open.
export function CollapsibleTrigger({
children,
className,
...props
}: Omit<ComponentProps<typeof BaseCollapsible.Trigger>, "render" | "className"> & { className?: string }) {
return (
<BaseCollapsible.Trigger render={<Button variant="secondary" className={cx("self-start", className)} />} {...props}>
{children}
<ChevronRight
aria-hidden
className="transition-transform duration-150 [[data-panel-open]>&]:rotate-90 motion-reduce:transition-none"
/>
</BaseCollapsible.Trigger>
);
}
export function CollapsiblePanel({ className, ...props }: WithClass<ComponentProps<typeof BaseCollapsible.Panel>>) {
return (
<BaseCollapsible.Panel
className={cx(
"h-[var(--collapsible-panel-height)] overflow-hidden text-base transition-[height] duration-150 ease-out data-[ending-style]:h-0 data-[starting-style]:h-0 [&[hidden]:not([hidden='until-found'])]:hidden motion-reduce:transition-none",
className,
)}
{...props}
/>
);
}Props
| Prop | Type | Default | What it does |
|---|---|---|---|
defaultOpen | boolean | false | Starts the section open, when it manages its own state. |
open | boolean | — | Whether the section is open, when you control it yourself. |
onOpenChange | function | — | Called with the new open state. |
disabled | boolean | false | Stops the button from opening or closing the section. |
Every other prop goes to the Base UI Collapsible underneath. CollapsibleTrigger takes its label as children.
The thinking
Not everything deserves space on the page at all times. A collapsible is the smallest way to tuck something away: one button, one panel.
- Built on Base UI: Base UI links the button to its panel, tracks the open state and measures the panel so it can animate to its real height.
- Three parts: Collapsible holds the state, CollapsibleTrigger is the button and CollapsiblePanel is what opens.
- Collapsible or Accordion: Use a Collapsible for one section. Use an Accordion for a stack of related sections that belong together.
Interface design
- A real Button: The trigger is the secondary Button, so it has the same hover, focus and press as every other button.
- Chevron after the label: The chevron sits after the text, like icons in the other buttons, and turns a quarter turn down while the section is open.
- No box: The panel has no border or fill. What you put inside decides how it looks.
Interaction
- Open and close: Click the button, or press Enter or Space when it has focus.
- Press: The button shrinks slightly while pressed, like every Button.
- Motion: The panel grows and shrinks in 150ms. With reduced motion turned on it opens at once.
Empathy
- State is announced: The button reports whether the section is expanded and which panel it controls.
- Closed means hidden: A closed panel is hidden from screen readers and the tab order, so its links and buttons can't be reached by accident.
- Name it by what is inside: The label is all anyone sees while it is closed, so Recovery keys is better than Show more.