Skip to content

Collapsible

Stable

A single region that people can show or hide — the smallest unit of progressive disclosure.

components/ui · 4 files
accordion.tsx

Installation

$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/collapsible.json

Usage

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.

Release 2.4 · September 18

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.

layout.tsx
page.tsx
package.json
Trees need more than disclosure
A file tree built from collapsibles is navigable with Tab. A full ARIA tree (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.

Do.The label tells people what they'll get before they click.
Don’t.“More” says nothing about what's hidden or how much of it.

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.

KeyBehavior
Tab
Moves focus to the trigger, then into the open panel's content.
EnterSpace
Opens or closes the panel.
  • Find in page. Set hiddenUntilFound on CollapsibleContent and 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 Button through render, make sure it is still a real button — or pass nativeButton={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.

PropTypeDefault
defaultOpen

Whether the panel is open on first render (uncontrolled).

booleanfalse
open

Whether the panel is open (controlled). Pair with onOpenChange.

booleanNo default
onOpenChange

Called when the panel opens or closes.

(open: boolean, details) => voidNo default
disabled

Prevents the panel from being toggled.

booleanfalse
render

Replaces the root element — e.g. an <li> when the collapsible is a list item.

ReactElement | (props, state) => ReactElementNo default

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.

PropTypeDefault
hiddenUntilFound

Keeps the closed panel in the DOM with hidden="until-found" so find-in-page can reveal it.

booleanfalse
keepMounted

Keeps the closed panel in the DOM. Ignored when hiddenUntilFound is set.

booleanfalse

Data attributes

AttributeOnPresent when
data-open / data-closedroot, panelThe panel is open / closed.
data-panel-opentriggerThe panel is open.
data-starting-style / data-ending-styleroot, panelThe panel is animating in / out.