---
id: "packages/active-ui/collapsible"
title: "Collapsible"
description: "One section that shows and hides its content behind a trigger, without styling of its own."
url: "/packages/active-ui/collapsible"
locale: "en"
revision: "521ac1c06622e5b7"
---

# Collapsible

A collapsible shows or hides one piece of content behind a trigger: the rest of a list, advanced
options, details of a row. It has no look of its own; you style the trigger and the content. For a
set of sections that behave together, use an [accordion](/packages/active-ui/accordion). This site
uses a collapsible for the full type of a prop in every API list.

**Example: Show the rest of a list**

Client: Runs entirely in your browser. Source: `examples/ui/collapsible.tsx`.

```tsx
// apps/docs/examples/ui/collapsible.tsx
'use client';

import {useState} from 'react';
import {ChevronsUpDownIcon} from 'lucide-react';

import {Button} from '@workspace/active-ui/components/primitives/button';
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from '@workspace/active-ui/components/primitives/collapsible';

const RULES = [
  'Contribution rate 7.5% of the pensionable salary',
  'A franchise set each year by law',
  'Retirement at the state pension age',
  'Partner pension of 70% of the old-age pension',
];

/**
 * One section that shows and hides itself: the first rule is always shown,
 * the rest behind a button that says what it does. The open state is
 * controlled here, to change the label.
 */
export default function CollapsibleExample() {
  const [open, setOpen] = useState(false);
  return (
    <Collapsible
      open={open}
      onOpenChange={setOpen}
      className="flex w-full max-w-sm flex-col gap-2"
    >
      <div className="flex items-center justify-between gap-4">
        <h4 className="text-sm font-semibold">Scheme rules</h4>
        <CollapsibleTrigger render={<Button variant="ghost" size="sm" />}>
          <ChevronsUpDownIcon data-icon="inline-start" aria-hidden />
          {open ? 'Show less' : `Show all ${RULES.length}`}
        </CollapsibleTrigger>
      </div>
      <p className="rounded-md border px-3 py-2 text-sm">{RULES[0]}</p>
      <CollapsibleContent className="flex flex-col gap-2">
        {RULES.slice(1).map((rule) => (
          <p key={rule} className="rounded-md border px-3 py-2 text-sm">
            {rule}
          </p>
        ))}
      </CollapsibleContent>
    </Collapsible>
  );
}
```

## Usage

```tsx
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from '@workspace/active-ui/components/primitives/collapsible';

<Collapsible>
  <CollapsibleTrigger render={<Button variant="ghost" size="sm" />}>
    Show all rules
  </CollapsibleTrigger>
  <CollapsibleContent>…</CollapsibleContent>
</Collapsible>;
```

## Anatomy

- `Collapsible`: the state; `open` and `onOpenChange` (controlled) or `defaultOpen`; `disabled`.
- `CollapsibleTrigger`: the button; `render` makes your own button the trigger. It carries
  `data-panel-open` while open, to turn an icon.
- `CollapsibleContent`: the content, removed while closed unless `keepMounted`, and
  `hiddenUntilFound` for content the browser's find should reach.

## What your application provides

- A trigger label that says what happens, and that changes with the state when it helps ("Show
  all 4", "Show less").
- The styling of both parts.

## Accessibility and keyboard

The trigger is a button with `aria-expanded` that controls the content; `Enter` and `Space` toggle
it.

## API

**Collapsible**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `defaultOpen` | `boolean` | no | Whether the collapsible panel is initially open.  To render a controlled collapsible, use the `open` prop instead. |
| `disabled` | `boolean` | no | Whether the component should ignore user interaction. |
| `onOpenChange` | `((open: boolean, eventDetails: CollapsibleRootChangeEventDetails) => void)` | no | Event handler called when the panel is opened or closed. |
| `open` | `boolean` | no | Whether the collapsible panel is currently open.  To render an uncontrolled collapsible, use the `defaultOpen` prop instead. |
| `render` | `ComponentRenderFn<HTMLProps, CollapsibleRootState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

**CollapsibleTrigger**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `nativeButton` | `boolean` | no | Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). |
| `render` | `ComponentRenderFn<HTMLProps, CollapsibleTriggerState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

**CollapsibleContent**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `hiddenUntilFound` | `boolean` | no | Allows the browser's built-in page search to find and expand the panel contents.  Overrides the `keepMounted` prop and uses `hidden="until-found"` to hide the element without removing it from the DOM. |
| `keepMounted` | `boolean` | no | Whether to keep the element in the DOM while the panel is hidden. This prop is ignored when `hiddenUntilFound` is used. |
| `render` | `ComponentRenderFn<HTMLProps, CollapsiblePanelState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

> [!NOTE]
> **Based on shadcn/ui**
>
> This page adapts the Collapsible documentation of shadcn/ui (MIT licence) to
> this package; see [Third-party notices](/third-party-notices).
