---
type: doc
title: Collapsible
description: An interactive component which expands/collapses a panel.
---

> Fulldev UI for Astro. The `@fulldev` registry in `components.json` points at `https://ui.full.dev/r/styles/{style}/{name}.json`; see [installation](/astro/docs/installation.md).

```astro title="src/components/examples/collapsible/collapsible-demo.astro"
---
import ChevronsUpDown from "@lucide/astro/icons/chevrons-up-down"

import { buttonVariants } from "@/components/ui/button"
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from "@/components/ui/collapsible"
---

<Collapsible class="flex w-[350px] flex-col gap-2">
  <div class="flex items-center justify-between gap-4 px-4">
    <h4 class="text-sm font-semibold">Order #4189</h4>
    <CollapsibleTrigger
      class={buttonVariants({
        variant: "ghost",
        size: "icon",
        class: "size-8",
      })}
    >
      <ChevronsUpDown />
      <span class="sr-only">Toggle details</span>
    </CollapsibleTrigger>
  </div>
  <div class="flex items-center justify-between rounded-md border px-4 py-2 text-sm">
    <span class="text-muted-foreground">Status</span>
    <span class="font-medium">Shipped</span>
  </div>
  <CollapsibleContent class="flex flex-col gap-2">
    <div class="rounded-md border px-4 py-2 text-sm">
      <p class="font-medium">Shipping address</p>
      <p class="text-muted-foreground">100 Market St, San Francisco</p>
    </div>
    <div class="rounded-md border px-4 py-2 text-sm">
      <p class="font-medium">Items</p>
      <p class="text-muted-foreground">2x Studio Headphones</p>
    </div>
  </CollapsibleContent>
</Collapsible>
```

## Installation

```bash
npx shadcn@latest add @fulldev/collapsible
```

## Usage

```ts
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from "@/components/ui/collapsible"
```

```astro
<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>
```

`CollapsibleTrigger` renders a `button`. To style it as a button, pass
`class={buttonVariants({ variant: "ghost" })}`.

## Composition

```text
Collapsible
├── CollapsibleTrigger
└── CollapsibleContent
```

## Open State

Use `defaultOpen` to render the collapsible expanded on first load. The root
fires `collapsible:change` with `detail.open` and accepts a `collapsible:set`
event with `{ open: boolean }` to open or close it from a script.

```astro
<Collapsible defaultOpen>
  <CollapsibleTrigger>Toggle</CollapsibleTrigger>
  <CollapsibleContent>Content</CollapsibleContent>
</Collapsible>
```

## Basic

Style the open state with `data-[state=open]:` on the root and
`group-aria-expanded/button:` inside a trigger styled with `buttonVariants`.

```astro title="src/components/examples/collapsible/collapsible-basic.astro"
---
import ChevronDownIcon from "@lucide/astro/icons/chevron-down"

import { Button, buttonVariants } from "@/components/ui/button"
import { Card, CardContent } from "@/components/ui/card"
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from "@/components/ui/collapsible"
---

<Card class="mx-auto w-full max-w-sm">
  <CardContent>
    <Collapsible class="data-[state=open]:bg-muted rounded-md">
      <CollapsibleTrigger
        class={buttonVariants({ variant: "ghost", class: "w-full" })}
      >
        Product details
        <ChevronDownIcon class="ml-auto group-aria-expanded/button:rotate-180" />
      </CollapsibleTrigger>
      <CollapsibleContent class="flex flex-col items-start gap-2 p-2.5 pt-0 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>
```

## Settings Panel

Use a trigger button to reveal additional settings.

```astro title="src/components/examples/collapsible/collapsible-settings.astro"
---
import MaximizeIcon from "@lucide/astro/icons/maximize"
import MinimizeIcon from "@lucide/astro/icons/minimize"

import { buttonVariants } 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"
---

<Card class="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 class="flex items-start gap-2">
      <FieldGroup class="grid w-full grid-cols-2 gap-2">
        <Field>
          <FieldLabel for="radius-x" class="sr-only">
            Radius X
          </FieldLabel>
          <Input id="radius-x" placeholder="0" value="0" />
        </Field>
        <Field>
          <FieldLabel for="radius-y" class="sr-only">
            Radius Y
          </FieldLabel>
          <Input id="radius-y" placeholder="0" value="0" />
        </Field>
        <CollapsibleContent class="col-span-full grid grid-cols-subgrid gap-2">
          <Field>
            <FieldLabel for="radius-x-2" class="sr-only">
              Radius X
            </FieldLabel>
            <Input id="radius-x-2" placeholder="0" value="0" />
          </Field>
          <Field>
            <FieldLabel for="radius-y-2" class="sr-only">
              Radius Y
            </FieldLabel>
            <Input id="radius-y-2" placeholder="0" value="0" />
          </Field>
        </CollapsibleContent>
      </FieldGroup>
      <CollapsibleTrigger
        class={buttonVariants({ variant: "outline", size: "icon" })}
      >
        <MaximizeIcon class="group-aria-expanded/button:hidden" />
        <MinimizeIcon class="hidden group-aria-expanded/button:block" />
      </CollapsibleTrigger>
    </Collapsible>
  </CardContent>
</Card>
```

## File Tree

Use nested collapsibles to build a file tree.

