Collapsible

A single expandable section with an animated height transition. Use it to progressively disclose content without navigating away.

Preview

A design system is a collection of reusable components, guidelines, and tokens that teams use to build consistent user interfaces.

Motion

PropertyEnterExit
height0 → auto (200ms ease)auto → 0 (200ms ease)
opacity0 → 1 (200ms ease)1 → 0 (200ms ease)
chevron rotate0° → 180°180° → 0°

Implementation

Collapsible.tsx
tsx
import { Collapsible } from "@/components/ui/Collapsible";

// Uncontrolled
<Collapsible title="Advanced settings">
  <p>These settings affect performance and security.</p>
</Collapsible>

// Open by default
<Collapsible title="Details" defaultOpen>
  <p>This section is open by default.</p>
</Collapsible>

// Controlled
const [open, setOpen] = useState(false);
<Collapsible title="More info" open={open} onOpenChange={setOpen}>
  <p>Controlled content.</p>
</Collapsible>

Props

PropTypeDefaultDescription
title*React.ReactNodeTrigger label rendered in the header button.
children*React.ReactNodeContent revealed when open.
defaultOpenbooleanfalseInitial open state for uncontrolled usage.
openbooleanControlled open state. Pair with onOpenChange.
onOpenChange(open: boolean) => voidCalled when the open state changes.
classNamestringAdditional classes on the root element.
contentClassNamestringAdditional classes on the content wrapper.

ARIA roles

ElementRoleKey attributes
Triggerbutton (implicit)aria-expanded (true/false), aria-controls
Contentregionaria-labelledby (trigger id)

Keyboard

KeyAction
Enter / SpaceToggle open or closed when trigger is focused
TabMove focus into the content area (first interactive element) when open
Shift+TabReturn focus to the trigger from the content area

Accessibility

  • The trigger button uses aria-expanded to communicate open/closed state to screen readers.
  • The content region is linked via aria-controls — screen readers can jump directly to the content.
  • Content uses role="region" so it appears as a landmark when open.
  • Keyboard: Enter or Space activates the trigger; Tab moves focus into the content when open.
  • The animated height uses framer-motion with AnimatePresence — content is truly removed from the DOM when closed, not just hidden.