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 is not public on npm yet. Until the first release, pack @diffgazer/add from the repository and install that tarball into this app, which is what puts dgadd on pnpm exec.

UI components require Tailwind CSS v4. Local copy mode imports src/styles/styles.css; package mode uses @diffgazer/ui CSS once packages are available.

Parameters

NameTypeDefaultDescription
openrequiredbooleanWhether 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() => voidFired 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
presentrequiredbooleanWhether the element should be in the DOM. Use this for conditional rendering.
exitingrequiredbooleanTrue during the closing animation phase while present remains true. Both become false after the animation or fallback timer completes.
onAnimationEndrequired(e: AnimationEvent) => voidReact-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.