Github

Collapsible

An interactive component which expands/collapses a panel.

Order #4189

StatusShipped
"use client";
import { ChevronsUpDown } from "lucide-react";import * as React from "react";import { Button } from "@/components/ui/button";import {	Collapsible,	CollapsibleContent,	CollapsibleTrigger,} from "@/components/ui/collapsible";import { Surface } from "@/components/ui/surface";
export function CollapsibleDemo() {	const [isOpen, setIsOpen] = React.useState(false);
	return (		<Collapsible			open={isOpen}			onOpenChange={setIsOpen}			className="flex w-[350px] flex-col gap-2"		>			<div className="flex items-center justify-between gap-4 px-4">				<h4 className="font-semibold text-sm">Order #4189</h4>				<CollapsibleTrigger					render={<Button variant="ghost" size="icon" className="size-8" />}				>					<ChevronsUpDown />					<span className="sr-only">Toggle details</span>				</CollapsibleTrigger>			</div>			<Surface className="flex items-center justify-between rounded-2xl p-4 text-sm">				<span className="text-muted-foreground">Status</span>				<span className="font-medium">Shipped</span>			</Surface>			<CollapsibleContent className="flex flex-col gap-2">				<Surface className="rounded-2xl p-4 text-sm">					<p className="font-medium">Shipping address</p>					<p className="text-muted-foreground">100 Market St, San Francisco</p>				</Surface>				<Surface className="rounded-2xl p-4 text-sm">					<p className="font-medium">Items</p>					<p className="text-muted-foreground">2x Studio Headphones</p>				</Surface>			</CollapsibleContent>		</Collapsible>	);}

Installation

pnpm dlx shadcn@latest add @herocn/collapsible

Usage

import {  Collapsible,  CollapsibleContent,  CollapsibleTrigger,} from "@/components/ui/collapsible"
<Collapsible>  <CollapsibleTrigger>Can I use this in my project?</CollapsibleTrigger>  <CollapsibleContent>    Yes. Free to use for personal and commercial projects. No attribution    required.  </CollapsibleContent></Collapsible>

Composition

Use the following composition to build a Collapsible:

Collapsible├── CollapsibleTrigger└── CollapsibleContent

Controlled State

Use the open and onOpenChange props to control the state.

import * as React from "react"
export function Example() {  const [open, setOpen] = React.useState(false)
  return (    <Collapsible open={open} onOpenChange={setOpen}>      <CollapsibleTrigger>Toggle</CollapsibleTrigger>      <CollapsibleContent>Content</CollapsibleContent>    </Collapsible>  )}

Examples

Basic

import { ChevronDownIcon } from "lucide-react";import { Button } from "@/components/ui/button";import { Card, CardContent } from "@/components/ui/card";import {	Collapsible,	CollapsibleContent,	CollapsibleTrigger,} from "@/components/ui/collapsible";
export function CollapsibleBasic() {	return (		<Card className="mx-auto w-full max-w-sm">			<CardContent>				<Collapsible className="rounded-md data-open:bg-muted">					<CollapsibleTrigger						render={<Button variant="ghost" className="w-full" />}					>						Product details						<ChevronDownIcon className="ml-auto group-data-panel-open/button:rotate-180" />					</CollapsibleTrigger>					<CollapsibleContent className="flex flex-col items-start gap-2 text-sm">						This panel can be expanded or collapsed to reveal additional						content.						<Button size="sm">Learn More</Button>					</CollapsibleContent>				</Collapsible>			</CardContent>		</Card>	);}

Settings Panel

Use a trigger button to reveal additional settings.

Radius
Set the corner radius of the element.
"use client";
import { MaximizeIcon, MinimizeIcon } from "lucide-react";import * as React from "react";import { Button } from "@/components/ui/button";import {	Card,	CardContent,	CardDescription,	CardHeader,	CardTitle,} from "@/components/ui/card";import {	Collapsible,	CollapsibleContent,	CollapsibleTrigger,} from "@/components/ui/collapsible";import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";import { Input } from "@/components/ui/input";
export function CollapsibleSettings() {	const [isOpen, setIsOpen] = React.useState(false);
	return (		<Card className="mx-auto w-full 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="flex items-start gap-2"				>					<FieldGroup className="grid w-full grid-cols-2 gap-2">						<Field>							<FieldLabel htmlFor="radius-x" className="sr-only">								Radius X							</FieldLabel>							<Input								variant="secondary"								id="radius"								placeholder="0"								defaultValue={0}							/>						</Field>						<Field>							<FieldLabel htmlFor="radius-y" className="sr-only">								Radius Y							</FieldLabel>							<Input								variant="secondary"								id="radius"								placeholder="0"								defaultValue={0}							/>						</Field>						<CollapsibleContent className="col-span-full grid grid-cols-subgrid gap-2">							<Field>								<FieldLabel htmlFor="radius-x" className="sr-only">									Radius X								</FieldLabel>								<Input									variant="secondary"									id="radius"									placeholder="0"									defaultValue={0}								/>							</Field>							<Field>								<FieldLabel htmlFor="radius-y" className="sr-only">									Radius Y								</FieldLabel>								<Input									variant="secondary"									id="radius"									placeholder="0"									defaultValue={0}								/>							</Field>						</CollapsibleContent>					</FieldGroup>					<CollapsibleTrigger render={<Button variant="outline" size="icon" />}>						{isOpen ? <MinimizeIcon /> : <MaximizeIcon />}					</CollapsibleTrigger>				</Collapsible>			</CardContent>		</Card>	);}

File Tree

Use nested collapsibles to build a file tree.

import { ChevronRightIcon, FileIcon, FolderIcon } from "lucide-react";import { Button } from "@/components/ui/button";import { Card, CardContent, CardHeader } from "@/components/ui/card";import {	Collapsible,	CollapsibleContent,	CollapsibleTrigger,} from "@/components/ui/collapsible";import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
type FileTreeItem = { name: string } | { name: string; items: FileTreeItem[] };
export function CollapsibleFileTree() {	const fileTree: FileTreeItem[] = [		{			name: "components",			items: [				{					name: "ui",					items: [						{ name: "button.tsx" },						{ name: "card.tsx" },						{ name: "dialog.tsx" },						{ name: "input.tsx" },						{ name: "select.tsx" },						{ name: "table.tsx" },					],				},				{ name: "login-form.tsx" },				{ name: "register-form.tsx" },			],		},		{			name: "lib",			items: [{ name: "utils.ts" }, { name: "cn.ts" }, { name: "api.ts" }],		},		{			name: "hooks",			items: [				{ name: "use-media-query.ts" },				{ name: "use-debounce.ts" },				{ name: "use-local-storage.ts" },			],		},		{			name: "types",			items: [{ name: "index.d.ts" }, { name: "api.d.ts" }],		},		{			name: "public",			items: [				{ name: "favicon.ico" },				{ name: "logo.svg" },				{ name: "images" },			],		},		{ name: "app.tsx" },		{ name: "layout.tsx" },		{ name: "globals.css" },		{ name: "package.json" },		{ name: "tsconfig.json" },		{ name: "README.md" },		{ name: ".gitignore" },	];
	const renderItem = (fileItem: FileTreeItem): React.ReactNode => {		if ("items" in fileItem) {			return (				<Collapsible key={fileItem.name}>					<CollapsibleTrigger						render={							<Button								variant="ghost"								size="sm"								className="group w-full justify-start transition-none hover:bg-accent hover:text-accent-foreground"							/>						}					>						<ChevronRightIcon className="group-data-panel-open:rotate-90" />						<FolderIcon />						{fileItem.name}					</CollapsibleTrigger>					<CollapsibleContent className="mt-1 ml-5 style-lyra:ml-4">						<div className="flex flex-col gap-1">							{fileItem.items.map((child) => renderItem(child))}						</div>					</CollapsibleContent>				</Collapsible>			);		}
		return (			<Button				key={fileItem.name}				variant="ghost"				size="sm"				className="w-full justify-start gap-2 text-foreground"			>				<FileIcon />				<span>{fileItem.name}</span>			</Button>		);	};
	return (		<Card className="mx-auto w-full max-w-[16rem] gap-2" size="sm">			<CardHeader>				<Tabs defaultValue="explorer">					<TabsList className="w-full">						<TabsTrigger value="explorer">Explorer</TabsTrigger>						<TabsTrigger value="settings">Outline</TabsTrigger>					</TabsList>				</Tabs>			</CardHeader>			<CardContent>				<div className="flex max-h-107 flex-col gap-1 overflow-auto px-1">					{fileTree.map((item) => renderItem(item))}				</div>			</CardContent>		</Card>	);}

RTL

"use client";
import { ChevronDownIcon } from "lucide-react";
import {	type Translations,	useTranslation,} from "@/components/language-selector";import { Button } from "@/components/ui/button";import { Card, CardContent } from "@/components/ui/card";import {	Collapsible,	CollapsibleContent,	CollapsibleTrigger,} from "@/components/ui/collapsible";
const translations: Translations = {	en: {		dir: "ltr",		values: {			trigger: "Frequently asked questions",			answer:				"Yes. Free to use for personal and commercial projects. No attribution required.",			learnMore: "Learn More",		},	},	ar: {		dir: "rtl",		values: {			trigger: "الأسئلة الشائعة",			answer:				"نعم. مجاني للاستخدام في المشاريع الشخصية والتجارية. لا يلزم ذكر المصدر.",			learnMore: "اعرف المزيد",		},	},	he: {		dir: "rtl",		values: {			trigger: "שאלות נפוצות",			answer: "כן. חינמי לשימוש בפרויקטים אישיים ומסחריים. אין צורך בייחוס.",			learnMore: "למד עוד",		},	},};
export function CollapsibleRtl() {	const { dir, language, t } = useTranslation(translations, "ar");
	return (		<Card className="mx-auto w-full max-w-sm" lang={language} dir={dir}>			<CardContent>				<Collapsible className="rounded-md data-open:bg-muted">					<CollapsibleTrigger						render={<Button variant="ghost" className="w-full" />}					>						{t.trigger}						<ChevronDownIcon className="ms-auto group-data-panel-open/button:rotate-180" />					</CollapsibleTrigger>					<CollapsibleContent className="flex flex-col items-start gap-2 text-sm">						{t.answer}						<Button size="sm">{t.learnMore}</Button>					</CollapsibleContent>				</Collapsible>			</CardContent>		</Card>	);}

API Reference

See the Base UI documentation for more information.