```astro title="src/components/examples/collapsible/collapsible-file-tree.astro"
---
import ChevronRightIcon from "@lucide/astro/icons/chevron-right"
import FileIcon from "@lucide/astro/icons/file"
import FolderIcon from "@lucide/astro/icons/folder"

import { Button, buttonVariants } 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[] }

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" },
]

// Renders the card, or one tree level when Astro.self passes `items`.
const { items } = Astro.props as { items?: FileTreeItem[] }
---

{items ? (
  items.map((fileItem) =>
    "items" in fileItem ? (
      <Collapsible>
        <CollapsibleTrigger
          class={buttonVariants({
            variant: "ghost",
            size: "sm",
            class:
              "group hover:bg-accent hover:text-accent-foreground w-full justify-start transition-none",
          })}
        >
          <ChevronRightIcon class="transition-transform group-data-[state=open]:rotate-90" />
          <FolderIcon />
          {fileItem.name}
        </CollapsibleTrigger>
        <CollapsibleContent class="style-lyra:ml-4 mt-1 ml-5">
          <div class="flex flex-col gap-1">
            <Astro.self items={fileItem.items} />
          </div>
        </CollapsibleContent>
      </Collapsible>
    ) : (
      <Button
        variant="link"
        size="sm"
        class="text-foreground w-full justify-start gap-2"
      >
        <FileIcon />
        <span>{fileItem.name}</span>
      </Button>
    )
  )
) : (
  <Card class="mx-auto w-full max-w-[16rem] gap-2" size="sm">
    <CardHeader>
      <Tabs defaultValue="explorer">
        <TabsList class="w-full">
          <TabsTrigger value="explorer">Explorer</TabsTrigger>
          <TabsTrigger value="settings">Outline</TabsTrigger>
        </TabsList>
      </Tabs>
    </CardHeader>
    <CardContent>
      <div class="flex flex-col gap-1">
        <Astro.self items={fileTree} />
      </div>
    </CardContent>
  </Card>
)}
```

## RTL

```astro title="src/components/examples/collapsible/collapsible-rtl.astro"
---
import ChevronsUpDown from "@lucide/astro/icons/chevrons-up-down"

import { buttonVariants } from "@/components/ui/button"
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from "@/components/ui/collapsible"

const translations = {
  en: {
    dir: "ltr",
    values: {
      orderNumber: "Order #4189",
      status: "Status",
      shipped: "Shipped",
      shippingAddress: "Shipping address",
      address: "100 Market St, San Francisco",
      items: "Items",
      itemsDescription: "2x Studio Headphones",
    },
  },
  ar: {
    dir: "rtl",
    values: {
      orderNumber: "الطلب #4189",
      status: "الحالة",
      shipped: "تم الشحن",
      shippingAddress: "عنوان الشحن",
      address: "100 Market St, San Francisco",
      items: "العناصر",
      itemsDescription: "2x سماعات الاستوديو",
    },
  },
  he: {
    dir: "rtl",
    values: {
      orderNumber: "הזמנה #4189",
      status: "סטטוס",
      shipped: "נשלח",
      shippingAddress: "כתובת משלוח",
      address: "100 Market St, San Francisco",
      items: "פריטים",
      itemsDescription: "2x אוזניות סטודיו",
    },
  },
}

const { dir, values: t } = translations.ar
---

<Collapsible class="flex w-[350px] flex-col gap-2" dir={dir}>
  <div class="flex items-center justify-between gap-4 px-4">
    <h4 class="text-sm font-semibold">{t.orderNumber}</h4>
    <CollapsibleTrigger
      class={buttonVariants({
        variant: "ghost",
        size: "icon",
        class: "size-8",
      })}
    >
      <ChevronsUpDown />
      <span class="sr-only">Toggle details</span>
    </CollapsibleTrigger>
  </div>
  <div class="flex items-center justify-between rounded-md border px-4 py-2 text-sm">
    <span class="text-muted-foreground">{t.status}</span>
    <span class="font-medium">{t.shipped}</span>
  </div>
  <CollapsibleContent class="flex flex-col gap-2">
    <div class="rounded-md border px-4 py-2 text-sm">
      <p class="font-medium">{t.shippingAddress}</p>
      <p class="text-muted-foreground">{t.address}</p>
    </div>
    <div class="rounded-md border px-4 py-2 text-sm">
      <p class="font-medium">{t.items}</p>
      <p class="text-muted-foreground">{t.itemsDescription}</p>
    </div>
  </CollapsibleContent>
</Collapsible>
```

## API Reference

Source: [`src/components/ui/collapsible`](https://github.com/fulldotdev/ui/tree/main/src/components/ui/collapsible)

| Prop               | Type      | Default | Description                                                  |
| ------------------ | --------- | ------- | ------------------------------------------------------------ |
| `defaultOpen`      | `boolean` | `false` | Opens the content on first load.                             |
| `hiddenUntilFound` | `boolean` | `false` | Uses `hidden="until-found"` so find-in-page reveals content. |

The root and the content get `data-state="open"` or `data-state="closed"`, and
the trigger gets `aria-expanded`. See the
[data-slot docs](https://github.com/bejamas/data-slot/blob/main/packages/collapsible/README.md)
for the events.

For stacked FAQ patterns with multiple items, prefer
[Accordion](/astro/components/accordion/).

## Install what this page shows

```bash
npx shadcn@latest add @fulldev/collapsible-examples
```
