Getting started
A working, animated, accessible disclosure panel in four steps. Install, wire a button to the open prop, polish the motion with conditional classes and a custom duration, then decide how the panel should mount. You write no animation code — the browser owns the motion.
Here's the destination. A notifications panel that expands and collapses smoothly, fades its content in step with the height change, and tells assistive technology exactly what the button controls.
1. Install
One package, one peer dependency (React 18+). Other package managers and browser support notes are on the installation page.
npm install @westopp/react-collapsible
Two imports and you're set: Collapsible is a named export, and the stylesheet — which carries the entire animation — is imported once at your app root.
// once, at your app root import '@westopp/react-collapsible/styles.css' // wherever you build the panel import { Collapsible } from '@westopp/react-collapsible'
2. A minimal toggle
Collapsible is fully controlled — it holds no open/closed state of its own. You keep the state (useState here, a store if you prefer) and pass it down as open.
The other required prop is id, and it isn't decoration. The button that toggles the panel declares aria-controls with the same value, plus aria-expanded reflecting the current state. That pairing is what tells screen readers the button and the panel belong together — wire it from the first line, it's two attributes.
import { useState } from 'react' import { Collapsible } from '@westopp/react-collapsible' export const NotificationsPanel = () => { const [open, setOpen] = useState(false) return ( <section> <button type="button" aria-controls="getting-started-notifications" aria-expanded={open} onClick={() => setOpen((v) => !v)} > {open ? 'Hide notifications' : 'Show notifications'} </button> <Collapsible id="getting-started-notifications" open={open}> <ul> <li>Deploy finished</li> <li>New comment on your PR</li> <li>Weekly digest ready</li> </ul> </Collapsible> </section> ) }
This already animates. The stylesheet transitions the panel between collapsed and expanded over 500ms — no measuring, no layout effects, pure CSS. The full contract behind id, including how collapsed content is treated, is on the accessibility page.
3. Polish the motion
The collapse handles the panel's size; the content deserves its own polish. Inside className, a class prefixed with open: applies only while the panel is open, closed: only while it's closed, and anything unprefixed always applies. The component flips these in sync with the collapse animation, so pair them with a transition utility and the fade tracks the height change exactly.
While you're at it, transitionDuration swaps the stylesheet's 500ms default for whatever suits the panel.
<Collapsible id="getting-started-notifications" open={open} transitionDuration="300ms" className="open:opacity-100 closed:opacity-0 transition-opacity duration-300" > {/* the same list as before */} </Collapsible>
Now the panel collapses in 300ms while the list fades over the same 300ms — which is exactly what the demo at the top of this page does.
4. Start expanded
Some panels should begin open. A Collapsible mounted with open already true animates open on first paint — the browser sees it enter the page exactly like a toggle. When that entry animation is unwanted, initialize your state to true and add initialOpen: the first frame renders with a zero-second transition, then the configured duration takes over for every toggle after.
const [open, setOpen] = useState(true) return ( <Collapsible id="getting-started-notifications" open={open} initialOpen> {/* content */} </Collapsible> )
initialOpen doesn't manage state — open still does that. It only suppresses the mount animation. How the open prop is scheduled internally — including the one-frame deferral that lets your conditional classes transition on open instead of snapping — is covered in controlled state.
Where next
- How it works — the CSS Grid technique end to end:
0frto1frtracks,@starting-style, and why no JavaScript ever measures your content. - Building an accordion — the most common composite pattern: several panels, one open at a time.