Collapsible
Collapsible is a component that shows or hides content.
This is the collapsed content. The element that shows and hides the content has role button
When the content is visible, the element with role `button` has `aria-expanded` set to `true`
When the content panel is hidden, it is set to `false`
Installation
Base UI components are all available as a single package.
npm install @base-ui-components/react
Once you have the package installed, import the component.
import { Collapsible } from '@base-ui-components/react/collapsible';
Anatomy
<Collapsible.Root />
is a top-level component that facilitates communication between other components. It does not render to the DOM by default.<Collapsible.Trigger />
is the trigger element, a<button>
by default, that toggles the open/closed state of the panel<Collapsible.Panel />
is component that contains the Collapsible's contents
<Collapsible.Root>
<Collapsible.Trigger>Toggle</Collapsible.Trigger>
<Collapsible.Panel>This is the content</Collapsible.Panel>
</Collapsible.Root>
Hidden state
<Collapsible.Panel />
is unmounted from the DOM by default when the component is closed. You can use the keepMounted
prop to to keep it mounted when closed, and instead rely on the hidden
attribute to hide the content:
<Collapsible.Root>
<Collapsible.Trigger>Toggle</Collapsible.Trigger>
<Collapsible.Panel keepMounted>{/* content */}</Collapsible.Panel>
</Collapsible.Root>
Improving searchability of hidden content
This is not yet
supported in Safari and
Firefox as of August 2024 and will fall back to the default hidden
behavior.
Content hidden in the Collapsible.Panel
component can be made accessible only to a browser's find-in-page functionality with the hiddenUntilFound
prop to improve searchability:
<Collapsible.Root defaultOpen={false}>
<Collapsible.Trigger>Toggle</Collapsible.Trigger>
<Collapsible.Panel hiddenUntilFound>
When this component is closed, this sentence will only be accessible to the browser's native
find-in-page functionality
</Collapsible.Panel>
</Collapsible.Root>
When hiddenUntilFound
is used, the Panel
remains mounted even when closed, overriding the keepMounted
prop.
CSS animations are recommended for animated collapsibles that use this feature. Currently there is browser bug that does not highlight the found text inside elements that have a CSS transition applied.
This relies on the HTML hidden="until-found"
attribute which only has partial browser support as of August 2024, but automatically falls back to the default hidden
state in unsupported browsers.
Animations
Animation states
Three states are available as data attributes to animate the Collapsible:
[data-open]
-open
state istrue
.[data-starting-style]
- thehidden
attribute was just removed from the DOM and the panel element participates in page layout. Thedata-starting-style
attribute will be removed 1 animation frame later.[data-ending-style]
- the panel element is in the process of being hidden from the DOM, but is still mounted.
The component can be animate when opening or closing using either:
- CSS animations
- CSS transitions
- JavaScript animations
Styling
The Collapsible.Panel
element receives the following CSS variables about its dimensions, which can be used to style animations or transitions:
--collapsible-panel-height
: Specifies the height of thePanel
.--collapsible-panel-width
: Specifies the width of thePanel
.
CSS Animations
CSS animations can be used with two declarations:
.CollapsiblePanel {
overflow: hidden;
animation: slideUp 300ms ease-in;
}
.CollapsiblePanel[data-open] {
animation: slideDown 300ms ease-out;
}
@keyframes slideDown {
from {
height: 0;
}
to {
height: var(--collapsible-panel-height);
}
}
@keyframes slideUp {
from {
height: var(--collapsible-panel-height);
}
to {
height: 0;
}
}
This is the collapsed content
You can find the Base UI repository here
This is a longer sentence and also the third paragraph
CSS Transitions
When using CSS transitions, styles for the Panel
must be applied to three states:
- The exiting styles, placed on the base element class
- The open styles, placed on the base element class with
[data-state="open"]
- The entering styles, placed on the base element class with
[data-starting-style]
.CollapsiblePanel {
overflow: hidden;
/* The final styles once closed/exited */
height: 0;
transition: height 300ms ease-in;
}
/* The final styles once opened/entered */
.CollapsiblePanel[data-open] {
height: var(--collapsible-panel-height);
transition: height 300ms ease-out;
}
/* The initial styles when opening/entering */
.CollapsiblePanel[data-starting-style] {
height: 0;
}
This is the collapsed content.
You can find the Base UI repository here.
This is a longer sentence and also the third paragraph.
JavaScript Animations
Use the keepMounted
prop lets an external library control the mounting, for example framer-motion
:
function App() {
const [open, setOpen] = useState(false);
return (
<Collapsible.Root open={open} onOpenChange={setOpen}>
<Collapsible.Trigger>Toggle</Collapsible.Trigger>
<Collapsible.Panel
keepMounted
render={
<motion.div
key="CollapsiblePanel"
initial={false}
animate={open ? 'open' : 'closed'}
exit={!open ? 'open' : 'closed'}
variants={{
open: {
height: 'auto',
transition: { duration: 0.6, ease: 'ease-out' },
},
closed: {
height: 0,
transition: { duration: 0.6, ease: 'ease-in' },
transitionEnd: { display: 'revert-layer' },
},
}}
/>
}
>
This is the content
</Collapsible.Panel>
</Collapsible.Root>
);
}
Overriding default components
Use the render
prop to override the rendered elements with your own components.
// Element shorthand
<Collapsible.Root render={<MyCollapsibleRoot />} />
// Function
<Collapsible.Root render={(props) => <MyCollapsibleRoot {...props} />} />
The Collapsible.Root
component can optionally not render an element to the DOM by passing null
to the render prop:
// does not render an element, only its children
<Collapsible.Root render={null} />
API Reference
CollapsibleRoot
Prop | Type | Default | Description |
---|---|---|---|
animated | bool | true | If true , the component supports CSS/JS-based animations and transitions. |
className | union | Class names applied to the element or a function that returns them based on the component's state. | |
defaultOpen | bool | false | If true , the Collapsible is initially open. This is the uncontrolled counterpart of open . |
disabled | bool | false | If true , the component is disabled. |
onOpenChange | func | Callback fired when the Collapsible is opened or closed. | |
open | bool | If true , the Collapsible is initially open. This is the controlled counterpart of defaultOpen . |
CollapsibleTrigger
Prop | Type | Default | Description |
---|---|---|---|
className | union | Class names applied to the element or a function that returns them based on the component's state. | |
render | union | A function to customize rendering of the component. |
CollapsiblePanel
Prop | Type | Default | Description |
---|---|---|---|
className | union | Class names applied to the element or a function that returns them based on the component's state. | |
bool | false | If true , sets the hidden state using hidden="until-found" . The panel remains mounted in the DOM when closed and overrides keepMounted . If false , sets the hidden state using hidden . | |
keepMounted | bool | false | If true , the panel remains mounted when closed and is instead hidden using the hidden attribute If false , the panel is unmounted when closed. If the hiddenUntilFound prop is used, the panel overrides this prop and is remains mounted when closed. |
render | union | A function to customize rendering of the component. |