# 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.

```jsx
import * as tooltip from "@zag-js/tooltip"
import { useMachine, normalizeProps } from "@zag-js/react"

export function Tooltip() {
  const service = useMachine(tooltip.machine, { id: "1" })

  const api = tooltip.connect(service, normalizeProps)

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

### Usage with Vue 3 (JSX)

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

```jsx
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 service = useMachine(tooltip.machine, { id: "1" })
    const apiRef = computed(() => tooltip.connect(service, 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:

```jsx
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 service = useMachine(tooltip.machine, { id: createUniqueId() })

  const api = createMemo(() => tooltip.connect(service, 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:

```html
<script lang="ts">
  import * as tooltip from "@zag-js/tooltip"
  import { useMachine, normalizeProps } from "@zag-js/svelte"

  const service = useMachine(tooltip.machine, { id: "1" })

  const api = $derived(tooltip.connect(service, 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.
A state machine is a tool for modeling stateful, reactive systems. It is useful
for declaratively describing the behavior of an application or component.

To model any logic as using the state machine pattern, it must have:

- A finite number of states.
- A finite number of transitions between states.

## Creating a state machine

Consider a simple toggle or switch component that consists of two states,
`active` and `inactive`. The initial state will be `active`

The supported transitions that can happen here:

- In the `active` state, when we click the toggle, it should transition to the
  `inactive` state
- In the `inactive` state, when we click the toggle, it should transition to the
  `active` state

Here's how we'll model the logic in code:

```jsx
import { createMachine } from "@zag-js/core"

const machine = createMachine({
  // initial state
  initialState() {
    return "active"
  },
  // the finite states
  states: {
    active: {
      on: {
        CLICK: {
          // go to inactive
          target: "inactive",
        },
      },
    },
    inactive: {
      on: {
        CLICK: {
          // go to active
          target: "active",
        },
      },
    },
  },
})
```

### TypeScript Guide

For TypeScript projects, you can add type definitions to make your machine
type-safe:

```tsx
import { createMachine, type Service } from "@zag-js/core"
import type { NormalizeProps, PropTypes } from "@zag-js/types"

interface ToggleSchema {
  state: "active" | "inactive"
  event: { type: "CLICK" }
}

export const machine = createMachine<ToggleSchema>({
  // ... same as above
})
```

## Writing the connect function

Now that we've modelled the component logic, let's map that to DOM attributes
and event handlers following the
[WAI-ARIA](https://www.w3.org/TR/wai-aria-practices-1.1/examples/checkbox/checkbox-1/checkbox-1.html)
specification for the switch component.

We'll write a function called `connect` to do this.

```jsx
function connect(service, normalize) {
  const { state, send } = service
  const active = state.matches("active")
  return {
    active,
    getButtonProps() {
      return normalize.button({
        type: "button",
        role: "switch",
        "aria-checked": active,
        onClick() {
          send({ type: "CLICK" })
        },
      })
    },
  }
}
```

### TypeScript Guide

For TypeScript projects, you can add type definitions to make your connect
function type-safe:

```tsx
import { type Service } from "@zag-js/core"
import type { NormalizeProps, PropTypes } from "@zag-js/types"

interface ToggleSchema {
  state: "active" | "inactive"
  event: { type: "CLICK" }
}

export function connect<T extends PropTypes>(
  service: Service<ToggleSchema>,
  normalize: NormalizeProps<T>,
) {
  const { state, send } = service
  const active = state.matches("active")
  return {
    active,
    getButtonProps() {
      return normalize.button({
        type: "button",
        role: "switch",
        "aria-checked": active,
        onClick() {
          send({ type: "CLICK" })
        },
      })
    },
  }
}
```

## Consuming the state machine

Here's how to consume the toggle machine logic and connect in React.js.

```jsx
import { useMachine, normalizeProps } from "@zag-js/react"
import { machine, connect } from "./toggle"

function Toggle() {
  const service = useMachine(machine)
  const api = connect(service, normalizeProps)

  return (
    <button
      {...api.getButtonProps()}
      style={{
        width: "40px",
        height: "24px",
        borderRadius: "999px",
        background: api.active ? "green" : "gray",
      }}
    >
      {api.active ? "ON" : "OFF"}
    </button>
  )
}
```

That's it! Now you've learned the fundamentals of a component state machine.

## Next Steps

Now that you understand the basics, learn about the advanced concepts that power Zag machines:

- [Building Machines Guide](/guides/building-machines) - Deep dive into context (bindable), watch/track, computed, refs, and more
## Why the need for `normalizeProps`?

The goal of `normalizeProps` is to convert the props of the component into the
format that is compatible with the respective framework.

It is also used to ensure that the returned properties are strongly typed.

There are subtle differences between how element attributes are named across
frameworks like React, Solid and Vue. 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`.

## How can I attach custom extra event handlers to the elements?

See the approach [here](/guides/composition#event-composition).

## How can I get Zag working in a custom window environment?

See the approach [here](/guides/composition#custom-window-environment).

## What would it take to support other frameworks?

We're currently interested in supporting as many frameworks as possible. The key
requirements are:

- **Support for "spread props"**: The framework should have support for
  spreading attributes and event handlers.

- **Exposed Typings**: The framework should expose the typings for the
  attributes and event handlers. This is optional but would provide the best DX.

## How do I upgrade all zag dependencies?

Since we use independent versioning for each zag package, it can sometimes be
inconvenient to upgrade all dependencies individually.

You can use scoped upgrades feature in your package manager to update all zag
packages seamlessly.

```bash
pnpm up "@zag-js/*"