# Collapsible

> Let people show or hide one optional section of content.

Source: https://www.honestui.com/docs/components/collapsible

```tsx
import { ChevronDown as ChevronDownIcon } from "honestui/icons"

import {
  Collapsible,
  CollapsiblePanel,
  CollapsibleTrigger,
} from "@/components/honest-ui/ui/collapsible"

export function CollapsibleDemo() {
  return (
    <Collapsible>
      <CollapsibleTrigger className="inline-flex items-center gap-2 text-sm font-medium data-panel-open:[&_svg]:rotate-180">
        Show recovery keys
        <ChevronDownIcon className="size-4" />
      </CollapsibleTrigger>
      <CollapsiblePanel>
        <ul className="flex flex-col gap-1 py-2 text-sm text-muted-foreground">
          <li className="rounded-sm bg-muted px-2 py-1 font-mono">
            4829-1735-6621
          </li>
          <li className="rounded-sm bg-muted px-2 py-1 font-mono">
            9182-6407-5532
          </li>
          <li className="rounded-sm bg-muted px-2 py-1 font-mono">
            3051-7924-9018
          </li>
        </ul>
      </CollapsiblePanel>
    </Collapsible>
  )
}

```

## Overview [#overview]

Collapsible hides content that is real but secondary: advanced settings, a long explanation, details most visits do not need. One trigger, one panel, one decision.

Choose deliberately between hiding and shortening:

* **Content most people never need** — Collapsible. This component.
* **Several related sections where opening one closes others** — Accordion. It coordinates what Collapsible leaves independent.
* **Content people must not miss** — nothing hidden. Collapsing required information does not make it optional; it makes it invisible.

## Anatomy [#anatomy]

A collapsible has a trigger (a real button) and a panel whose visibility it controls. The panel mounts expanded or collapsed per the root's state, with height animation handled by the primitive.

## Behavior [#behavior]

**Keyboard.**

| Key                                 | Result                                                                       |
| ----------------------------------- | ---------------------------------------------------------------------------- |
| <kbd>Enter</kbd> / <kbd>Space</kbd> | Toggle the panel                                                             |
| <kbd>Tab</kbd>                      | Move to the next control — inside the panel when open, past both when closed |

The trigger is a native button, so it arrives in the natural tab order and announces its expanded state. Screen readers hear "collapsed"/"expanded" without extra ARIA on your part.

**Independence.** Each Collapsible stands alone. Toggling one never touches another — that coordination is exactly what you are choosing when you pick Accordion instead.

