Skip to main content

Presence helps manage mount/unmount transitions with exit animations.

When a component is hidden or removed, the DOM usually removes it immediately. Presence keeps it mounted long enough for exit animations to finish.

The presence machine requires using CSS animations to animate the component's exit.

Loading...

Installation

Install the presence package:

npm install @zag-js/presence @zag-js/react # or yarn add @zag-js/presence @zag-js/react

Usage

Import the presence package:

import * as presence from "@zag-js/presence"

The presence package exports two key functions:

  • machine - State machine logic.
  • connect - Maps machine state to JSX props and event handlers.

Then use the framework integration helpers:

import * as presence from "@zag-js/presence" import { useMachine, normalizeProps } from "@zag-js/react" interface PresenceProps { present: boolean unmountOnExit?: boolean onExitComplete?: () => void } function Presence(props: PresenceProps) { const { unmountOnExit, present, onExitComplete, ...rest } = props const service = useMachine(presence.machine, { present, onExitComplete, }) const api = presence.connect(service, normalizeProps) if (!api.present && unmountOnExit) return null return ( <div hidden={!api.present} data-state={api.skip ? undefined : present ? "open" : "closed"} ref={api.setNode} {...rest} /> ) }

Running code after exit animation

Use onExitComplete to run logic after the exit animation finishes.

const service = useMachine(presence.machine, { present: open, onExitComplete() { console.log("Exit animation finished") }, })

Applying presence changes immediately

Set immediate to true to skip deferring present-state changes to the next frame.

const service = useMachine(presence.machine, { present: open, immediate: true, })

Styling guide

To style any entry and exit animations, set up the @keyframes and apply the animations.

@keyframes enter { from { scale: 0.9; opacity: 0; } to { opacity: 1; scale: 1; } } @keyframes exit { to { opacity: 0; scale: 0.9; } } [data-state="open"] { animation: enter 0.15s ease-out; } [data-state="closed"] { animation: exit 0.1s ease-in; }

You can then use the Presence component in your project.

function Example() { const [open, setOpen] = React.useState(true) return ( <> <button onClick={() => setOpen((c) => !c)}>Toggle</button> <Presence present={open} unmountOnExit> <div>Content</div> </Presence> </> ) }

Methods and Properties

Machine Context

The presence machine exposes the following context properties:

  • presentbooleanWhether the node is present (controlled by the user)
  • onExitCompleteVoidFunctionFunction called when the animation ends in the closed state
  • immediatebooleanWhether to synchronize the present change immediately or defer it to the next frame

Machine API

The presence api exposes the following methods:

  • skipbooleanWhether the animation should be skipped.
  • presentbooleanWhether the node is present in the DOM.
  • setNode(node: HTMLElement) => voidFunction to set the node (as early as possible)
  • unmountVoidFunctionFunction to programmatically unmount the node
Edit this page on GitHub