Shadcn Collapsible
Free shadcn/ui collapsible for React — built on Radix. An accessible show/hide disclosure: a controlled toggle, a chevron trigger, show-more text, a sidebar filter group, nested trees, and expandable rows.
Installation
{
"registries": {
"@designrevision": "https://registry.designrevision.com/r/{name}.json"
}
}
Add to your existing components.json. Requires shadcn/ui v2.3+.
Packages
Props
No props documented yet.
"use client"
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
function Collapsible({
...props
}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
}
function CollapsibleTrigger({
...props
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
return (
<CollapsiblePrimitive.CollapsibleTrigger
data-slot="collapsible-trigger"
{...props}
/>
)
}
function CollapsibleContent({
...props
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
return (
<CollapsiblePrimitive.CollapsibleContent
data-slot="collapsible-content"
{...props}
/>
)
}
export { Collapsible, CollapsibleTrigger, CollapsibleContent }
Examples
Basic
The canonical collapsible — a toggle that reveals extra items, controlled with open state.
Packages
Props
No props documented yet.
"use client"
import * as React from "react"
import { ChevronsUpDown } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible"
export default function CollapsibleDemo01() {
const [open, setOpen] = React.useState(false)
return (
<Collapsible
open={open}
onOpenChange={setOpen}
className="flex w-full max-w-sm flex-col gap-2"
>
<div className="flex items-center justify-between gap-4 px-1">
<h4 className="text-sm font-semibold">
@peduarte starred 3 repositories
</h4>
<CollapsibleTrigger asChild>
<Button variant="ghost" size="icon" className="size-8">
<ChevronsUpDown />
<span className="sr-only">Toggle</span>
</Button>
</CollapsibleTrigger>
</div>
<div className="rounded-md border px-4 py-2 font-mono text-sm">
@radix-ui/primitives
</div>
<CollapsibleContent className="flex flex-col gap-2">
<div className="rounded-md border px-4 py-2 font-mono text-sm">
@radix-ui/colors
</div>
<div className="rounded-md border px-4 py-2 font-mono text-sm">
@stitches/react
</div>
</CollapsibleContent>
</Collapsible>
)
}
Chevron trigger
A full-width trigger with a chevron that rotates on open, via `group-data-[state=open]`.
Packages
Props
No props documented yet.
import { ChevronRight } from "lucide-react"
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible"
export default function CollapsibleChevron01() {
return (
<Collapsible className="w-full max-w-sm rounded-md border">
<CollapsibleTrigger className="group flex w-full items-center justify-between p-4 text-sm font-medium">
What is your refund policy?
<ChevronRight className="size-4 transition-transform group-data-[state=open]:rotate-90" />
</CollapsibleTrigger>
<CollapsibleContent className="text-muted-foreground px-4 pb-4 text-sm">
We offer a 30-day money-back guarantee on all plans — no questions
asked. Refunds are processed to your original payment method.
</CollapsibleContent>
</Collapsible>
)
}
Show more / less
A read-more pattern — the trigger label flips between Show more and Show less.
Packages
Props
No props documented yet.
"use client"
import * as React from "react"
import { Button } from "@/components/ui/button"
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible"
export default function CollapsibleText01() {
const [open, setOpen] = React.useState(false)
return (
<Collapsible open={open} onOpenChange={setOpen} className="w-full max-w-md">
<p className="text-sm leading-relaxed">
Embla is a bare-bones carousel library with great fluid motion and
awesome swipe precision. It is library agnostic, dependency free, and
100% open source.
</p>
<CollapsibleContent>
<p className="mt-2 text-sm leading-relaxed">
It ships a tiny core with optional plugins for autoplay, auto-scroll,
class names, and more. The shadcn Carousel wraps it with styling and
accessible controls.
</p>
</CollapsibleContent>
<CollapsibleTrigger asChild>
<Button variant="link" className="mt-1 h-auto p-0">
{open ? "Show less" : "Show more"}
</Button>
</CollapsibleTrigger>
</Collapsible>
)
}
Filter group
A collapsible sidebar filter — a category group of checkboxes that folds away.
Packages
Props
No props documented yet.
import { ChevronDown } from "lucide-react"
import { Checkbox } from "@/components/ui/checkbox"
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible"
import { Label } from "@/components/ui/label"
const categories = ["Electronics", "Clothing", "Home & Garden", "Sports"]
export default function CollapsibleFilter01() {
return (
<div className="w-full max-w-xs">
<Collapsible defaultOpen className="rounded-md border p-3">
<CollapsibleTrigger className="group flex w-full items-center justify-between text-sm font-medium">
Category
<ChevronDown className="size-4 transition-transform group-data-[state=open]:rotate-180" />
</CollapsibleTrigger>
<CollapsibleContent className="mt-3 space-y-2.5">
{categories.map((category) => (
<div key={category} className="flex items-center gap-2">
<Checkbox id={category} />
<Label htmlFor={category} className="text-sm font-normal">
{category}
</Label>
</div>
))}
</CollapsibleContent>
</Collapsible>
</div>
)
}
Nested (file tree)
Collapsibles inside collapsibles — a folder/file tree with rotating chevrons.
Packages
Props
No props documented yet.
import { ChevronRight, FileText, Folder } from "lucide-react"
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible"
export default function CollapsibleNested01() {
return (
<div className="w-full max-w-xs text-sm">
<Collapsible defaultOpen>
<CollapsibleTrigger className="group hover:bg-accent flex w-full items-center gap-1.5 rounded-md px-2 py-1.5">
<ChevronRight className="size-4 transition-transform group-data-[state=open]:rotate-90" />
<Folder className="size-4" />
src
</CollapsibleTrigger>
<CollapsibleContent className="ml-4 border-l pl-2">
<Collapsible>
<CollapsibleTrigger className="group hover:bg-accent flex w-full items-center gap-1.5 rounded-md px-2 py-1.5">
<ChevronRight className="size-4 transition-transform group-data-[state=open]:rotate-90" />
<Folder className="size-4" />
components
</CollapsibleTrigger>
<CollapsibleContent className="ml-4 border-l pl-2">
<div className="flex items-center gap-1.5 px-2 py-1.5">
<FileText className="size-4" />
button.tsx
</div>
<div className="flex items-center gap-1.5 px-2 py-1.5">
<FileText className="size-4" />
card.tsx
</div>
</CollapsibleContent>
</Collapsible>
<div className="flex items-center gap-1.5 px-2 py-1.5">
<FileText className="size-4" />
index.ts
</div>
</CollapsibleContent>
</Collapsible>
</div>
)
}
Expandable row
A list row that expands to reveal details — the order/record disclosure pattern.
Packages
Props
No props documented yet.
import { ChevronDown } from "lucide-react"
import { Badge } from "@/components/ui/badge"
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible"
export default function CollapsibleDetails01() {
return (
<Collapsible className="w-full max-w-md rounded-lg border">
<CollapsibleTrigger className="group flex w-full items-center justify-between p-4">
<div className="flex items-center gap-3">
<span className="font-medium">Order #3201</span>
<Badge variant="secondary">Shipped</Badge>
</div>
<ChevronDown className="size-4 transition-transform group-data-[state=open]:rotate-180" />
</CollapsibleTrigger>
<CollapsibleContent className="space-y-1.5 border-t px-4 py-3 text-sm">
<div className="flex justify-between">
<span className="text-muted-foreground">Items</span>
<span>3</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Total</span>
<span>$148.00</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Tracking</span>
<span>1Z999AA10123456784</span>
</div>
</CollapsibleContent>
</Collapsible>
)
}
The Shadcn Collapsible is the simplest disclosure: an accessible show/hide for a single region, built on Radix. Three parts — root, trigger, content — and Radix handles the state, focus, and ARIA. The examples below cover a controlled toggle, a rotating chevron, show-more text, a filter group, nested trees, and expandable rows.
Three parts
<Collapsible>
<CollapsibleTrigger>Toggle</CollapsibleTrigger>
<CollapsibleContent>Hidden until opened.</CollapsibleContent>
</Collapsible>
Collapsible owns the open state, CollapsibleTrigger toggles it, and CollapsibleContent is shown or hidden.
Collapsible vs Accordion
They look similar but solve different problems:
- Collapsible — toggle one region open or closed.
- Accordion — a set of items where opening one can close the others (an FAQ, a settings list).
Reach for Collapsible when there's a single thing to reveal.
Controlled state
Pass open and onOpenChange to control it — useful for flipping a trigger label:
const [open, setOpen] = React.useState(false)
<Collapsible open={open} onOpenChange={setOpen}>
{/* … */}
<CollapsibleTrigger asChild>
<Button variant="link">{open ? "Show less" : "Show more"}</Button>
</CollapsibleTrigger>
</Collapsible>
For an uncontrolled collapsible, pass defaultOpen instead.
Rotating chevron
Put group on the trigger and key the icon rotation to the open state:
<CollapsibleTrigger className="group ...">
Section
<ChevronDown className="transition-transform group-data-[state=open]:rotate-180" />
</CollapsibleTrigger>
The trigger carries data-state="open" | "closed", so the chevron turns when it expands.
Animating the height
Radix exposes the content height as --radix-collapsible-content-height. Add keyframes that animate from 0 to that variable to get a smooth slide:
@keyframes collapsible-down { from { height: 0 } to { height: var(--radix-collapsible-content-height) } }
@keyframes collapsible-up { from { height: var(--radix-collapsible-content-height) } to { height: 0 } }
Apply them on data-[state=open] / data-[state=closed]. Without animation the section just toggles, which is fine for most uses.
Common patterns
- Show more / less — a read-more for long text (the Show more example).
- Filter group — a collapsible sidebar facet of checkboxes (the Filter group example).
- Nested — collapsibles inside collapsibles for a file tree (the Nested example).
- Expandable row — a list row that opens to show details (the Expandable row example).
Accessibility
Radix links the trigger to the content with aria-controls and aria-expanded, and manages the open state with the keyboard (Enter/Space toggles). Keep the trigger text meaningful so the control is clear when announced.