Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Components

Free

Collapsible for React

Hide one section behind a button until someone opens it. For a single show-more in React with Base UI, like extra details under a summary.

Framework
UI library

A collapsible hides one section behind a button.

Use it for a single show-more, like an address under an order status. For several sections in a list, use an Accordion so the arrow keys move between them. In React, Base UI connects the button to the panel.

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.

Anatomy

Collapsible anatomy: root, trigger, indicator, content
Collapsible
├── CollapsibleTrigger
├── CollapsibleContent
└── CollapsibleIndicator

Usage

import { ChevronsUpDownIcon } from "lucide-react";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import {
  Collapsible, CollapsibleContent, CollapsibleTrigger,
} from "@/components/ui/collapsible";
const CollapsibleDemo = () => {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <Collapsible
      className="flex w-[350px] flex-col gap-2"
      onOpenChange={setIsOpen}
      open={isOpen}
    >
      <div className="flex items-center justify-between gap-2 px-4">
        <h4 className="font-semibold text-sm">Order #4189</h4>
        <CollapsibleTrigger
          render={<Button className="size-6" size="icon-xs" variant="ghost" />}
        >
          <ChevronsUpDownIcon aria-hidden="true" />
          <span className="sr-only">Toggle details</span>
        </CollapsibleTrigger>
      </div>
      <div className="flex items-center justify-between rounded-md border px-4 py-2 text-sm">
        <span className="text-muted-foreground">Status</span>
        <span className="font-medium">Shipped</span>
      </div>
      <CollapsibleContent className="flex flex-col gap-2">
        <div className="rounded-md border px-4 py-2 text-sm">
          <p className="font-medium">Shipping address</p>
          <p className="text-muted-foreground">100 Market St, San Francisco</p>
        </div>
        <div className="rounded-md border px-4 py-2 text-sm">
          <p className="font-medium">Items</p>
          <p className="text-muted-foreground">2x Studio Headphones</p>
        </div>
      </CollapsibleContent>
    </Collapsible>
  );
};

Accessibility

Space
Opens/closes the collapsible.
Enter
Opens/closes the collapsible.

Demos

Basic

A card holds one full-width trigger, Product details, and a chevron that turns when the panel is open. Inside is a short note and a Learn More button. Nothing outside reads the open state, so the collapsible keeps it. Use that when the trigger is the only control that should care, and treat the card as just a frame.

Preview width

Settings

Two radius fields stay on screen. The bottom corners live in CollapsibleContent and show up when the icon button is pressed. The button's accessible name switches between show and hide, which is why open is controlled: the icon depends on it. Use this for advanced values that would double the form for most people, and do not hide a field this way if it has to be filled.

Preview width

File Tree

Folders are nested collapsibles and files are plain buttons. Each level opens on its own, which is enough for a small static outline in a card. There is no selection, no typeahead, and no rename, so a real navigator should be a Tree View instead of stacked disclosures. Arrow keys will not walk these rows, because each trigger is its own button.

Preview width

Rtl

The same order summary, in Arabic, with dir set to rtl. The status row and the hidden address follow the text, and the toggle sits at the inline end. Set direction on the collapsible when this block is translated even if the page around it is not. The chevron does not point to a physical side, so leave it, and keep margins logical or the rows drift.

Preview width

FAQ

Should I use a Collapsible or an Accordion?

Use a Collapsible for one region that shows and hides on its own, like "Show more" or a settings group. When several sections sit together and work as one list, with arrow keys between the headers, use an Accordion instead of stacking Collapsibles.

How do I open a Collapsible from another button in React?

Hold the open state yourself and pass it in: the trigger still toggles it, and your other button writes the same state. Pass open and update it in onOpenChange, which receives the new boolean.

How do I make a Collapsible start open?

Set defaultOpen on Collapsible.

It still opens and closes on its own afterwards. If people should find it the way they left it, save the state yourself and pass it back as the controlled value.

Does the content of a closed Collapsible stay in the page?

Yes, it stays mounted and hidden, so fields inside it still submit with a form.

How do I use my own button as the Collapsible trigger in Base UI?

Render your button through CollapsibleTrigger so it gets the open state and the ARIA attributes. Pass your button as the trigger's render prop. An icon-only trigger needs an aria-label.

How do I change or turn off the Collapsible animation?

The content animates its height when it opens and closes. Set disableAnimation on CollapsibleContent to open and close it at once, or edit the timing classes in the collapsible file in your components/ui folder. People who ask for reduced motion don't get the animation.

Can I nest Collapsibles, like a file tree?

Yes. Each Collapsible is independent, so you can put one inside another's content, as the File Tree demo does. For a large tree with keyboard navigation and selection, use a Tree View instead.

API Reference

Collapsible

<div> data-slot="collapsible"

PropType
open?boolean

Whether the collapsible panel is currently open. To render an uncontrolled collapsible, use the defaultOpen prop instead.

defaultOpen?boolean

Whether the collapsible panel is initially open. To render a controlled collapsible, use the open prop instead.

Default
false
onOpenChange?(open: boolean, eventDetails: CollapsibleRootChangeEventDetails) → void

Event handler called when the panel is opened or closed.

Type
(open: boolean, eventDetails: CollapsibleRootChangeEventDetails) → void
Parameters

open – boolean

eventDetails – CollapsibleRootChangeEventDetails

Returns
void
disabled?boolean

Whether the component should ignore user interaction.

Default
false
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

AttributeType
data-state"open" | "closed"

"open" or "closed".

Type
"open" | "closed"

CollapsibleTrigger

<button> data-slot="collapsible-trigger"

PropType
nativeButton?boolean

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>).

Default
true
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Forwarded to the part.

AttributeType
data-disabled?string

Present when disabled is true.

data-state"open" | "closed"

"open" or "closed".

Type
"open" | "closed"

CollapsibleContent

<div> data-slot="collapsible-content"

PropType
disableAnimation?boolean

Opens and closes it at once, without the height animation.

Default
false
hiddenUntilFound?boolean

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.

Default
false
keepMounted?boolean

Whether to keep the element in the DOM while the panel is hidden. This prop is ignored when hiddenUntilFound is used.

Default
false
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

CollapsibleIndicator

<span> data-slot="collapsible-indicator"

PropType
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

With other components

  • Free

    Button

    Buttons

    The usual trigger

  • Free

    Card

    Layout

    Frame around the panel

  • Free

    Field

    Inputs

    Extra settings fields

  • Free

    Input

    Inputs

    Values in the panel

  • Free

    Tabs

    Navigation

    Explorer chrome

  • Free

    Accordion

    Disclosure

    A one-open list

  • Free

    Sidebar

    Navigation

    Folders in the nav

  • Free

    Switch

    Selection

    Control in the row

  • Free

    Separator

    Layout

    Split the extra fields

  • Free

    Dialog

    Overlays

    Details in an overlay

  • Free

    Sheet

    Overlays

    Side panel instead

  • Free

    Tree View

    Disclosure

    Many nested levels