Skip to main content
0.79.0
View Zag.js on Github
Join the Discord server

Getting Started

Zag can be used within most JS frameworks like Vue, React and Solid.

To get Zag running, you'll need to:

  1. Install the machine for the component you're interested in. Let's say you want to use the tooltip machine.
npm install @zag-js/tooltip # or yarn add @zag-js/tooltip
  1. Install the adapter for the framework of your choice. At the moment, Zag is available for React, Vue 3 and Solid.js. Let's say you use React.
npm install @zag-js/react # or yarn add @zag-js/react

Congrats! You're ready to use tooltip machine in your project.

Using the machine

Here's an example of the tooltip machine used in a React.js project.

import * as tooltip from "@zag-js/tooltip" import { useMachine, normalizeProps } from "@zag-js/react" export function Tooltip() { const [state, send] = useMachine(tooltip.machine({ id: "1" })) const api = tooltip.connect(state, send, normalizeProps) return ( <> <button {...api.getTriggerProps()}>Hover me</button> {api.open && ( <div {...api.getPositionerProps()}> <div {...api.getContentProps()}>Tooltip</div> </div> )} </> ) }

Zag uses valtio behind the scenes to provide automatic render optimizations due to the useSnapshot hook used within the machine hook. Learn more

Usage with Vue 3 (JSX)

Zag works seamlessly with Vue's JSX approach. Here's how to use the same tooltip logic in Vue:

import * as tooltip from "@zag-js/tooltip" import { normalizeProps, useMachine } from "@zag-js/vue" import { computed, defineComponent, h, Fragment } from "vue" export default defineComponent({ name: "Tooltip", setup() { const [state, send] = useMachine(tooltip.machine({ id: "1" })) const apiRef = computed(() => tooltip.connect(state.value, send, normalizeProps), ) return () => { const api = apiRef.current return ( <> <div> <button {...api.getTriggerProps()}>Hover me</button> {api.open && ( <div {...api.getPositionerProps()}> <div {...api.getContentProps()}>Tooltip</div> </div> )} </div> </> ) } }, })

There are some extra functions that need to be used in order to make it work:

  • normalizeProps - Converts the props of the component into the format that is compatible with Vue.
  • computed - Ensures that the tooltip's api is always up to date with the current state of the machine.

Usage with Solid.js

We love Solid.js and we've added support for it. Here's how to use the same tooltip logic in Solid:

import * as tooltip from "@zag-js/tooltip" import { normalizeProps, useMachine } from "@zag-js/solid" import { createMemo, createUniqueId, Show } from "solid-js" export function Tooltip() { const [state, send] = useMachine(tooltip.machine({ id: createUniqueId() })) const api = createMemo(() => tooltip.connect(state, send, normalizeProps)) return ( <div> <button {...api().getTriggerProps()}>Hover me</button> <Show when={api().open}> <div {...api().getPositionerProps()}> <div {...api().getContentProps()}>Tooltip</div> </div> </Show> </div> ) }

There are some extra functions that need to be used in order to make it work:

  • normalizeProps - Converts the props of the component into the format that is compatible with Solid.
  • createMemo - Ensures that the tooltip's api is always up to date with the current state of the machine.

Usage with Svelte

Here's how to use the same tooltip logic in Svelte:

<script lang="ts"> import * as tooltip from "@zag-js/tooltip" import { useMachine, normalizeProps } from "@zag-js/svelte" const [snapshot, send] = useMachine(tooltip.machine({ id: "1" })) const api = $derived(tooltip.connect(snapshot, send, normalizeProps)) </script> <div> <button {...api.getTriggerProps()}>Hover me</button> {#if api.open} <div {...api.getPositionerProps()}> <div {...api.getContentProps()}>Tooltip</div> </div> {/if} </div>

There are some extra functions that need to be used in order to make it work:

  • normalizeProps - Converts the props of the component into the format that is compatible with Svelte.
  • $derived - Ensures that the tooltip's api is always up to date with the current state of the machine.

About prop normalization

There are subtle difference between how JSX attributes are named across frameworks like React, Solid, Vue and Svelte. Here are some examples:

Keydown listener

  • React and Solid: The keydown listener property is onKeyDown.
  • Vue: The keydown listener property is onKeydown.

Styles

  • React: Pass a numeric value for margin attributes like { marginBottom: 4 }.
  • Solid: It has to be { "margin-bottom": "4px" }.
  • Vue: You need to ensure the value is a string with unit. { marginBottom: "4px" }.

These little nuances between frameworks are handled automatically when you use normalizeProps.

The goal of Zag is to help you abstract the interaction and accessibility patterns into a statechart so you never have to re-invent the wheel.

Thanks for reading! If you're curious about how state machines work, the next page will give you a quick overview.

Edit this page on GitHub

Proudly made in🇳🇬by Segun Adebayo

Copyright © 2024
On this page