Collapsible
StableA single region that people can show or hide — the smallest unit of progressive disclosure.
Installation
$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/collapsible.jsonUsage
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible"<Collapsible>
<CollapsibleTrigger render={<Button variant="ghost" size="sm" />}>
Show details
</CollapsibleTrigger>
<CollapsibleContent>Anything you want to reveal.</CollapsibleContent>
</Collapsible>CollapsibleTrigger renders an unstyled <button>. Pass a prfct Button through render to give it a look — it keeps its own styles and gains the trigger's behavior and ARIA attributes.
Examples
Show more
Reveal the long tail of a summary. Control open when the trigger label should change with the state — Show all changes becomes Show less.
Faster builds, calmer logs
Incremental builds now reuse the cache across branches, cutting median build time by 38%. Logs group repeated warnings instead of printing each one.
File tree
Collapsibles nest cleanly, which makes them the building block for trees. Each folder is its own Collapsible; the chevron reads its trigger's data-panel-open attribute, so every level rotates independently.
role="tree" with arrow-key navigation and type-ahead) is a different pattern — reach for it when people browse hundreds of nodes.Advanced settings
Tuck rarely changed options below the essentials of a form. The fields stay in the DOM order right where they apply, so the form still reads top to bottom.
Motion
CollapsibleContent animates its measured height (--collapsible-panel-height) with the base duration and standard easing, fading its content in the same beat. To disable the animation, override the transition in your copy. Reduced motion needs no work: with prefers-reduced-motion, the panel opens instantly.
Guidelines
When to use
- To hide one block of secondary content behind a clear label: details, advanced options, the rest of a long list.
- As the building block for custom disclosure patterns — file trees, sidebar sections, "show more" summaries.
When not to use
- For several related sections that should behave as a set — use an Accordion.
- For content that should float above the page — use a Popover.
- To hide content people need to complete the task at hand.
Label the outcome
The trigger should say what appears — Advanced settings, Show all changes, 3 more files — not just More. Pair text with a chevron that rotates; an icon alone doesn't say what's hidden.
Keep the trigger in place
Put the trigger before the content it controls and don't move it when the panel opens. People should be able to close what they just opened without hunting for the control.
Accessibility
The trigger is a native <button> with aria-expanded and, while open, aria-controls pointing at the panel.
| Key | Behavior |
|---|---|
Tab | Moves focus to the trigger, then into the open panel's content. |
EnterSpace | Opens or closes the panel. |
- Find in page. Set
hiddenUntilFoundonCollapsibleContentand the browser can search inside a closed panel and open it on a match. - Focus stays put. Opening a panel doesn't move focus; the next Tab lands inside it.
- Custom triggers. When you render something other than a
Buttonthroughrender, make sure it is still a real button — or passnativeButton={false}so Base UI adds button semantics.
API reference
Collapsible
Owns the open state. Renders a <div>. Accepts every prop of Base UI Collapsible.Root.
CollapsibleTrigger
The button that toggles the panel. Gets data-panel-open while open, which is handy for rotating icons. Accepts every prop of Base UI Collapsible.Trigger, including render and nativeButton.
CollapsibleContent
The panel. Animates its height by default.