Collapsible
A single disclosure: a trigger that expands and collapses a region, animated without measuring so an interrupted toggle simply reverses, with the closed content kept in the page.
Available for Svelte 5, React, and Vue 3.
Preview
<script lang="ts">
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "fancy-ui-svelte";
let open = $state(false);
</script>
<Collapsible bind:open class="w-full max-w-sm">
<div class="flex items-center justify-between gap-4">
<h4 class="text-sm font-semibold">3 repositories starred</h4>
<CollapsibleTrigger class="rounded-md border px-3 py-1 text-sm">
{open ? "Show less" : "Show more"}
</CollapsibleTrigger>
</div>
<div class="mt-2 rounded-md border px-3 py-2 font-mono text-sm">fancy-ui/core</div>
<CollapsibleContent>
<div class="mt-2 flex flex-col gap-2">
<div class="rounded-md border px-3 py-2 font-mono text-sm">fancy-ui/motion</div>
<div class="rounded-md border px-3 py-2 font-mono text-sm">fancy-ui/registry</div>
</div>
</CollapsibleContent>
</Collapsible>Installation
Usage
<script lang="ts">
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "fancy-ui-svelte";
let open = $state(false);
</script>
<Collapsible bind:open class="w-full max-w-sm">
<div class="flex items-center justify-between gap-4">
<h4 class="text-sm font-semibold">3 repositories starred</h4>
<CollapsibleTrigger class="rounded-md border px-3 py-1 text-sm">
{open ? "Show less" : "Show more"}
</CollapsibleTrigger>
</div>
<div class="mt-2 rounded-md border px-3 py-2 font-mono text-sm">fancy-ui/core</div>
<CollapsibleContent>
<div class="mt-2 flex flex-col gap-2">
<div class="rounded-md border px-3 py-2 font-mono text-sm">fancy-ui/motion</div>
<div class="rounded-md border px-3 py-2 font-mono text-sm">fancy-ui/registry</div>
</div>
</CollapsibleContent>
</Collapsible>Examples
Basic Usage
A trigger that reveals extra rows beneath a short list.
<script lang="ts">
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "$lib/fancy-ui/collapsible";
let open = $state(false);
</script>
<Collapsible bind:open class="w-full max-w-sm">
<div class="flex items-center justify-between gap-4">
<h4 class="text-sm font-semibold">3 repositories starred</h4>
<CollapsibleTrigger class="rounded-md border px-3 py-1 text-sm">
{open ? "Show less" : "Show more"}
</CollapsibleTrigger>
</div>
<div class="mt-2 rounded-md border px-3 py-2 font-mono text-sm">fancy-ui/core</div>
<CollapsibleContent>
<div class="mt-2 flex flex-col gap-2">
<div class="rounded-md border px-3 py-2 font-mono text-sm">fancy-ui/motion</div>
<div class="rounded-md border px-3 py-2 font-mono text-sm">fancy-ui/registry</div>
</div>
</CollapsibleContent>
</Collapsible>Multiple
Several independent panels, each with its own open state.
<script lang="ts">
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "$lib/fancy-ui/collapsible";
const sections = [
{ id: "a", title: "Appearance", body: "Theme, density and the accent colour." },
{ id: "b", title: "Language", body: "The language of menus and messages." },
{ id: "c", title: "Advanced", body: "Experimental options that may change." },
];
</script>
<div class="flex w-full max-w-sm flex-col gap-2">
{#each sections as section (section.id)}
<Collapsible class="rounded-md border px-3" open={section.id === "a"}>
<CollapsibleTrigger class="flex w-full items-center justify-between py-2 text-sm font-medium">
{section.title}
</CollapsibleTrigger>
<CollapsibleContent class="text-muted-foreground pb-2 text-sm">
{section.body}
</CollapsibleContent>
</Collapsible>
{/each}
</div>Controlled
Open state shared with a button outside the component.
<script lang="ts">
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "$lib/fancy-ui/collapsible";
import { Button } from "$lib/fancy-ui/button";
let open = $state(false);
let changes = $state(0);
</script>
<div class="flex w-full max-w-sm flex-col gap-3">
<div class="flex flex-wrap items-center gap-2">
<Button size="sm" variant="outline" onclick={() => (open = !open)}>Toggle from outside</Button>
<span class="text-muted-foreground text-sm">
{open ? "open" : "closed"} · {changes} change{changes === 1 ? "" : "s"} from the trigger
</span>
</div>
<Collapsible bind:open onOpenChange={() => (changes += 1)} class="rounded-md border px-3">
<CollapsibleTrigger class="flex w-full items-center justify-between py-2 text-sm font-medium">
Release notes
</CollapsibleTrigger>
<CollapsibleContent class="text-muted-foreground pb-2 text-sm">
Both the trigger and the button above drive the same state; only the trigger reports through
onOpenChange.
</CollapsibleContent>
</Collapsible>
</div>Props
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | false | Whether the content is expanded, bindable. |
onOpenChange | (open: boolean) => void | - | Called with the new state each time the trigger toggles it. |
disabled | boolean | false | Makes the trigger inert. A bound open can still be changed from outside. |
class | string | - | Additional CSS classes, merged onto the root. |
ref | HTMLDivElement | null | null | Bindable reference to the root element. |
CollapsibleTrigger.disabled | boolean | false | Disables this trigger on top of the root's own disabled. |
CollapsibleTrigger.class | string | - | Additional CSS classes, merged onto the trigger button. |
CollapsibleTrigger.ref | HTMLButtonElement | null | null | Bindable reference to the trigger's button element. |
CollapsibleContent.forceMount | boolean | true | Keeps the children mounted while closed (inert and hidden). Pass false to unmount them once the collapse has finished and mount them again on open. |
CollapsibleContent.class | string | - | Additional CSS classes, merged onto the inner body so padding collapses with the panel. |
CollapsibleContent.ref | HTMLDivElement | null | null | Bindable reference to the region element that animates. |
Slots
| Slot | Description |
|---|---|
children | A CollapsibleTrigger and a CollapsibleContent. |
CollapsibleTrigger.children | The trigger's content. |
CollapsibleContent.children | The content. |
Links
Related components
Accordion
A stack of headed sections where one or several open at a time, with a collapse that animates without measuring, arrow-key navigation between headers and labelled regions for assistive tech.
Breadcrumb
A data-driven trail of links with automatic truncation, a decorative separator, and the current page marked for assistive tech.
CommandMenu
A modal search palette over a flat vocabulary of items — grouped results, diacritic-insensitive filtering, match highlighting, and full keyboard navigation with focus that never leaves the search field.