Getting Started
Zag can be used within most JS frameworks like Vue, React and Solid.
To get Zag running, you'll need to:
- 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
- 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 theuseSnapshot
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'sapi
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'sapi
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'sapi
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