Collapsible
An interactive component which expands/collapses a panel.
Made by imskyleenPowered by
Installation
Base UI migration — forceMount is now keepMounted. The CollapsibleContent primitive no longer forwards
forceMount; to keep the panel in the DOM for SEO / transition purposes pass keepMounted on the underlying
Collapsible.Panel instead. See MIGRATION.md.
Controlled
Multiple independent collapsibles
Disabled
Default open
Chevron on leading edge
Reveal sensitive content
Usage
<Collapsible>
<CollapsibleTrigger>Collapsible Trigger</CollapsibleTrigger>
<CollapsibleContent>Collapsible Content</CollapsibleContent>
</Collapsible>When to use
Use this decision tree to determine when to use the Collapsible component:
Props
Keyboard interactions
| Key | Action |
|---|---|
Enter / Space on CollapsibleTrigger | Toggles the panel open / closed. |
Tab | Moves focus to the next focusable element (including into the panel when open). |
Accessibility
- The trigger exposes
aria-expanded+aria-controlspointing at the panel; Base UI also sets the compatdata-state="open" | "closed"attribute so existing CSS selectors keep working. - The trigger must be a real button (or another focusable element via
asChild); static<div>triggers lose keyboard activation. - Use
keepMountedonCollapsibleContentwhen the hidden content must remain in the DOM for SEO, intersection observers, or mid-animation measurements. See MIGRATION.md —forceMountfrom Radix is no longer forwarded.
Controlled vs uncontrolled
Uncontrolled — trigger toggles the panel locally:
<Collapsible>
<CollapsibleTrigger>Toggle</CollapsibleTrigger>
<CollapsibleContent>{/* ... */}</CollapsibleContent>
</Collapsible>Controlled — drive open from parent state (e.g. expand all / collapse all from a toolbar):
const [open, setOpen] = useState(false);
<Collapsible open={open} onOpenChange={setOpen}>
<CollapsibleTrigger>Toggle</CollapsibleTrigger>
<CollapsibleContent>{/* ... */}</CollapsibleContent>
</Collapsible>Animate UI Props
CollapsibleContent
Prop
Type
Don't delete from the DOM
The choice made (also Base UI's default) is to remove the element from the DOM for accessibility and performance reasons. However, this may pose a problem for SEO. If you want your Collapsible content to be taken into account by Google, replace the CollapsibleContent component with:
const CollapsibleContent = React.forwardRef<
React.ElementRef<typeof CollapsiblePrimitive.Content>,
CollapsibleContentProps
>(
(
{
className,
children,
transition = { type: 'spring', stiffness: 150, damping: 17 },
...props
},
ref,
) => {
const { isOpen } = useCollapsible();
return (
<CollapsiblePrimitive.Content asChild forceMount ref={ref} {...props}>
<motion.div
layout
initial={false}
animate={
isOpen
? { opacity: 1, height: 'auto', overflow: 'hidden' }
: { opacity: 0, height: 0, overflow: 'hidden' }
}
transition={transition}
className={className}
>
{children}
</motion.div>
</CollapsiblePrimitive.Content>
);
},
);Credits
- We use Base UI for the collapsible component.
- We take our inspiration from Shadcn UI for the collapsible style.
- We use Animate UI from imskyleen for all the animations.
Recent changes
- patchv1.2.0Pin shipped dependency floors to the version we develop against. Registry items now declare ranges like `^5.1.9` (the actual installed version) instead of collapsing to `^5.0.0`, so consumers start on the known-tested b…#133
- minorv1.1.0Theme docs refresh, readability pass on semantic foregrounds, and consumer-facing Base UI regression fixes.#121
- minorv1.0.0Post-Base-UI polish. Public API unchanged.#116
- majorv1.0.0Migrate every Radix-based primitive to `@base-ui/react@^1.4.0` (Base UI).#114
- patchv0.3.1**Accordion** - `AccordionTrigger` now shows `cursor-pointer` on hover.#112