Collapsible
A single expand / collapse primitive: one trigger, one panel. You bring the trigger and the content; the panel fades in and out with the Yuno motion (the same as the Accordion). Reach for the Accordion when you have a set of grouped panels instead of one standalone toggle.
Anatomy
- 1Collapsible
The Root that owns the open state. Control it with open / onOpenChange so your trigger label and caret can react.
- 2CollapsibleTrigger
The control that toggles the panel. The Figma canonical is a ghost icon Button with a CaretUpDown (the static up/down chevron); a full-width header row with a CaretDown that rotates 180° is the section-header alternative. Pass asChild to wrap either.
- 3CollapsibleContent
The panel that opens. It fades in / out and stays overflow-hidden; the className you pass lands on the inner wrapper, so your padding is untouched by the animation.
States
Two states: closed and open. In the canonical layout a first item stays visible and the rest are revealed on toggle. Drive the state so the trigger reflects it.
Recipes
Ready-to-copy compositions covering the most common Yuno usages of this atom.
The canonical Figma layout: a titled header with a ghost CaretUpDown icon button, a first item always visible, and the rest revealed on toggle — each a bordered rounded-md box. Use it for 'show the other N' lists (accounts in a group, methods on a provider).
function ListToggle() {
const [open, setOpen] = React.useState(false);
return (
<Collapsible open={open} onOpenChange={setOpen} className="flex flex-col gap-2">
<div className="flex items-center justify-between px-4">
<span className="text-sm font-semibold text-foreground">Growth team · 3 accounts</span>
<CollapsibleTrigger asChild>
<Button variant="ghost" size="icon" aria-label="Toggle accounts">
<CaretUpDown weight="light" className="size-4" />
</Button>
</CollapsibleTrigger>
</div>
<div className="rounded-md border border-border px-4 py-2 text-sm">acct_main_us</div>
<CollapsibleContent className="flex flex-col gap-2">
<div className="rounded-md border border-border px-4 py-2 text-sm">acct_eu_2</div>
<div className="rounded-md border border-border px-4 py-2 text-sm">acct_latam_3</div>
</CollapsibleContent>
</Collapsible>
);
}Hide power-user options behind a full-width header row so the common path stays short. The revealed panel holds the extra fields (switches, inputs). Common in Connection and Routing setup forms; here the CaretDown rotates 180° on open.
<Collapsible open={open} onOpenChange={setOpen}>
<CollapsibleTrigger asChild>
<button type="button"
className="flex w-full items-center justify-between rounded-md py-2 text-sm font-medium text-foreground outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50">
Advanced settings
<CaretDown weight="light"
className={`size-4 transition-transform duration-200 ${open ? "rotate-180" : ""}`} />
</button>
</CollapsibleTrigger>
<CollapsibleContent className="flex flex-col gap-4 pt-3">
<div className="flex items-center justify-between">
<Label htmlFor="cap-3ds" className="font-normal">Force 3DS challenge</Label>
<Switch id="cap-3ds" />
</div>
<div className="flex items-center justify-between">
<Label htmlFor="cap-retry" className="font-normal">Retry on soft decline</Label>
<Switch id="cap-retry" defaultChecked />
</div>
</CollapsibleContent>
</Collapsible>Import
Copy this import line at the top of the file where you compose this atom.
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";Props
Everything else from the underlying HTML or Radix primitive is forwarded via ...props.
| Prop | Type | Default | Description |
|---|---|---|---|
| Collapsible.open / defaultOpen | boolean | — | Controlled or uncontrolled open state. Controlled needs onOpenChange (pass it so the trigger can react). |
| Collapsible.onOpenChange | (open: boolean) => void | — | Fires whenever the panel opens or closes. |
| Collapsible.disabled | boolean | false | Prevents toggling and marks the trigger disabled. |
| CollapsibleTrigger.asChild | boolean | false | Merges props onto the child element (Radix Slot). Pass when wrapping a Button or a native button. |
| CollapsibleContent.className | string | — | Merged onto the inner wrapper (your padding / spacing). The animation container above it stays overflow-hidden. |
| CollapsibleContent.forceMount | boolean | — | Keeps the content in the DOM when closed (for animations or SEO). Rarely needed. |
When to use
- Standalone 'Show more / less' toggles inside a card or a table row.
- Advanced or optional settings tucked behind a header so the common path stays short.
- Any single block of secondary content you want hidden by default.
When not to use
- A set of grouped panels that share a header style — use Accordion.
- Structural navigation between areas — use Sidebar or Tabs.
- Content the user must see to complete the task — don't hide it by default.
Usage
- Control the open state (open / onOpenChange) so the trigger reflects it.
- Give the trigger a clear expand affordance — a CaretUpDown ghost icon button (canonical), or a CaretDown that rotates 180° for a section header.
- Keep the trigger obviously clickable (a Button or a full-width header row).
- Don't hide information the user needs to finish the task behind a default-closed panel.
- Don't stack several Collapsibles that share one header style — that is an Accordion.
- Don't put the revealed items in mono — the Figma shows mono, but Yuno is Geist Sans everywhere.
Related
Cross-links to atoms and patterns you may reach for next.