Skip to content

Presence

hookanimationmountunmountpresence

Hook for animating mount/unmount transitions with CSS animations — keeps element in DOM during exit animation, removes after completion.

tsx
const ref = useRef<HTMLDivElement>(null);const { present } = usePresence({ open, ref });return present ? (  <div ref={ref} data-state={open ? "open" : "closed"}>    {children}  </div>) : null;

Installation

$pnpm exec dgadd add ui/presence
[Installs to]src/hooks/use-presence.ts[Item]ui/presence

dgadd ships in @diffgazer/add on npm: pnpm add -D @diffgazer/add puts it on pnpm exec, and npx @diffgazer/add runs it without the install.

UI components require Tailwind CSS v4. Local copy mode imports src/styles/styles.css; package mode uses @diffgazer/ui CSS from the installed package.

Parameters

NameTypeDefaultDescription
openrequiredboolean—Whether the content should be visible. When true, mounts immediately. When false, keeps mounted until the exit animation resolves or the fallback timer fires.
refRefObject<HTMLElement | null>—Ref to the animated element. When provided, filters bubbling animationend events from child elements. Recommended when the animated element has children with their own CSS animations.
exitFallbackMsnumber250Max ms to wait for animationend before forcing the closing -> hidden transition. Raise to at least 2x the exit-animation duration when customizing longer animations.
onExitComplete() => void—Fired after the exit animation resolves, or after exitFallbackMs fires, and the element transitions to hidden.

Returns

{ present: boolean; exiting: boolean; onAnimationEnd: (e: AnimationEvent) => void }Object with present flag for conditional rendering plus optional React-synthetic-event callbacks. When a ref is supplied the hook attaches its own DOM listeners and most consumers only need `present`.
NameTypeDefaultDescription
presentrequiredboolean—Whether the element should be in the DOM. Use this for conditional rendering.
exitingrequiredboolean—True during the closing animation phase while present remains true. Both become false after the animation or fallback timer completes.
onAnimationEndrequired(e: AnimationEvent) => void—React-synthetic onAnimationEnd callback for consumers that do not pass a ref. When a ref is provided this handler is a no-op; the hook's native listener handles completion and filters bubbling child events.

Examples

Basic Toggle

Preview

Tooltip Hover

Preview

Notes

State machine

Uses a three-phase state machine (hidden → open → closing → hidden). Phase transitions happen synchronously during render so the element is in the DOM before any useLayoutEffect runs in child components.

CSS animation pattern

Pair with data-state attribute and CSS @keyframes. Set data-state="open"|"closed" and use data-[state=open]:animate-[...] / data-[state=closed]:animate-[...] for enter/exit animations.

Used by

Built into FloatingPanel (the shared anchored-surface primitive) and DialogShell. Popover, Select, and Tooltip inherit presence transitions through FloatingPanel; CommandPaletteContent inherits through DialogShell.

Source

Highlighted source loads after this disclosure opens. Browse the source repository.