Docs
useExitTransition

useExitTransition

Keeps an element mounted through its CSS exit transition. This is the design system's replacement for JS animation-library "presence" wrappers: the exit is pure CSS (a motion preset's [data-state="closing"] state), and the hook only decides when the DOM node can go away.

When open flips to false, the element re-renders with data-state="closing" — the exit transition starts — and unmounts on the first matching transitionend, backed by two guards: a timeout fallback at the computed transition time plus slack (transitionend is lossy), and a synchronous unmount when the computed duration is zero (reduced-motion kill-switches, test environments, or no transition at all).

When to use

✅ Use when…🚫 Avoid when…
  • Conditionally-rendered surfaces that should animate out before leaving the DOM: modals, sheets, toasts, popovers.
  • Together with a motion preset — the preset styles the closing state, the hook drives the unmount.
  • Elements that stay mounted and only toggle visibility (hidden, display) — use @starting-style + transition-behavior: allow-discrete in CSS, no JS needed.
  • Enter-only animation — @starting-style already covers mount with zero JS.

Signature

const { mounted, state, ref } = useExitTransition(
  open: boolean,
  opt?: { property?: string },
);
  • mounted — render the element while true; flips to false after the exit finishes.
  • state — mirror as data-state={state} on the animated element ('open' | 'closing').
  • ref — attach to the element whose transition drives the unmount.
  • opt.property — only a transitionend for this CSS property completes the exit (useful when several properties transition and one is the authoritative "done" signal, e.g. grid-template-rows on a collapsing wrapper).

Example

function Toast({ open, children }) {
  const { mounted, state, ref } = useExitTransition(open);
 
  if (!mounted) return null;
  return (
    <Flex ref={ref} motion="slide-up" data-state={state}>
      {children}
    </Flex>
  );
}

Accessibility

Under prefers-reduced-motion: reduce the motion vars zero the transition durations, so exits complete almost immediately — the hook needs no special handling. When a closing element carries a live region (role="status"), set aria-hidden="true" during the closing phase so screen readers do not announce content that is on its way out.

See also

  • The motion prop (motion presets) — the CSS side of this contract.
  • Motion concept page — tokens, reduced motion and the enter/exit model.