GeezerVison is showing zagjs.com — Zag - Rapidly build UI components without sweating over the logic. Zag provides the component API for the WebGet StartedPowered 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 UIZag 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"
import { useId } from "react"
export function NumberInput() {
// 1. Consume the machine
const service = useMachine(numberInput.machine, { id: useId() })
// 2. Convert machine to the provided API
const api = numberInput.connect(service, normalizeProps)
// 3. Render the component
return (
<div {...api.getRootProps()}>
<label {...api.getLabelProps()}>Enter number:</label>
<div>
<button {...api.getDecrementTriggerProps()}>DEC</button>
<input {...api.getInputProps()} />
<button {...api.getIncrementTriggerProps()}>INC</button>
</div>
</div>
)
}
Work in your favorite JS frameworkFinite 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"
import { useId } from "react"
export function NumberInput() {
const service = useMachine(numberInput.machine, { id: useId() })
const api = numberInput.connect(service, normalizeProps)
return (
<div {...api.getRootProps()}>
<label {...api.getLabelProps()}>Enter number:</label>
<div>
<button {...api.getDecrementTriggerProps()}>DEC</button>
<input {...api.getInputProps()} />
<button {...api.getIncrementTriggerProps()}>INC</button>
</div>
</div>
)
}
<script setup>
import * as numberInput from "@zag-js/number-input"
import { normalizeProps, useMachine } from "@zag-js/vue"
import { computed, useId } from "vue"
const service = useMachine(numberInput.machine, { id: useId() })
const api = computed(() => numberInput.connect(service, normalizeProps))
</script>
<template>
<div ref="ref" v-bind="api.getRootProps()">
<label v-bind="api.getLabelProps()">Enter number</label>
<div>
<button v-bind="api.getDecrementTriggerProps()">DEC</button>
<input v-bind="api.getInputProps()" />
<button v-bind="api.getIncrementTriggerProps()">INC</button>
</div>
</div>
</template>
import * as numberInput from "@zag-js/number-input"
import { normalizeProps, useMachine } from "@zag-js/solid"
import { createMemo, createUniqueId } from "solid-js"
export function NumberInput() {
const service = useMachine(numberInput.machine, { id: createUniqueId() })
const api = createMemo(() => numberInput.connect(service, normalizeProps))
return (
<div {...api().getRootProps()}>
<label {...api().getLabelProps()}>Enter number:</label>
<div>
<button {...api().getDecrementTriggerProps()}>DEC</button>
<input {...api().getInputProps()} />
<button {...api().getIncrementTriggerProps()}>INC</button>
</div>
</div>
)
}
<script lang="ts">
import * as numberInput from "@zag-js/number-input"
import { normalizeProps, useMachine } from "@zag-js/svelte"
const id = $props.id()
const service = useMachine(numberInput.machine, { id })
const api = $derived(numberInput.connect(service, normalizeProps))
</script>
<div {...api.getRootProps()}>
<label {...api.getLabelProps()}>Enter number:</label>
<div>
<button {...api.getDecrementTriggerProps()}>DEC</button>
<input {...api.getInputProps()} />
<button {...api.getIncrementTriggerProps()}>INC</button>
</div>
</div>
The better way to model component logicToday, 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 AdebayoCreator of Zag.js Build your design system with state machines today |