Collapse
Reveal or hide content with a pure-CSS height animation — a single-cell grid whose row track animates between 0fr and 1fr, so auto-height content expands and collapses cleanly both ways.
Collapse animates a section open and closed by its height — without ever measuring the DOM. It's the disclosure primitive the Notification toasts use to reveal their description and action independently, extracted so you can reuse it anywhere (accordion rows, "show more" panels, inline detail).
The trick is a single-cell grid whose row track animates between 0fr (collapsed) and 1fr (revealed). Because the track is a fraction, it resolves to the content's own height — so auto-height content animates in and out with a plain CSS transition, no ResizeObserver, no height reads, no jump.
import { Collapse } from '@42/ui-react/collapse';
const [open, setOpen] = useState(false);
<button onClick={() => setOpen((o) => !o)}>Toggle</button>
<Collapse open={open}>
<p>Any content — it reveals and collapses at its natural height.</p>
</Collapse>Playground
Toggle open and watch the content animate. It's a Server Component (state lives in the parent), so there's no hydration cost.
Component
Dir
Presets
Props
Code
<Component open> This content reveals and collapses with a height animation.</Component>How it animates
The outer element is the animated grid (grid-template-rows: 0fr → 1fr); the inner cell has min-height: 0 and clips its overflow, so the content slides under the fold as the track opens. Two things follow from that:
- Independent sections. Each
Collapseanimates on its own timeline, so siblings (say a description and an action row) reveal separately rather than fighting over one shared height. - Reduced motion. The transition is dropped under
prefers-reduced-motion, so the toggle is instant.
Override the timing with a duration-* / ease-* class on className (tailwind-merge lets the later class win), and style the sliding region — padding, layout — with contentClassName:
<Collapse open={open} className="duration-500" contentClassName="flex flex-col gap-2 pt-2">
<Detail />
<Detail />
</Collapse>Keeping content mounted through the exit
Collapse animates its own height, but if you unmount the children the moment you close it, there's nothing left to animate out — it snaps. Keep the last content mounted through the collapse (a ref cache is enough) so the exit has something to shrink:
const lastDetail = useRef(detail);
if (detail != null) lastDetail.current = detail;
<Collapse open={detail != null}>{detail ?? lastDetail.current}</Collapse>API
Prop
Type
Also accepts any div prop (id, role, style, event handlers, …), forwarded to the grid root.