Skip to content

Collapsible

An interactive component which expands/collapses a panel.

Code
import {
Button,
ChevronsUpDownIcon,
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@falcon/ui-kit";
import { useState } from "react";
export function Example() {
const [isOpen, setIsOpen] = useState(false);
return (
<Collapsible
open={isOpen}
onOpenChange={setIsOpen}
className="tw:flex tw:w-[350px] tw:flex-col tw:gap-2"
>
<div className="tw:flex tw:items-center tw:justify-between tw:gap-4 tw:px-4">
<h4 className="tw:text-sm tw:font-semibold">Order #4189</h4>
<CollapsibleTrigger
render={<Button variant="ghost" size="icon" className="tw:size-8" />}
>
<ChevronsUpDownIcon />
<span className="tw:sr-only">Toggle details</span>
</CollapsibleTrigger>
</div>
<div className="tw:flex tw:items-center tw:justify-between tw:rounded-md tw:border tw:border-border tw:px-4 tw:py-2 tw:text-sm">
<span className="tw:text-muted-foreground">Status</span>
<span className="tw:font-medium">Shipped</span>
</div>
<CollapsibleContent className="tw:flex tw:flex-col tw:gap-2">
<div className="tw:rounded-md tw:border tw:border-border tw:px-4 tw:py-2 tw:text-sm">
<p className="tw:font-medium">Shipping address</p>
<p className="tw:text-muted-foreground">
4820 County Road 12, Ames, IA
</p>
</div>
<div className="tw:rounded-md tw:border tw:border-border tw:px-4 tw:py-2 tw:text-sm">
<p className="tw:font-medium">Items</p>
<p className="tw:text-muted-foreground">2x Liquid Fertilizer Totes</p>
</div>
</CollapsibleContent>
</Collapsible>
);
}

Use the following composition to build a Collapsible:

Collapsible
├── CollapsibleTrigger
└── CollapsibleContent

Use the open and onOpenChange props to control the state.

Code
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@falcon/ui-kit";
import { useState } from "react";
export function Example() {
const [open, setOpen] = useState(false);
return (
<Collapsible open={open} onOpenChange={setOpen}>
<CollapsibleTrigger>Toggle</CollapsibleTrigger>
<CollapsibleContent>Content</CollapsibleContent>
</Collapsible>
);
}
Code
import {
Button,
Card,
CardContent,
ChevronDownIcon,
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@falcon/ui-kit";
export function Example() {
return (
<Card className="tw:mx-auto tw:w-full tw:max-w-sm">
<CardContent>
<Collapsible className="tw:rounded-md tw:data-open:bg-muted">
<CollapsibleTrigger
render={<Button variant="ghost" className="tw:w-full" />}
>
Product details
<ChevronDownIcon className="tw:ml-auto tw:group-data-panel-open/button:rotate-180" />
</CollapsibleTrigger>
<CollapsibleContent className="tw:flex tw:flex-col tw:items-start tw:gap-2 tw:p-2.5 tw:pt-0 tw:text-sm">
<div>
This panel can be expanded or collapsed to reveal additional
content.
</div>
<Button size="xs">Learn More</Button>
</CollapsibleContent>
</Collapsible>
</CardContent>
</Card>
);
}

Use a trigger button to reveal additional settings.

Code
import {
Button,
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
Field,
FieldGroup,
FieldLabel,
Input,
MaximizeIcon,
MinimizeIcon,
} from "@falcon/ui-kit";
import { useState } from "react";
export function Example() {
const [isOpen, setIsOpen] = useState(false);
return (
<Card className="tw:mx-auto tw:w-full tw:max-w-xs" size="sm">
<CardHeader>
<CardTitle>Radius</CardTitle>
<CardDescription>Set the corner radius of the element.</CardDescription>
</CardHeader>
<CardContent>
<Collapsible
open={isOpen}
onOpenChange={setIsOpen}
className="tw:flex tw:items-start tw:gap-2"
>
<FieldGroup className="tw:grid tw:w-full tw:grid-cols-2 tw:gap-2">
<Field>
<FieldLabel htmlFor="radius-x" className="tw:sr-only">
Radius X
</FieldLabel>
<Input id="radius-x" placeholder="0" defaultValue={0} />
</Field>
<Field>
<FieldLabel htmlFor="radius-y" className="tw:sr-only">
Radius Y
</FieldLabel>
<Input id="radius-y" placeholder="0" defaultValue={0} />
</Field>
<CollapsibleContent className="tw:col-span-full tw:grid tw:grid-cols-subgrid tw:gap-2">
<Field>
<FieldLabel htmlFor="radius-x-2" className="tw:sr-only">
Radius X
</FieldLabel>
<Input id="radius-x-2" placeholder="0" defaultValue={0} />
</Field>
<Field>
<FieldLabel htmlFor="radius-y-2" className="tw:sr-only">
Radius Y
</FieldLabel>
<Input id="radius-y-2" placeholder="0" defaultValue={0} />
</Field>
</CollapsibleContent>
</FieldGroup>
<CollapsibleTrigger
aria-label="Toggle additional settings"
render={<Button variant="outline" size="icon" />}
>
{isOpen ? <MinimizeIcon /> : <MaximizeIcon />}
</CollapsibleTrigger>
</Collapsible>
</CardContent>
</Card>
);
}

Use nested collapsibles to build a file tree.

Code
import {
Button,
Card,
CardContent,
CardHeader,
ChevronRightIcon,
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
FileIcon,
FolderIcon,
Tabs,
TabsList,
TabsTrigger,
} from "@falcon/ui-kit";
type FileTreeItem = { name: string } | { name: string; items: FileTreeItem[] };
export function Example() {
const fileTree: FileTreeItem[] = [
{
name: "agronomy",
items: [
{
name: "field-plans",
items: [
{ name: "north-40.pdf" },
{ name: "south-quarter.pdf" },
{ name: "creek-bottom.pdf" },
{ name: "home-place.pdf" },
{ name: "section-12.pdf" },
{ name: "west-80.pdf" },
],
},
{ name: "soil-samples.xlsx" },
{ name: "tissue-results.xlsx" },
],
},
{
name: "agreements",
items: [
{ name: "credit-application.pdf" },
{ name: "prepay-2026.pdf" },
{ name: "storage-contract.pdf" },
],
},
{
name: "invoices",
items: [
{ name: "inv-2026-0141.pdf" },
{ name: "inv-2026-0159.pdf" },
{ name: "inv-2026-0163.pdf" },
],
},
{
name: "labels",
items: [{ name: "glyphosate-sds.pdf" }, { name: "atrazine-sds.pdf" }],
},
{
name: "scouting",
items: [
{ name: "weed-pressure.jpg" },
{ name: "stand-count.jpg" },
{ name: "storm-damage.jpg" },
],
},
{ name: "crop-plan-2026.pdf" },
{ name: "yield-summary.xlsx" },
{ name: "rainfall-log.csv" },
{ name: "equipment-list.pdf" },
{ name: "applicator-license.pdf" },
{ name: "price-sheet.pdf" },
{ name: "archive.zip" },
];
function renderItem(fileItem: FileTreeItem) {
if ("items" in fileItem) {
return (
<Collapsible key={fileItem.name}>
<CollapsibleTrigger
render={
<Button
variant="ghost"
size="sm"
className="tw:group tw:w-full tw:justify-start tw:transition-none tw:hover:bg-accent tw:hover:text-accent-foreground"
/>
}
>
<ChevronRightIcon className="tw:transition-transform tw:group-data-panel-open:rotate-90" />
<FolderIcon />
{fileItem.name}
</CollapsibleTrigger>
<CollapsibleContent className="tw:mt-1 tw:ml-5">
<div className="tw:flex tw:flex-col tw:gap-1">
{fileItem.items.map((child) => renderItem(child))}
</div>
</CollapsibleContent>
</Collapsible>
);
}
return (
<Button
key={fileItem.name}
variant="link"
size="sm"
className="tw:w-full tw:justify-start tw:gap-2 tw:text-foreground"
>
<FileIcon />
<span>{fileItem.name}</span>
</Button>
);
}
return (
<Card className="tw:mx-auto tw:w-full tw:max-w-[16rem] tw:gap-2" size="sm">
<CardHeader>
<Tabs defaultValue="documents">
<TabsList className="tw:w-full">
<TabsTrigger value="documents">Documents</TabsTrigger>
<TabsTrigger value="recent">Recent</TabsTrigger>
</TabsList>
</Tabs>
</CardHeader>
<CardContent>
<div className="tw:flex tw:flex-col tw:gap-1">
{fileTree.map((item) => renderItem(item))}
</div>
</CardContent>
</Card>
);
}

Groups a trigger with its content panel. Renders a <div> element.

className?: string

CSS class applied to the collapsible root.

defaultOpen?: boolean

Whether the panel is initially open. Use open when controlled.

disabled?: boolean

Whether the collapsible ignores user interaction.

onOpenChange?: ((open: boolean, eventDetails: CollapsibleRootChangeEventDetails) => void)

Called when the panel opens or closes.

open?: boolean

Whether the panel is currently open. Use defaultOpen when uncontrolled.

render?: React.ReactElement<any, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, CollapsibleRootState>

Allows replacing the root element or composing it with another component.

style?: React.CSSProperties | ((state: CollapsibleRootState) => React.CSSProperties | undefined)

Style applied to the collapsible root.

Contains the collapsible content. Renders a <div> element.

className?: string

CSS class applied to the content panel.

hiddenUntilFound?: boolean

Allow browser find-in-page to reveal hidden content.

keepMounted?: boolean

Keep the content panel mounted while it is closed. Ignored when hiddenUntilFound is set.

render?: React.ReactElement<any, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, CollapsiblePanelState>

Allows replacing the panel element or composing it with another component.

style?: React.CSSProperties | ((state: CollapsiblePanelState) => React.CSSProperties | undefined)

Style applied to the content panel.

Opens and closes the content panel. Renders a <button> element.

className?: string

CSS class applied to the trigger.

nativeButton?: boolean

Whether render returns a native button. Set to false for another element.

render?: React.ReactElement<any, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, CollapsibleTriggerState>

Allows replacing the trigger element or composing it with another component.

style?: React.CSSProperties | ((state: CollapsibleTriggerState) => React.CSSProperties | undefined)

Style applied to the trigger.