# Collapsible (/docs/collapsible)





## API Reference [#api-reference]

<BaseUIReference href="https://base-ui.com/react/components/collapsible" />

## Basic [#basic]

<Preview cssProperties="collapsiblePlaygroundCssProperties">
  <CollapsibleExample />

  <Preview.Code>
    {`
          import {
            Collapsible,
            CollapsibleTrigger,
            CollapsiblePanel,
          } from "moduix";

          export function CollapsibleDemo() {
            return (
              <Collapsible className={styles.root}>
                <CollapsibleTrigger>Recovery keys</CollapsibleTrigger>
                <CollapsiblePanel>
                  <ul className={styles.keysList}>
                    {recoveryKeys.map((key) => (
                      <li key={key}>{key}</li>
                    ))}
                  </ul>
                </CollapsiblePanel>
              </Collapsible>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .root {
            width: 16rem;
          }

          .keysList {
            display: flex;
            flex-direction: column;
            gap: var(--spacing-1);
            margin: 0;
            padding-inline-start: var(--spacing-5);
          }
        `}
  </Preview.CSS>

  <Preview.Data>
    {`
          const recoveryKeys = ["alien-bean-pasta", "wild-irish-burrito", "horse-battery-staple"];
        `}
  </Preview.Data>

  <Preview.CSSProperties>
    {(context) => <CollapsibleCssPropertiesPanel {...context} />}
  </Preview.CSSProperties>

  <Preview.CSSPlayground>
    {(context) => <CollapsibleCssPlaygroundPanel {...context} />}
  </Preview.CSSPlayground>
</Preview>

## Anatomy [#anatomy]

`Collapsible` groups one toggle control and one panel that share the same open state.
Keep `CollapsibleTrigger` and `CollapsiblePanel` inside the same root so keyboard and
accessibility wiring stay connected.

```text
Collapsible
├─ CollapsibleTrigger
│  └─ label
└─ CollapsiblePanel
   └─ content
```

```tsx
<Collapsible>
  <CollapsibleTrigger>Recovery keys</CollapsibleTrigger>
  <CollapsiblePanel>
    <div className={styles.panelContent}>Store these keys somewhere safe.</div>
  </CollapsiblePanel>
</Collapsible>
```

| Part                     | Role                                                                                                          |
| ------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `Collapsible`            | Root state machine. Controls `open`, `defaultOpen`, `disabled`, and `onOpenChange`.                           |
| `CollapsibleTrigger`     | Interactive toggle button. Opens or closes the panel, receives open/disabled attributes, and renders an icon. |
| `CollapsibleTriggerIcon` | Advanced visual state marker. Use directly only when manually composing trigger contents.                     |
| `CollapsiblePanel`       | Collapsible content region. Put spacing/surface wrappers inside it for cleaner animations.                    |

`CollapsibleTrigger` renders its icon internally so the common composition stays compact.
Use `icon`, `hideIcon`, `classNames.icon`, and `slotProps.icon` when you need to replace,
remove, style, or pass props to that internal slot. `CollapsibleTriggerIcon` is still exported
for advanced manual composition, but most projects do not need it.

`Collapsible` does not use service slots like `portal`, `backdrop`, or `viewport`. In most cases,
keep the default structure and customize visible parts: the root, trigger, icon slot, panel,
and content wrapper inside the panel.

## Composition [#composition]

Use `Collapsible` for root state and behavior props such as `open`, `defaultOpen`,
`onOpenChange`, and `disabled`.

All parts in this component are visible slots. Style `Collapsible`, `CollapsibleTrigger`,
and `CollapsiblePanel` directly with `className`. Use `classNames.icon` for the icon rendered
inside `CollapsibleTrigger`, or compose `CollapsibleTriggerIcon` manually when you need full
control over trigger children. All Base UI behavior props such as `render`, `nativeButton`,
`keepMounted`, and `hiddenUntilFound` pass through to the matching primitive part.

## Examples [#examples]

### Default Open [#default-open]

Use `defaultOpen` when the panel should be expanded on first render.

<Preview>
  <CollapsibleExample />

  <Preview.Code>
    {`
          import {
            Collapsible,
            CollapsibleTrigger,
            CollapsiblePanel,
          } from "moduix";

          export function DefaultOpenCollapsibleDemo() {
            return (
              <Collapsible defaultOpen className={styles.root}>
                <CollapsibleTrigger>Recovery keys</CollapsibleTrigger>
                <CollapsiblePanel>
                  <ul className={styles.keysList}>
                    {recoveryKeys.map((key) => (
                      <li key={key}>{key}</li>
                    ))}
                  </ul>
                </CollapsiblePanel>
              </Collapsible>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .root {
            width: 16rem;
          }

          .keysList {
            display: flex;
            flex-direction: column;
            gap: var(--spacing-1);
            margin: 0;
            padding-inline-start: var(--spacing-5);
          }
        `}
  </Preview.CSS>

  <Preview.Data>
    {`
          const recoveryKeys = ["alien-bean-pasta", "wild-irish-burrito", "horse-battery-staple"];
        `}
  </Preview.Data>
</Preview>

### Controlled [#controlled]

Control `open` from React state when the collapsible needs to coordinate with other UI.

