BETA
View Zag.js on Github
Join the Discord server

UI components powered by Finite State Machines

A collection of framework-agnostic UI component patterns like accordion, menu, and dialog that can be used to build design systems for React, Vue and Solid.js

Get Started

Zag provides the component API for the Web

Get Started

Powered by Statecharts

Simple, resilient component logic. Write component logic once and use anywhere.

Accessible

Built-in adapters that connects machine output to DOM semantics in a WAI-ARIA compliant way.

Framework agnostic

Component logic is largely JavaScript code and can be consumed in any JS framework.

Machines handle the logic. You handle the UI

Zag machine APIs are completely headless and unstyled. Use your favorite styling solution and get it matching your design system.

  • Install the machine you need
  • Consume the machine
  • Connect machine to your UI
import * as numberInput from "@zag-js/number-input" import { useMachine, normalizeProps } from "@zag-js/react" export function NumberInput() { // 1. Consume the machine const [state, send] = useMachine(numberInput.machine({ id: "1" })) // 2. Setup the machine with a unique id // 3. Grab the provided API const api = numberInput.connect(state, send, normalizeProps) // 4. Render the component return ( <div {...api.rootProps}> <label {...api.labelProps}>Enter number:</label> <div> <button {...api.decrementButtonProps}>DEC</button> <input {...api.inputProps} /> <button {...api.incrementButtonProps}>INC</button> </div> </div> ) }

Work in your favorite JS framework

Finite state machines for building accessible design systems and UI components. Works with React, Vue and Solid.

import * as numberInput from "@zag-js/number-input" import { useMachine, normalizeProps } from "@zag-js/react" export function NumberInput() { const [state, send] = useMachine(numberInput.machine({ id: "1" })) const api = numberInput.connect(state, send, normalizeProps) return ( <div {...api.rootProps}> <label {...api.labelProps}>Enter number:</label> <div> <button {...api.decrementButtonProps}>DEC</button> <input {...api.inputProps} /> <button {...api.incrementButtonProps}>INC</button> </div> </div> ) }

The better way to model component logic

Today, design systems are becoming a very popular toolkit for companies to create a cohesive and accessible user experience for their customers.

With the rise of component-driven development, there's an endless re-implementation of common widgets (tabs, menu, etc.) in multiple frameworks. These implementations tend to grow in complexity over time and often become hard to understand, debug, improve, or test.
We need a better way to model component logic.
Zag is a new approach to the component design process, designed to help you avoid re-inventing the wheel and build better UI components regardless of framework. Heavily inspired by XState, but built to make it easier to maintain, test, and enhance.

With Zag, we're abstracting the complex logic for many components into a cohesive, framework-agnostic system — giving you complete control over styling and providing a thin adapter for your favorite framework.

Welcome to the future of building interactive components!
Segun Adebayo

Segun Adebayo

Creator of Zag.js

Build your design system with state machines today

Get Started