Skip to content
Component installs need the registry setup— the Base UI shadcn project, the @vegastack namespace and the Cloudflare Access service token.
VegaStack Design

Collapsible

A single toggleable open/close region with an animated height, built on Base UI Collapsible.

Status
stable
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/collapsible

Usage

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 — data-slot="collapsible"
CollapsibleContent — data-slot="collapsible-content"
CollapsibleTrigger — data-slot="collapsible-trigger"
<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 the disabled flag.
  • CollapsibleTrigger — the native <button> that toggles the region (data-slot="collapsible-trigger"). Open state is exposed as data-panel-open; a composed chevron rotates via that attribute. Wired to the panel with aria-controls + aria-expanded.
  • CollapsibleContent — the panel (data-slot="collapsible-content") revealed when open. Animates its height between 0 and the measured content height via Base UI's --collapsible-panel-height CSS var.

Padding: the panel itself is overflow-hidden and height-animated, so apply padding to an inner wrapper (e.g. <p className="pt-2">) rather than to CollapsibleContent — 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.

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> with aria-expanded reflecting the open state and aria-controls pointing at the panel — assistive tech announces it as an expandable control.
  • The panel is removed from the DOM while closed by default (keepMounted={false}); pass keepMounted/hiddenUntilFound on CollapsibleContent to 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 sets outline: none. Its pointer affordance is the row wash (surface-2 on hover, surface-3 pressed) — a disclosure is not a link, so it never underlines on hover.
  • disabled on the root removes interaction from the whole region.
KeyAction
Enter / SpaceToggle the region open or closed when the trigger is focused.
Tab / Shift + TabMove focus to and from the trigger, then into the revealed panel content.
ContractStates tested
Behaviourdefault, disabled, open
Accessibilitysemantic-html
Visualdefault, hover, disabled, open

Do / Don't

Do
Use a Collapsible for a single, self-contained show/hide region — extra detail, an optional form section, a 'read more'.
Don't
Stack several Collapsibles to fake an accordion where only one opens at a time — use an Accordion, which coordinates a single-open group.

On this page