<Preview>
  <ControlledCollapsibleExample />

  <Preview.Code>
    {`
          import {
            Collapsible,
            CollapsibleTrigger,
            CollapsiblePanel,
          } from "moduix";
          import { useState } from "react";

          export function ControlledCollapsibleDemo() {
            const [open, setOpen] = useState(false);

            return (
              <Collapsible open={open} onOpenChange={setOpen} className={styles.root}>
                <CollapsibleTrigger>Recovery keys</CollapsibleTrigger>
                <CollapsiblePanel>
                  <ul className={styles.keysList}>
                    {recoveryKeys.map((key) => (
                      <li key={key}>{key}</li>
                    ))}
                  </ul>
                </CollapsiblePanel>
                <span className={styles.status}>Current state: {open ? "open" : "closed"}</span>
              </Collapsible>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .root {
            width: 16rem;
          }

          .keysList {
            display: flex;
            flex-direction: column;
            gap: var(--spacing-1);
            margin: 0;
            padding-inline-start: var(--spacing-5);
          }

          .status {
            margin-top: var(--spacing-2);
            color: var(--color-muted-foreground);
            font-size: var(--text-xs);
            line-height: var(--line-height-text-xs);
          }
        `}
  </Preview.CSS>

  <Preview.Data>
    {`
          const recoveryKeys = ["alien-bean-pasta", "wild-irish-burrito", "horse-battery-staple"];
        `}
  </Preview.Data>
</Preview>

### Disabled [#disabled]

Set `disabled` on the root when the trigger should be visible but unavailable for interaction.

<Preview>
  <DisabledCollapsibleExample />

  <Preview.Code>
    {`
          import {
            Collapsible,
            CollapsibleTrigger,
            CollapsiblePanel,
          } from "moduix";

          export function DisabledCollapsibleDemo() {
            return (
              <Collapsible disabled className={styles.root}>
                <CollapsibleTrigger>Recovery keys</CollapsibleTrigger>
                <CollapsiblePanel>
                  <ul className={styles.keysList}>
                    {recoveryKeys.map((key) => (
                      <li key={key}>{key}</li>
                    ))}
                  </ul>
                </CollapsiblePanel>
              </Collapsible>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .root {
            width: 16rem;
          }

          .keysList {
            display: flex;
            flex-direction: column;
            gap: var(--spacing-1);
            margin: 0;
            padding-inline-start: var(--spacing-5);
          }
        `}
  </Preview.CSS>

  <Preview.Data>
    {`
          const recoveryKeys = ["alien-bean-pasta", "wild-irish-burrito", "horse-battery-staple"];
        `}
  </Preview.Data>
</Preview>

### Hidden Until Found [#hidden-until-found]

Use `hiddenUntilFound` when browser page search should be able to find and expand hidden panel content.

<Preview>
  <HiddenUntilFoundCollapsibleExample />

  <Preview.Code>
    {`
          import {
            Collapsible,
            CollapsibleTrigger,
            CollapsiblePanel,
          } from "moduix";

          export function HiddenUntilFoundCollapsibleDemo() {
            return (
              <Collapsible className={styles.root}>
                <CollapsibleTrigger>Searchable recovery keys</CollapsibleTrigger>
                <CollapsiblePanel hiddenUntilFound>
                  <ul className={styles.keysList}>
                    {recoveryKeys.map((key) => (
                      <li key={key}>{key}</li>
                    ))}
                  </ul>
                </CollapsiblePanel>
              </Collapsible>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .root {
            width: 16rem;
          }

          .keysList {
            display: flex;
            flex-direction: column;
            gap: var(--spacing-1);
            margin: 0;
            padding-inline-start: var(--spacing-5);
          }
        `}
  </Preview.CSS>

  <Preview.Data>
    {`
          const recoveryKeys = ["alien-bean-pasta", "wild-irish-burrito", "horse-battery-staple"];
        `}
  </Preview.Data>
</Preview>

### Custom Icon And Styles [#custom-icon-and-styles]

Pass `icon` to replace the default visual marker and style it with `classNames.icon`.
Use `className` on the root, trigger, and panel slots when styling with CSS Modules,
Tailwind CSS, or CSS-in-JS. Put your own wrapper inside the panel when the content needs
layout or surface styles.

<Preview>
  <CustomStylesCollapsibleExample />

  <Preview.Code>
    {`
          import {
            Collapsible,
            CollapsibleTrigger,
            CollapsiblePanel,
            ChevronDownIcon,
          } from "moduix";

          export function CustomStylesCollapsibleDemo() {
            return (
              <Collapsible className={styles.customRoot}>
                <CollapsibleTrigger
                  className={styles.customTrigger}
                  icon={<ChevronDownIcon />}
                  classNames={{ icon: styles.customTriggerIcon }}
                >
                  Styled recovery keys
                </CollapsibleTrigger>
                <CollapsiblePanel className={styles.customPanel}>
                  <div className={styles.customPanelContent}>
                    <ul className={styles.keysList}>
                      {recoveryKeys.map((key) => (
                        <li key={key}>{key}</li>
                      ))}
                    </ul>
                  </div>
                </CollapsiblePanel>
              </Collapsible>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .customRoot {
            width: 16rem;
            color: var(--color-foreground);
          }

          .customTrigger {
            padding: var(--spacing-2) var(--spacing-3);
            border-radius: var(--radius-md);
            background-color: var(--color-muted);
            color: var(--color-foreground);
          }

          @media (hover: hover) {
            .customTrigger:hover {
              background-color: var(--color-accent);
            }
          }

          .customTriggerIcon {
            --collapsible-icon-open-transform: rotate(180deg);

            color: var(--color-primary);
          }

          .customPanel {
            color: var(--color-muted-foreground);
          }

          .customPanelContent {
            margin-top: var(--spacing-1);
            padding: var(--spacing-2) var(--spacing-3);
            border-radius: var(--radius-md);
            background-color: var(--color-muted);
          }

          .keysList {
            display: flex;
            flex-direction: column;
            gap: var(--spacing-1);
            margin: 0;
            padding-inline-start: var(--spacing-5);
          }
        `}
  </Preview.CSS>

  <Preview.Data>
    {`
          const recoveryKeys = ["alien-bean-pasta", "wild-irish-burrito", "horse-battery-staple"];
        `}
  </Preview.Data>
</Preview>
