Light

Collapsible

An interactive component which expands/collapses a panel.

Spec · from metadata

When to use

  • Showing/hiding optional or secondary content
  • Expandable sections in settings or configurations
  • Sidebar sub-menus or nested navigation

When not to use

  • Multiple collapsible sections in a list — use Accordion instead
  • Tab-based content switching — use Tabs instead
  • Modal content — use Dialog or Sheet instead

Anti-patterns

Avoid<Collapsible>
  <button onClick={toggle}>Toggle</button>
  <div>{open && <p>Content</p>}</div>
</Collapsible>
Prefer<Collapsible>
  <CollapsibleTrigger>Toggle</CollapsibleTrigger>
  <CollapsibleContent>
    <p>Content</p>
  </CollapsibleContent>
</Collapsible>

Use CollapsibleTrigger and CollapsibleContent — they handle ARIA attributes and state management.

Accessibility

  • Required ARIAaria-expanded (auto on trigger)aria-controls (auto)
  • Screen readerRadix Collapsible manages aria-expanded on trigger and aria-controls linking trigger to content.

Import

import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from "@timelycare/helix-ui"

Props

interface CollapsibleProps {
  open?: boolean
  defaultOpen?: boolean
  onOpenChange?: (open: boolean) => void
  disabled?: boolean
  className?: string
  children: React.ReactNode
}

interface CollapsibleTriggerProps {
  asChild?: boolean
  className?: string
  children: React.ReactNode
}

interface CollapsibleContentProps {
  forceMount?: boolean
  className?: string
  children: React.ReactNode
}

Variants

Collapsible is a headless/behavioral component—visual styling comes from content composition.

PatternUse For
BasicSimple show/hide content
With IconChevron rotation indicating state
FAQ StyleBorder-bottom items with expand

States

StateImplementation
ClosedContent hidden (default)
OpenContent visible

State Indicators (add to trigger)

  • Chevron rotation (rotate-180 when open)
  • Plus/minus icon swap
  • Text change ("Show more" → "Show less")

Icons

IconUse For
ChevronDownMost common - rotate on open
ChevronsUpDownToggle indicator
Plus / MinusAdd/remove style

Common Patterns

Basic Collapsible

<Collapsible>
  <CollapsibleTrigger>Toggle content</CollapsibleTrigger>
  <CollapsibleContent>
    <p>This content can be shown or hidden.</p>
  </CollapsibleContent>
</Collapsible>

With Button Trigger and Icon

import { ChevronsUpDown } from "lucide-react"

<Collapsible className="w-[350px] space-y-2">
  <div className="flex items-center justify-between px-4">
    <h4 className="text-sm font-semibold">@peduarte starred 3 repositories</h4>
    <CollapsibleTrigger asChild>
      <Button variant="ghost" size="sm" className="w-9 p-0">
        <ChevronsUpDown className="h-4 w-4" />
        <span className="sr-only">Toggle</span>
      </Button>
    </CollapsibleTrigger>
  </div>
  <div className="rounded-md border px-4 py-3 font-mono text-sm">
    @radix-ui/primitives
  </div>
  <CollapsibleContent className="space-y-2">
    <div className="rounded-md border px-4 py-3 font-mono text-sm">
      @radix-ui/colors
    </div>
    <div className="rounded-md border px-4 py-3 font-mono text-sm">
      @stitches/react
    </div>
  </CollapsibleContent>
</Collapsible>

Controlled State

const [isOpen, setIsOpen] = useState(false)

<Collapsible open={isOpen} onOpenChange={setIsOpen}>
  <CollapsibleTrigger className="flex items-center gap-2">
    <span>{isOpen ? "Hide" : "Show"} details</span>
    <ChevronDown 
      className={`h-4 w-4 transition-transform ${isOpen ? "rotate-180" : ""}`} 
    />
  </CollapsibleTrigger>
  <CollapsibleContent>
    <div className="pt-2">
      <p>Additional details are now visible.</p>
    </div>
  </CollapsibleContent>
</Collapsible>

FAQ Item Style

<Collapsible className="border-b">
  <CollapsibleTrigger className="flex w-full items-center justify-between py-4 font-medium">
    What is your refund policy?
    <ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
  </CollapsibleTrigger>
  <CollapsibleContent>
    <div className="pb-4">
      <p className="text-sm text-muted-foreground">
        We offer a 30-day money-back guarantee on all purchases.
      </p>
    </div>
  </CollapsibleContent>
</Collapsible>

Animation

For smooth expand/collapse, add these to CollapsibleContent:

<CollapsibleContent className="overflow-hidden data-[state=closed]:animate-collapse-up data-[state=open]:animate-collapse-down">

Requires animation keyframes in Tailwind config:

// tailwind.config.js
keyframes: {
  "collapse-down": {
    from: { height: "0" },
    to: { height: "var(--radix-collapsible-content-height)" },
  },
  "collapse-up": {
    from: { height: "var(--radix-collapsible-content-height)" },
    to: { height: "0" },
  },
}

Accessibility

  • Trigger has aria-expanded automatically
  • Content linked via aria-controls
  • Enter/Space to toggle when focused
  • Use sr-only for icon-only triggers

Gotchas

ProblemSolution
Content visible initiallyDon't use defaultOpen unless intended
Icon not rotatingAdd transition-transform and conditional rotate-180
Animation jumpyAdd overflow-hidden to CollapsibleContent
Need custom trigger elementUse asChild prop on CollapsibleTrigger

See Also


Last updated: February 9, 2026