Skip to main content
Zag is distributed as independent npm packages — one per component machine, plus a small adapter for each framework. This guide walks through installing and using the tooltip machine as a concrete example. The same pattern applies to every other machine in the library.

Install the packages

You need two packages:
  1. A component machine — the framework-agnostic interaction logic (e.g. @zag-js/tooltip)
  2. A framework adapter — integrates the machine with your framework’s reactivity system
1

Install a component machine

Pick the machine for the component you want to use. This example uses @zag-js/tooltip.
npm install @zag-js/tooltip
2

Install the framework adapter

Install the adapter that matches your framework.
npm install @zag-js/react

Using the machine

All frameworks follow the same two-step pattern: start the machine with useMachine, then connect it to your component tree using the machine’s connect function.
import * as tooltip from "@zag-js/tooltip"
import { useMachine, normalizeProps } from "@zag-js/react"

export function Tooltip() {
  const service = useMachine(tooltip.machine, { id: "my-tooltip" })
  const api = tooltip.connect(service, normalizeProps)

  return (
    <>
      <button {...api.getTriggerProps()}>Hover me</button>
      {api.open && (
        <div {...api.getPositionerProps()}>
          <div {...api.getContentProps()}>Tooltip content</div>
        </div>
      )}
    </>
  )
}

Understanding normalizeProps

normalizeProps is a required argument to every connect call. It converts the props returned by the machine into the exact format each framework expects. There are subtle differences in how element attributes and event handlers are named across frameworks. For example: Event handler casing
FrameworkKeydown listener property
React, SolidonKeyDown
VueonKeydown
Inline styles
FrameworkStyle value for margin
React{ marginBottom: 4 } (number)
Solid{ "margin-bottom": "4px" }
Vue{ marginBottom: "4px" } (string with unit)
These differences are handled automatically when you import normalizeProps from the correct framework adapter. You never need to think about them.
Always import normalizeProps from the same package as useMachine — for example, @zag-js/react for React projects. Each adapter exports the normalizer appropriate for that framework.

Upgrading all Zag packages

Zag uses independent versioning for each package. To upgrade all @zag-js/* packages at once, use your package manager’s scoped upgrade command:
pnpm up "@zag-js/*"

Build docs developers (and LLMs) love