Collapsible
A trigger that shows and hides one panel.
shadcn's base-nova collapsible, unchanged — it draws nothing of its own, so the trigger and the panel look like whatever you put in them. It is published so that every primitive a Vibra block names comes from this registry. The trigger carries aria-expanded, and aria-controls while the panel shows. To animate the panel, time its height on --collapsible-panel-height with a motion token, as the Animated example does, and it stops with everything else when the reader asks for less motion.
Install
npx shadcn@latest add @vibra/collapsibleNeeds the @vibra registry in your components.json — set it up once.
Examples
import { ChevronsUpDownIcon } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
const HEADERS = [
{ name: "x-request-id", value: "req_8f2c41d9" },
{ name: "x-ratelimit-remaining", value: "4,812" },
{ name: "content-type", value: "application/json" },
]
export default function CollapsibleDemo() {
return (
<Collapsible className="flex w-full max-w-sm flex-col gap-2">
<div className="flex items-center justify-between gap-4">
<p className="text-sm">
<span className="font-mono text-xs">POST /v1/events</span>
<span className="text-muted-foreground"> · 201 in 84 ms</span>
</p>
<CollapsibleTrigger render={<Button variant="ghost" size="sm" />}>
<ChevronsUpDownIcon data-icon="inline-start" aria-hidden="true" />
Headers
</CollapsibleTrigger>
</div>
<CollapsibleContent>
<dl className="flex flex-col gap-1 rounded-lg bg-surface p-3 ring-1 ring-border">
{HEADERS.map((header) => (
<div key={header.name} className="flex justify-between gap-4 text-xs">
<dt className="font-mono text-muted-foreground">{header.name}</dt>
<dd className="font-mono tabular-nums">{header.value}</dd>
</div>
))}
</dl>
</CollapsibleContent>
</Collapsible>
)
}Icon that turns
An icon-only trigger with a fixed name. The chevron turns on the fast token, at once under reduced motion, and points the same way in either direction.
"use client"
import { ChevronDownIcon, LandmarkIcon } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
const BREAKDOWN = [
{ label: "Charges", amount: "€3,612.80" },
{ label: "Refunds", amount: "−€124.64" },
{ label: "Processing fees", amount: "−€87.75" },
]
const PANEL =
"h-(--collapsible-panel-height) overflow-hidden transition-[height] duration-(--duration-base) ease-(--ease-standard) data-starting-style:h-0 data-ending-style:h-0"
// An icon-only trigger: its name is fixed, and aria-expanded says whether the
// breakdown is showing. The chevron turns half a circle on the fast token —
// at once, for a reader who asked for less motion — and points the same way
// in either reading direction.
export default function CollapsibleIcon() {
return (
<Collapsible className="w-full max-w-sm rounded-lg bg-card ring-1 ring-border">
<div className="flex items-center gap-3 p-3">
<span className="flex size-8 shrink-0 items-center justify-center rounded-md bg-muted">
<LandmarkIcon aria-hidden="true" className="size-4" />
</span>
<div className="flex min-w-0 flex-1 flex-col">
<span className="truncate text-sm font-medium">Payout to Meridian Bank ··0917</span>
<span className="text-xs text-muted-foreground">Pending · arrives Mon 7 Sep</span>
</div>
<span className="text-sm font-medium tabular-nums">€3,400.41</span>
<CollapsibleTrigger
render={<Button variant="ghost" size="icon-sm" aria-label="Payout breakdown" />}
>
<ChevronDownIcon
aria-hidden="true"
className="transition-transform duration-(--duration-fast) ease-(--ease-standard) group-data-[panel-open]/button:rotate-180"
/>
</CollapsibleTrigger>
</div>
<CollapsibleContent className={PANEL}>
<dl className="mx-3 mb-3 flex flex-col gap-1.5 border-t border-border pt-3 text-sm">
{BREAKDOWN.map((line) => (
<div key={line.label} className="flex justify-between gap-4">
<dt className="text-muted-foreground">{line.label}</dt>
<dd className="tabular-nums">{line.amount}</dd>
</div>
))}
<div className="flex justify-between gap-4 font-medium">
<dt>Paid out</dt>
<dd className="tabular-nums">€3,400.41</dd>
</div>
</dl>
</CollapsibleContent>
</Collapsible>
)
}Animated
Options that fold open on the motion token, instantly for a reader who asked for less motion, with room kept for the focus outline while the panel clips.
"use client"
import * as React from "react"
import { SlidersHorizontalIcon } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
import { FieldLegend, FieldSet } from "@/components/ui/field"
import { Label } from "@/components/ui/label"
// Base UI measures the panel into --collapsible-panel-height; the height runs
// from and to zero on the motion token, which is 0ms for a reader who asked
// for less motion, so for them the options are simply there. The panel clips
// while it moves, so -m-1 here and p-1 inside leave the focus outline its 4px.
const PANEL =
"-m-1 h-(--collapsible-panel-height) overflow-hidden transition-[height] duration-(--duration-base) ease-(--ease-standard) data-starting-style:h-0 data-ending-style:h-0"
const EXTRAS = [
{ id: "refunded", label: "The 3 refunded orders" },
{ id: "cancelled", label: "The 4 cancelled orders" },
{ id: "emails", label: "Customer email addresses" },
]
export default function CollapsibleAnimated() {
const [queued, setQueued] = React.useState(false)
return (
<form
className="flex w-full max-w-sm flex-col gap-4"
onSubmit={(event) => {
event.preventDefault()
setQueued(true)
}}
>
<div className="flex flex-col gap-1">
<h3 className="text-sm font-semibold">Export August orders</h3>
<p className="text-sm text-muted-foreground">
<span className="tabular-nums">77</span> orders placed 1–31 August, as one CSV file.
</p>
</div>
<Collapsible className="flex flex-col gap-3">
<CollapsibleTrigger render={<Button variant="outline" size="sm" className="w-fit" />}>
<SlidersHorizontalIcon data-icon="inline-start" aria-hidden="true" />
More options
</CollapsibleTrigger>
<CollapsibleContent className={PANEL}>
<FieldSet className="gap-2.5 p-1">
<FieldLegend variant="label">Also include</FieldLegend>
{EXTRAS.map((extra) => (
<div key={extra.id} className="flex items-center gap-2.5">
<Checkbox id={`export-${extra.id}`} name={extra.id} />
<Label htmlFor={`export-${extra.id}`} className="font-normal">
{extra.label}
</Label>
</div>
))}
</FieldSet>
</CollapsibleContent>
</Collapsible>
<div className="flex flex-wrap items-center gap-x-3 gap-y-2">
<Button type="submit">Export CSV</Button>
<p role="status" className="text-sm text-muted-foreground">
{queued ? "Queued. We’ll email orders-2026-08.csv." : ""}
</p>
</div>
</form>
)
}Show more
Three of twelve, with the hidden count in the button's words. The rest are hidden until found, so find in page opens the list.
"use client"
import * as React from "react"
import { Button } from "@/components/ui/button"
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
const INVOICES = [
{ number: "INV-100055", company: "Fathom Partners", amount: "$1,272.00", due: "Sep 5" },
{ number: "INV-100008", company: "Northwind Studio", amount: "$168.00", due: "Sep 5" },
{ number: "INV-100135", company: "Foxglove Analytics", amount: "$7,056.00", due: "Sep 7" },
{ number: "INV-100025", company: "Northwind Studio", amount: "$168.00", due: "Sep 7" },
{ number: "INV-100117", company: "Northwind Media", amount: "$1,224.00", due: "Sep 8" },
{ number: "INV-100004", company: "Meridian Group", amount: "$108.00", due: "Sep 9" },
{ number: "INV-100044", company: "Alder Robotics", amount: "$864.00", due: "Sep 13" },
{ number: "INV-100131", company: "Trellis Partners", amount: "$1,320.00", due: "Sep 14" },
{ number: "INV-100018", company: "Fathom Media", amount: "$776.85", due: "Sep 14" },
{ number: "INV-100129", company: "Cobalt Digital", amount: "$16,416.00", due: "Sep 15" },
{ number: "INV-100255", company: "Trellis Labs", amount: "$4,992.00", due: "Sep 16" },
{ number: "INV-100160", company: "Quarry Group", amount: "$144.00", due: "Sep 17" },
]
const SHOWN = 3
const PANEL =
"h-(--collapsible-panel-height) overflow-hidden transition-[height] duration-(--duration-base) ease-(--ease-standard) data-starting-style:h-0 data-ending-style:h-0"
function Row({ invoice }: { invoice: (typeof INVOICES)[number] }) {
return (
<li className="flex items-center justify-between gap-4 py-2 text-sm">
<span className="flex min-w-0 flex-col">
<span className="truncate font-medium">{invoice.company}</span>
<span className="font-mono text-xs text-muted-foreground">{invoice.number}</span>
</span>
<span className="flex shrink-0 flex-col items-end">
<span className="tabular-nums">{invoice.amount}</span>
<span className="text-xs text-muted-foreground tabular-nums">Due {invoice.due}</span>
</span>
</li>
)
}
// The count is in the button's words, so nobody opens it to find out. The rest
// wait hidden-until-found: the browser's find in page reaches INV-100129 and
// opens the list on the way.
export default function CollapsibleShowMore() {
const [open, setOpen] = React.useState(false)
const hidden = INVOICES.length - SHOWN
return (
<Collapsible open={open} onOpenChange={setOpen} className="flex w-full max-w-sm flex-col">
<h3 id="due-soon" className="text-sm font-semibold">
Due in the next two weeks · <span className="tabular-nums">{INVOICES.length}</span> invoices
</h3>
<ul aria-labelledby="due-soon" className="divide-y divide-border">
{INVOICES.slice(0, SHOWN).map((invoice) => (
<Row key={invoice.number} invoice={invoice} />
))}
</ul>
<CollapsibleContent hiddenUntilFound className={PANEL}>
<ul aria-label={`${hidden} more invoices due in the next two weeks`} className="divide-y divide-border border-t">
{INVOICES.slice(SHOWN).map((invoice) => (
<Row key={invoice.number} invoice={invoice} />
))}
</ul>
</CollapsibleContent>
<CollapsibleTrigger render={<Button variant="ghost" size="sm" className="mt-1 w-fit" />}>
{open ? "Show fewer" : `Show ${hidden} more`}
</CollapsibleTrigger>
</Collapsible>
)
}Settings group
Groups that say what they hold while shut: "3 of 4 on" is part of the trigger's name, and it counts again as a switch flips.
"use client"
import * as React from "react"
import { ChevronDownIcon } from "lucide-react"
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
import { Label } from "@/components/ui/label"
import { Switch } from "@/components/ui/switch"
const GROUPS = [
{
id: "email",
title: "Email",
settings: [
{ id: "digest", label: "Weekly revenue digest", on: true },
{ id: "paid", label: "An invoice is paid", on: true },
{ id: "failed", label: "A payment fails", on: true },
{ id: "mentions", label: "Someone mentions me", on: false },
],
},
{
id: "app",
title: "In the app",
settings: [
{ id: "assigned", label: "A task is assigned to me", on: true },
{ id: "device", label: "A new device signs in", on: true },
{ id: "releases", label: "Release notes", on: false },
],
},
]
const PANEL =
"h-(--collapsible-panel-height) overflow-hidden transition-[height] duration-(--duration-base) ease-(--ease-standard) data-starting-style:h-0 data-ending-style:h-0"
// Each group says what it holds while shut — "3 of 4 on" is part of the
// trigger's name — so a reader checks a group without opening it.
export default function CollapsibleSettings() {
const [on, setOn] = React.useState<Record<string, boolean>>(() =>
Object.fromEntries(GROUPS.flatMap((group) => group.settings.map((setting) => [setting.id, setting.on])))
)
return (
<div className="flex w-full max-w-sm flex-col divide-y divide-border rounded-lg ring-1 ring-border">
{GROUPS.map((group, index) => {
const count = group.settings.filter((setting) => on[setting.id]).length
return (
<Collapsible key={group.id} defaultOpen={index === 0}>
<h3>
<CollapsibleTrigger className="group/settings flex w-full items-center gap-3 px-3 py-2.5 text-start text-sm font-medium focus-ring hover:bg-muted/50">
<span className="flex-1">{group.title}</span>{" "}
<span className="text-xs font-normal text-muted-foreground tabular-nums">
{count === 0 ? "All off" : `${count} of ${group.settings.length} on`}
</span>
<ChevronDownIcon
aria-hidden="true"
className="size-4 text-muted-foreground transition-transform duration-(--duration-fast) ease-(--ease-standard) group-data-[panel-open]/settings:rotate-180"
/>
</CollapsibleTrigger>
</h3>
<CollapsibleContent className={PANEL}>
<ul className="flex flex-col gap-3 px-3 pt-1 pb-3">
{group.settings.map((setting) => (
<li key={setting.id} className="flex items-center justify-between gap-4">
<Label htmlFor={`notify-${setting.id}`} className="font-normal">
{setting.label}
</Label>
<Switch
id={`notify-${setting.id}`}
checked={on[setting.id]}
onCheckedChange={(checked) => setOn((current) => ({ ...current, [setting.id]: checked }))}
/>
</li>
))}
</ul>
</CollapsibleContent>
</Collapsible>
)
})}
</div>
)
}FAQ
Questions as headings. Up, Down, Home and End step between them, and find in page opens the answer it lands in.
"use client"
import * as React from "react"
import { MinusIcon, PlusIcon } from "lucide-react"
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
import { KbdShortcut } from "@/components/ui/kbd-shortcut"
const QUESTIONS = [
{
question: "How are seats counted?",
answer: "Every member who can sign in takes a seat, and an invited member takes one from the day they accept.",
},
{
question: "Can we change plans mid-month?",
answer: "Yes. Moving up takes effect at once and is prorated to the day; moving down starts with your next invoice.",
},
{
question: "Is yearly billing cheaper?",
answer: "Starter is $187 a year instead of $228 billed monthly, and Team is $500 instead of $588.",
},
{
question: "What happens when a payment fails?",
answer: "We retry three times over seven days and email the billing contact each time. The workspace stays open.",
},
]
const STEPS: Record<string, (at: number, count: number) => number> = {
ArrowDown: (at, count) => (at + 1) % count,
ArrowUp: (at, count) => (at - 1 + count) % count,
Home: () => 0,
End: (_, count) => count - 1,
}
// Tab, Enter and Space come with the buttons. Up and Down step between the
// questions, Home and End jump to the first and the last, and the answers are
// hidden-until-found: find in page reaches a word in a shut answer and opens it.
export default function CollapsibleFaq() {
const list = React.useRef<HTMLDivElement>(null)
function onKeyDown(event: React.KeyboardEvent) {
const step = STEPS[event.key]
const triggers = [...(list.current?.querySelectorAll<HTMLElement>("[data-slot=collapsible-trigger]") ?? [])]
const at = triggers.indexOf(event.target as HTMLElement)
if (!step || at === -1) return
event.preventDefault()
triggers[step(at, triggers.length)].focus()
}
return (
<div className="flex w-full max-w-md flex-col gap-3">
<div ref={list} onKeyDown={onKeyDown} className="divide-y divide-border border-y border-border">
{QUESTIONS.map((item) => (
<Collapsible key={item.question}>
<h3>
<CollapsibleTrigger className="group/faq flex w-full items-center justify-between gap-4 rounded-md py-3 text-start text-sm font-medium focus-ring">
{item.question}
<PlusIcon aria-hidden="true" className="size-4 shrink-0 text-muted-foreground group-data-[panel-open]/faq:hidden" />
<MinusIcon aria-hidden="true" className="hidden size-4 shrink-0 text-muted-foreground group-data-[panel-open]/faq:block" />
</CollapsibleTrigger>
</h3>
<CollapsibleContent hiddenUntilFound>
<p className="pb-3 text-sm text-muted-foreground">{item.answer}</p>
</CollapsibleContent>
</Collapsible>
))}
</div>
<p className="flex flex-wrap items-center gap-1.5 text-xs text-muted-foreground">
<KbdShortcut keys="up" size="sm" />
<KbdShortcut keys="down" size="sm" />
move between questions,
<KbdShortcut keys="home" size="sm" />
<KbdShortcut keys="end" size="sm" />
jump to either end.
</p>
</div>
)
}Nested
A deploy that opens into its steps, and each step into its log. The step with a warning starts open.
"use client"
import { ChevronRightIcon, CircleCheckIcon, TriangleAlertIcon } from "lucide-react"
import { cn } from "@/lib/utils"
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
const STEPS = [
{ name: "Install dependencies", seconds: 6, log: ["npm ci", "added 1,284 packages in 5.8s"] },
{ name: "Build", seconds: 14, log: ["next build", "Compiled 212 routes in 13.1s"] },
{
name: "Run migrations",
seconds: 4,
warning: true,
log: ["apply 0042_order_refunds", "warning: orders_placed_at_idx exists"],
},
{ name: "Health checks", seconds: 8, log: ["GET /healthz 200 in 41 ms", "GET /api/orders 200 in 88 ms"] },
]
const TRIGGER =
"flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-start text-sm focus-ring hover:bg-muted"
// Points to the inline end while shut — mirrored for a right-to-left page —
// and turns down when open, on the fast token, so it turns at once for a
// reader who asked for less motion.
const CHEVRON =
"size-4 shrink-0 text-muted-foreground transition-transform duration-(--duration-fast) ease-(--ease-standard) rtl:rotate-180"
// A deploy opens into its steps, and each step into its log: two levels of the
// same collapsible, the inner ones inside the outer panel, so Tab walks them in
// reading order and a shut step's log is out of the way.
export default function CollapsibleNested() {
return (
<Collapsible defaultOpen className="w-full max-w-md rounded-lg bg-card p-1 ring-1 ring-border">
<CollapsibleTrigger className={cn(TRIGGER, "group/deploy")}>
<ChevronRightIcon aria-hidden="true" className={cn(CHEVRON, "group-data-[panel-open]/deploy:rotate-90")} />
<span className="min-w-0 flex-1 truncate">
Deploy <span className="font-mono text-xs">89a04ca</span> to production
</span>{" "}
<span className="shrink-0 text-xs text-muted-foreground tabular-nums">Passed in 32 s</span>
</CollapsibleTrigger>
<CollapsibleContent>
<ol className="flex flex-col gap-0.5 ps-5 pb-1">
{STEPS.map((step) => (
<li key={step.name}>
<Collapsible defaultOpen={step.warning}>
<CollapsibleTrigger className={cn(TRIGGER, "group/step")}>
<ChevronRightIcon
aria-hidden="true"
className={cn(CHEVRON, "group-data-[panel-open]/step:rotate-90")}
/>
{step.warning ? (
<TriangleAlertIcon aria-hidden="true" className="size-4 shrink-0 text-warning" />
) : (
<CircleCheckIcon aria-hidden="true" className="size-4 shrink-0 text-success" />
)}
<span className="min-w-0 flex-1 truncate">
{step.name}
{step.warning ? <span className="text-muted-foreground"> · 1 warning</span> : null}
</span>{" "}
<span className="shrink-0 text-xs text-muted-foreground tabular-nums">{step.seconds} s</span>
</CollapsibleTrigger>
<CollapsibleContent>
<pre className="my-0.5 ms-6 rounded-md bg-surface px-3 py-1.5 font-mono text-xs leading-5 whitespace-pre-wrap text-muted-foreground ring-1 ring-border">
{step.log.join("\n")}
</pre>
</CollapsibleContent>
</Collapsible>
</li>
))}
</ol>
</CollapsibleContent>
</Collapsible>
)
}Sidebar group
A nav group is a button, never a link. The group holding the current page starts open, and that page is marked current.
"use client"
import { ChevronRightIcon, LayoutDashboardIcon, PackageIcon, ReceiptIcon, UsersIcon } from "lucide-react"
import { cn } from "@/lib/utils"
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
const CURRENT = "/ecommerce/inventory"
const NAV = [
{ title: "Overview", href: "/ecommerce", icon: LayoutDashboardIcon },
{
title: "Orders",
icon: ReceiptIcon,
items: [
{ title: "Order book", href: "/ecommerce/orders" },
{ title: "Fulfillment", href: "/ecommerce/fulfillment" },
],
},
{
title: "Catalogue",
icon: PackageIcon,
items: [
{ title: "Products", href: "/ecommerce/products" },
{ title: "Inventory", href: "/ecommerce/inventory" },
],
},
{ title: "Customers", href: "/ecommerce/customers", icon: UsersIcon },
]
const ROW =
"flex h-8 w-full items-center gap-2 rounded-md px-2 text-start text-sm text-sidebar-foreground transition-colors duration-(--duration-fast) ease-(--ease-standard) focus-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground [&>svg]:size-4 [&>svg]:shrink-0"
const CURRENT_ROW = "bg-sidebar-accent font-medium text-sidebar-accent-foreground"
// The panel clips while it moves, so -mx-1/px-1 and the list's py-1 leave the
// focus outline round each page its 4px.
const PANEL =
"-mx-1 h-(--collapsible-panel-height) overflow-hidden px-1 transition-[height] duration-(--duration-base) ease-(--ease-standard) data-starting-style:h-0 data-ending-style:h-0"
// A group is a button that shows and hides its pages, never a link of its
// own; the one holding the current page starts open, so the reader lands
// with their place in view.
export default function CollapsibleSidebar() {
return (
<nav aria-label="Northwind Store" className="w-56 rounded-xl bg-sidebar p-2">
<ul className="flex flex-col gap-0.5">
{NAV.map((entry) => (
<li key={entry.title}>
{entry.items ? (
<Collapsible defaultOpen={entry.items.some((item) => item.href === CURRENT)}>
<CollapsibleTrigger className={cn(ROW, "group/nav")}>
<entry.icon aria-hidden="true" />
<span className="flex-1 truncate">{entry.title}</span>
<ChevronRightIcon
aria-hidden="true"
className="text-sidebar-foreground/70 transition-transform duration-(--duration-fast) ease-(--ease-standard) rtl:rotate-180 group-data-[panel-open]/nav:rotate-90"
/>
</CollapsibleTrigger>
<CollapsibleContent className={PANEL}>
<ul className="ms-4 flex flex-col gap-0.5 border-s border-sidebar-border py-1 ps-2">
{entry.items.map((item) => (
<li key={item.href}>
<a
href={item.href}
aria-current={item.href === CURRENT ? "page" : undefined}
className={cn(ROW, "h-7", item.href === CURRENT && CURRENT_ROW)}
>
{item.title}
</a>
</li>
))}
</ul>
</CollapsibleContent>
</Collapsible>
) : (
<a href={entry.href} className={ROW}>
<entry.icon aria-hidden="true" />
{entry.title}
</a>
)}
</li>
))}
</ul>
</nav>
)
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| open / defaultOpen | boolean | false | Whether the panel shows: controlled, or where it starts. |
| onOpenChange | (open: boolean) => void | — | Called when the trigger, or find in page, opens or shuts the panel. |
| CollapsibleTrigger.render | React.ReactElement | — | Draws the trigger as another element, such as a kit Button; it keeps aria-expanded and aria-controls. |
| CollapsibleContent.hiddenUntilFound | boolean | false | Keeps a shut panel in the page as hidden="until-found", so find in page reaches its text and opens it. |
Dependencies
Registry
Source
"use client"
import { Collapsible as CollapsiblePrimitive } from "@base-ui/react/collapsible"
function Collapsible({ ...props }: CollapsiblePrimitive.Root.Props) {
return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
}
function CollapsibleTrigger({ ...props }: CollapsiblePrimitive.Trigger.Props) {
return (
<CollapsiblePrimitive.Trigger data-slot="collapsible-trigger" {...props} />
)
}
function CollapsibleContent({ ...props }: CollapsiblePrimitive.Panel.Props) {
return (
<CollapsiblePrimitive.Panel data-slot="collapsible-content" {...props} />
)
}
export { Collapsible, CollapsibleTrigger, CollapsibleContent }