**State.** Controlled (`open` / `onOpenChange`) or uncontrolled. Controlled state is how you implement "expand all" or reopen a section after validation fails — see [Don't do this](#dont-do-this).

## Accessibility [#accessibility]

The trigger exposes `aria-expanded` and controls the panel through the primitive's wiring; the panel's content is simply present or absent to assistive technology, matching what sighted users get. Trigger copy should preview what is inside ("Shipping options") rather than narrate the mechanism ("Click to expand").

Hidden panels keep out of the tab order while closed — focus skips genuinely unavailable controls instead of landing on ghosts.

Tokens handle dark mode and RTL mirroring automatically; the chevron rotates rather than swapping icons so reduced-motion users still get a state cue from geometry.

## Installation [#installation]


  

  
    <CliBlock commands="[&#x22;collapsible&#x22;]" />
  

  
    
      
        Install the following dependencies:
      

      ```bash
      npm install @base-ui-components/react
      ```

      
        Copy and paste the following code into your project.
      

      ### components/ui/collapsible.tsx

```tsx
"use client"

import { Collapsible as CollapsiblePrimitive } from "@base-ui-components/react/collapsible"

import { cn } from "@/lib/utils"

function Collapsible({ ...props }: CollapsiblePrimitive.Root.Props) {
  return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
}

function CollapsibleTrigger({
  className,
  ...props
}: CollapsiblePrimitive.Trigger.Props) {
  return (
    <CollapsiblePrimitive.Trigger
      data-slot="collapsible-trigger"
      className={cn("cursor-pointer", className)}
      {...props}
    />
  )
}

function CollapsiblePanel({
  className,
  ...props
}: CollapsiblePrimitive.Panel.Props) {
  return (
    <CollapsiblePrimitive.Panel
      data-slot="collapsible-panel"
      className={cn(
        "h-(--collapsible-panel-height) overflow-hidden transition-[height] duration-200 data-ending-style:h-0 data-starting-style:h-0",
        className
      )}
      {...props}
    />
  )
}

export {
  Collapsible,
  CollapsibleTrigger,
  CollapsiblePanel,
  CollapsiblePanel as CollapsibleContent,
}

```

      
        Update the import paths to match your project setup.
      
    
  


## Usage [#usage]

```tsx
import {
  Collapsible,
  CollapsiblePanel,
  CollapsibleTrigger,
} from "@/components/ui/collapsible";
```

```tsx
<Collapsible>
  <CollapsibleTrigger>Shipping options</CollapsibleTrigger>
  <CollapsiblePanel>{/* rates, delivery estimates */}</CollapsiblePanel>
</Collapsible>
```

## Don't do this [#dont-do-this]

### Hiding errors and required fields [#hiding-errors-and-required-fields]

```tsx
// Bad
<Collapsible defaultClosed>
  <CollapsibleTrigger>Billing address</CollapsibleTrigger>
  <CollapsiblePanel>{/* required fields */}</CollapsiblePanel>
</Collapsible>
```

Required input behind a closed panel fails invisibly: submit produces an error pointing at a field the person can see nowhere. If validation can fail inside a panel, open that panel programmatically when the error lands — controlled state exists precisely for this — or accept that this content does not belong in a collapsible at all.

### Burying the summary in the panel [#burying-the-summary-in-the-panel]

```tsx
// Bad
<CollapsibleTrigger>Details</CollapsibleTrigger>
```

```tsx
// Good
<CollapsibleTrigger>Refund policy · 30 days, no questions</CollapsibleTrigger>
```

"Details", "More", and "Advanced" make every collapsed section identical and cost a click just to tell apart. Put the one fact that helps someone decide whether to open — the policy's headline, the count of advanced options — into the trigger itself.

## Examples [#examples]

### Advanced settings pattern [#advanced-settings-pattern]

```tsx
import { ChevronDown as ChevronDownIcon } from "honestui/icons"

import {
  Collapsible,
  CollapsiblePanel,
  CollapsibleTrigger,
} from "@/components/honest-ui/ui/collapsible"

export function CollapsibleReleaseNotes() {
  return (
    <Collapsible className="w-full max-w-md rounded-xl border p-4" defaultOpen>
      <CollapsibleTrigger className="flex w-full items-center justify-between font-medium">
        Release notes
        <ChevronDownIcon className="size-4" />
      </CollapsibleTrigger>
      <CollapsiblePanel className="pt-3 text-sm text-muted-foreground">
        Version 2.4 adds command shortcuts, denser tables, and improved focus rings.
      </CollapsiblePanel>
    </Collapsible>
  )
}

```

### Command help pattern [#command-help-pattern]

```tsx
import {
  Collapsible,
  CollapsiblePanel,
  CollapsibleTrigger,
} from "@/components/honest-ui/ui/collapsible"

export function CollapsibleCommandHelp() {
  return (
    <Collapsible className="w-full max-w-sm rounded-xl border bg-card p-4">
      <CollapsibleTrigger className="font-medium">Keyboard shortcuts</CollapsibleTrigger>
      <CollapsiblePanel className="pt-3 text-sm text-muted-foreground">
        Press <kbd className="rounded border px-1.5 py-0.5 text-xs text-foreground">Ctrl K</kbd> to open search and <kbd className="rounded border px-1.5 py-0.5 text-xs text-foreground">G</kbd> then <kbd className="rounded border px-1.5 py-0.5 text-xs text-foreground">D</kbd> for dashboard.
      </CollapsiblePanel>
    </Collapsible>
  )
}

```

## API reference [#api-reference]

`Collapsible` accepts Base UI Collapsible props:

| Prop                    | Values            | Default      |
| ----------------------- | ----------------- | ------------ |
| `open` / `onOpenChange` | boolean, callback | uncontrolled |
| `defaultOpen`           | boolean           | `false`      |

Parts: `CollapsibleTrigger`, `CollapsiblePanel`.

See the [Base UI Collapsible API](https://base-ui.com/react/components/collapsible#api-reference).
