Collapsible
A single section that can be expanded or collapsed. For multiple independent sections use Accordion with type='multiple'.
Show-more list (chevron rotates on open)
@peduarte starred 3 repositories
Advanced options (outline trigger)
Installation
pnpm dlx @hex-core/cli add collapsibleExpandable table row detail
Drill into a row without leaving the table. Each Collapsible owns its own tbody (multiple tbody elements per table is valid HTML and gives Radix Slot a real element to clone via asChild). Chevron rotates via the trigger's data-state attribute.
import { useState } from "react";
import { ChevronRight } from "lucide-react";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import { TableCell, TableRow } from "@/components/ui/table";
interface Order {
id: string;
customer: string;
total: string;
items: { sku: string; qty: number }[];
}
export function OrderRow({ order }: { order: Order }) {
const [open, setOpen] = useState(false);
return (
<Collapsible asChild open={open} onOpenChange={setOpen}>
<tbody>
<TableRow>
<TableCell className="w-8">
<CollapsibleTrigger className="group inline-flex h-6 w-6 items-center justify-center rounded hover:bg-muted" aria-label="Toggle line items">
<ChevronRight className="h-4 w-4 transition-transform group-data-[state=open]:rotate-90" />
</CollapsibleTrigger>
</TableCell>
<TableCell className="font-mono text-xs">{order.id}</TableCell>
<TableCell>{order.customer}</TableCell>
<TableCell className="text-right">{order.total}</TableCell>
</TableRow>
<CollapsibleContent asChild>
<TableRow className="bg-muted/40">
<TableCell />
<TableCell colSpan={3} className="py-3">
<ul className="space-y-1 text-sm">
{order.items.map((line) => (
<li key={line.sku} className="flex justify-between">
<span className="font-mono text-xs">{line.sku}</span>
<span>x{line.qty}</span>
</li>
))}
</ul>
</TableCell>
</TableRow>
</CollapsibleContent>
</tbody>
</Collapsible>
);
}Nested advanced settings
Hide power-user options behind a labelled trigger inside a Card section so the default form stays focused.
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
import { ChevronDown } from "lucide-react";
export function DeploySettings() {
return (
<Card>
<CardHeader>
<CardTitle>Deploy</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="branch">Branch</Label>
<Input id="branch" defaultValue="main" />
</div>
<Collapsible className="rounded-md border">
<CollapsibleTrigger className="group flex w-full items-center justify-between px-4 py-3 text-sm font-medium">
Advanced
<ChevronDown className="h-4 w-4 transition-transform group-data-[state=open]:rotate-180" />
</CollapsibleTrigger>
<CollapsibleContent className="space-y-3 border-t px-4 py-3">
<div className="space-y-2">
<Label htmlFor="build-cmd">Build command</Label>
<Input id="build-cmd" defaultValue="pnpm build" />
</div>
<div className="space-y-2">
<Label htmlFor="node">Node version</Label>
<Input id="node" defaultValue="20.x" />
</div>
<div className="flex items-center justify-between">
<Label htmlFor="telemetry" className="font-normal">Send build telemetry</Label>
<Switch id="telemetry" defaultChecked />
</div>
</CollapsibleContent>
</Collapsible>
</CardContent>
</Card>
);
}API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | — | Controlled open state |
defaultOpen | boolean | false | Default open state |
onOpenChange | function | — | Callback on open change: (open: boolean) => void |
disabled | boolean | false | Disable toggling |
AI Guidance
When to use
Use for a single show-more/show-less section: 'View full details', 'Advanced settings', preview cards with expandable notes.
When not to use
Don't use for multiple related sections (use Accordion). Don't use for overlays (use Dialog/Popover). Don't use for navigation (use DropdownMenu).
Common mistakes
- Using Collapsible for multiple sections instead of Accordion
- Missing asChild when passing a Button as trigger
- Not animating content height (Radix exposes --radix-collapsible-content-height for CSS keyframes)
Accessibility
Radix sets aria-expanded on the trigger and aria-controls → content id. Trigger is keyboard-operable (Enter/Space).
Related components
Token budget: 250
Verified against @hex-core/components@1.12.0