Collapsible
A single toggleable open/close region with an animated height, built on Base UI Collapsible.
- Status
- Since
0.1.0- Accessibility pattern
- APG disclosure
Last updated
Unlimited projects, priority support, advanced analytics, and SSO. Billed annually with a 14-day free trial.
Install
Add Collapsible from the VegaStack registry. The CLI verifies the item's integrity hash before writing it.
pnpm dlx shadcn@latest add @vegastack/collapsibleUsage
import {
Collapsible,
CollapsibleTrigger,
CollapsibleContent,
} from "@/components/ui/collapsible";
import { ChevronDown } from "lucide-react";
<Collapsible defaultOpen>
<CollapsibleTrigger>
Show details
<ChevronDown />
</CollapsibleTrigger>
<CollapsibleContent>
<p className="pt-2">The revealed content goes here.</p>
</CollapsibleContent>
</Collapsible>;Anatomy
Collapsible is a compound component built on Base UI's Collapsible. Compose the parts inside the root:
<Collapsible>
<CollapsibleTrigger>
Label
<ChevronDown />
</CollapsibleTrigger>
<CollapsibleContent>{/* revealed content */}</CollapsibleContent>
</Collapsible>Collapsible— the root (data-slot="collapsible"); owns the open/close state (open/defaultOpen/onOpenChange) and thedisabledflag.CollapsibleTrigger— the native<button>that toggles the region (data-slot="collapsible-trigger"). Open state is exposed asdata-panel-open; a composed chevron rotates via that attribute. Wired to the panel witharia-controls+aria-expanded.CollapsibleContent— the panel (data-slot="collapsible-content") revealed when open. Animates its height between0and the measured content height via Base UI's--collapsible-panel-heightCSS var.
Padding: the panel itself is
overflow-hiddenand height-animated, so apply padding to an inner wrapper (e.g.<p className="pt-2">) rather than toCollapsibleContent— padding on the panel fights the height transition.
Examples
States — closed by default, open by default, and disabled.
This region starts expanded and collapses on click.
Controlled
Drive the open state yourself with open + onOpenChange (instead of the uncontrolled defaultOpen). Useful when another control toggles the region or when the open state lives in your own store.
State owned by the parent: false
const [open, setOpen] = useState(false);
<Collapsible open={open} onOpenChange={setOpen}>
<CollapsibleTrigger>
Show details
<ChevronDown />
</CollapsibleTrigger>
<CollapsibleContent>…</CollapsibleContent>
</Collapsible>;Keep mounted for find-in-page
By default the panel is unmounted while closed (keepMounted={false}). Pass keepMounted together with hiddenUntilFound on CollapsibleContent to keep the content in the DOM while collapsed — the browser’s find-in-page and search crawlers can then reach it and auto-expand the region.
With keepMounted and hiddenUntilFound, this panel stays in the DOM while closed, so the browser’s find-in-page (and crawlers) can reach it and auto-expand the region.
API Reference
Collapsible, CollapsibleTrigger, and CollapsibleContent add no props of
their own — each accepts everything the corresponding Base UI
Collapsible part accepts
(Root, Trigger, Panel): open / defaultOpen / onOpenChange and
disabled on the root, keepMounted / hiddenUntilFound on the panel, plus
className, ref, and render everywhere.
Accessibility
- The trigger renders a native
<button>witharia-expandedreflecting the open state andaria-controlspointing at the panel — assistive tech announces it as an expandable control. - The panel is removed from the DOM while closed by default (
keepMounted={false}); passkeepMounted/hiddenUntilFoundonCollapsibleContentto keep it findable by in-page search (see the Keep mounted for find-in-page example). - The trigger is a native
<button>, so it keeps the browser’s focus outline and never setsoutline: none. Its pointer affordance is the row wash (surface-2on hover,surface-3pressed) — a disclosure is not a link, so it never underlines on hover. disabledon the root removes interaction from the whole region.
| Key | Action |
|---|---|
| Enter / Space | Toggle the region open or closed when the trigger is focused. |
| Tab / Shift + Tab | Move focus to and from the trigger, then into the revealed panel content. |
| Contract | States tested |
|---|---|
| Behaviour | default, disabled, open |
| Accessibility | semantic-html |
| Visual | default, hover, disabled, open |