Convert Figma logo to code with AI

chakra-ui logozag

Finite state machines for building accessible design systems and UI components. Works with modern frameworks, and even just Vanilla JS

3,931
157
3,931
6

Top Related Projects

A collection of libraries and tools that help you build adaptive, accessible, and robust user experiences.

Radix Primitives is an open-source UI component library for building high-quality, accessible design systems and web apps. Maintained by @workos.

7,824

Toolkit for building accessible web apps with React

18,417

Fluent UI web represents a collection of utilities, React components, and web components for building web applications.

Quick Overview

Zag is a set of low-level UI component primitives for building accessible and customizable UI components. It provides a collection of headless UI components that can be styled and integrated into various frontend frameworks and libraries.

Pros

  • Framework-agnostic: Can be used with React, Vue, Solid, and other frontend frameworks
  • Highly accessible: Built with a focus on keyboard navigation and screen reader support
  • Customizable: Provides low-level primitives that can be styled and extended easily
  • TypeScript support: Written in TypeScript for better type safety and developer experience

Cons

  • Learning curve: Requires understanding of headless UI concepts and state machines
  • Limited pre-built styles: Requires more effort to style components from scratch
  • Documentation could be more comprehensive: Some advanced use cases may not be well-documented
  • Relatively new project: May have fewer community resources compared to more established UI libraries

Code Examples

  1. Creating a simple accordion component:
import * as accordion from "@zag-js/accordion"
import { useMachine, normalizeProps } from "@zag-js/react"

function Accordion() {
  const [state, send] = useMachine(accordion.machine({ id: "1" }))
  const api = accordion.connect(state, send, normalizeProps)

  return (
    <div {...api.rootProps}>
      <h3>
        <button {...api.getTriggerProps({ value: "item-1" })}>Item 1</button>
      </h3>
      <div {...api.getContentProps({ value: "item-1" })}>Content 1</div>
      {/* Add more items as needed */}
    </div>
  )
}
  1. Creating a toggle button:
import * as toggle from "@zag-js/toggle"
import { useMachine, normalizeProps } from "@zag-js/react"

function ToggleButton() {
  const [state, send] = useMachine(toggle.machine({ id: "2" }))
  const api = toggle.connect(state, send, normalizeProps)

  return (
    <button {...api.buttonProps}>
      {api.isPressed ? "On" : "Off"}
    </button>
  )
}
  1. Creating a tooltip:
import * as tooltip from "@zag-js/tooltip"
import { useMachine, normalizeProps } from "@zag-js/react"

function Tooltip() {
  const [state, send] = useMachine(tooltip.machine({ id: "3" }))
  const api = tooltip.connect(state, send, normalizeProps)

  return (
    <>
      <button {...api.triggerProps}>Hover me</button>
      <div {...api.positionerProps}>
        <div {...api.contentProps}>This is a tooltip</div>
      </div>
    </>
  )
}

Getting Started

To start using Zag in your project:

  1. Install the core package and desired component packages:

    npm install @zag-js/core @zag-js/react @zag-js/accordion
    
  2. Import and use the component in your React application:

    import * as accordion from "@zag-js/accordion"
    import { useMachine, normalizeProps } from "@zag-js/react"
    
    function MyComponent() {
      const [state, send] = useMachine(accordion.machine({ id: "my-accordion" }))
      const api = accordion.connect(state, send, normalizeProps)
    
      // Use the api to render your component
    }
    
  3. Style the component using your preferred CSS solution.

Competitor Comparisons

A collection of libraries and tools that help you build adaptive, accessible, and robust user experiences.

Pros of React Spectrum

  • Comprehensive design system with a wide range of accessible components
  • Robust documentation and extensive examples
  • Strong support from Adobe and integration with other Adobe products

Cons of React Spectrum

  • Larger bundle size due to its comprehensive nature
  • Steeper learning curve for developers new to the ecosystem
  • Less flexibility for customization compared to more lightweight alternatives

Code Comparison

React Spectrum:

import { Button } from '@adobe/react-spectrum';

function MyComponent() {
  return <Button variant="cta">Click me</Button>;
}

Zag:

import { Button } from '@zag-js/react';

function MyComponent() {
  return <Button>Click me</Button>;
}

Key Differences

  • React Spectrum offers a more opinionated and complete design system, while Zag focuses on providing headless UI components
  • Zag allows for greater customization and flexibility in styling and behavior
  • React Spectrum has more built-in accessibility features, whereas Zag requires more manual implementation
  • Zag has a smaller footprint and may be better suited for projects that require a lightweight solution
  • React Spectrum benefits from Adobe's extensive resources and ecosystem support

Both libraries aim to provide accessible and reusable components, but they cater to different use cases and project requirements. The choice between them depends on factors such as project size, customization needs, and integration with existing design systems.

Radix Primitives is an open-source UI component library for building high-quality, accessible design systems and web apps. Maintained by @workos.

Pros of Primitives

  • More mature and widely adopted in the React community
  • Extensive documentation and examples available
  • Larger ecosystem of components and plugins

Cons of Primitives

  • Steeper learning curve for beginners
  • Less flexibility in customization compared to Zag
  • Heavier bundle size due to more comprehensive feature set

Code Comparison

Primitives:

import * as Dialog from '@radix-ui/react-dialog';

<Dialog.Root>
  <Dialog.Trigger>Open</Dialog.Trigger>
  <Dialog.Content>
    <Dialog.Title>Dialog Title</Dialog.Title>
    <Dialog.Description>Dialog content here</Dialog.Description>
  </Dialog.Content>
</Dialog.Root>

Zag:

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

const [state, send] = useMachine(dialog.machine())
const api = dialog.connect(state, send, normalizeProps)

<div {...api.rootProps}>
  <button {...api.triggerProps}>Open</button>
  <div {...api.contentProps}>Dialog content here</div>
</div>

Both libraries provide unstyled, accessible components for building user interfaces. Primitives offers a more traditional React component approach, while Zag uses a state machine-based architecture. Zag's approach may offer more fine-grained control over component behavior, but Primitives' simpler API might be easier for some developers to grasp quickly.

7,824

Toolkit for building accessible web apps with React

Pros of Ariakit

  • More extensive component library with a wider range of pre-built accessible components
  • Stronger focus on accessibility, with built-in ARIA attributes and keyboard navigation
  • More flexible styling options, allowing for easier customization of components

Cons of Ariakit

  • Steeper learning curve due to its more comprehensive API and features
  • Larger bundle size, which may impact performance in smaller projects

Code Comparison

Ariakit:

import { Button, Dialog } from "ariakit";

function App() {
  return (
    <Dialog.Root>
      <Dialog.Trigger as={Button}>Open dialog</Dialog.Trigger>
      <Dialog>Dialog content</Dialog>
    </Dialog.Root>
  );
}

Zag:

import { useDialog } from "@zag-js/dialog";
import { useMachine } from "@zag-js/react";

function App() {
  const [state, send] = useMachine(useDialog);
  const api = useDialog(state, send);
  return (
    <div>
      <button onClick={api.open}>Open dialog</button>
      {api.isOpen && <div>Dialog content</div>}
    </div>
  );
}

Both libraries provide accessible dialog components, but Ariakit offers a more declarative API, while Zag uses a machine-based approach for state management.

18,417

Fluent UI web represents a collection of utilities, React components, and web components for building web applications.

Pros of Fluent UI

  • Extensive component library with a wide range of UI elements
  • Strong integration with Microsoft products and services
  • Robust documentation and design guidelines

Cons of Fluent UI

  • Larger bundle size due to comprehensive feature set
  • Steeper learning curve for developers new to the ecosystem
  • Less flexibility for customization compared to more lightweight libraries

Code Comparison

Fluent UI button example:

import { PrimaryButton } from '@fluentui/react';

<PrimaryButton text="Click me" onClick={handleClick} />

Zag button example:

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

const [state, send] = useMachine(button.machine({ id: "1" }))
const api = button.connect(state, send, normalizeProps)

return <button {...api.buttonProps}>Click me</button>

Zag offers a more low-level approach, providing greater control over the component's behavior, while Fluent UI provides a higher-level abstraction with pre-styled components.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

Zag.js hero image

Zag

Finite state machines for accessible JavaScript components

  • Write once, use everywhere 🦄: The component interactions are modelled in a framework agnostic way. We provide adapters for JS frameworks like React, Solid, or Vue.
  • Focus on accessibility ♿️: Zag is built with accessibility in mind. We handle many details related to keyboard interactions, focus management, aria roles and attributes.
  • Headless ✨: The machine APIs are completely unstyled and gives you the control to use any styling solution you prefer.
  • Powered by state machines 🌳: Zag is built on top of the latest ideas in Statecharts. We don't follow the SCXML specifications, but we've created an API that we think will help us build more complex components fast.

Documentation

To see the documentation, visit zagjs.com/

Releases

For changelog, Check CHANGELOG.md


Problem

With the rise of design systems and component-driven development, there's an endless re-implementation of common component patterns (Tabs, Menu, Modal, etc.) in multiple frameworks.

Most of these implementations seem to be fairly similar in spirit, the differences being around the reactivity and effects systems for the framework (e.g. useState, useEffect in React.js). Framework specific solutions tend to grow in complexity over time and often become hard to understand, debug, improve or test.

Solution

Zag is a JavaScript API that implements common component patterns using the state machine methodology.

Installation

npm i --save @zag-js/{component}

# or

yarn add @zag-js/{component}

{component} represents any component machine like dialog (@zag-js/dialog), tooltip (@zag-js/tooltip) , etc.

For framework specific solutions, we provide simple wrappers to help you consume the component state machines.

  • ⚛️ @zag-js/react - React hooks for consuming machines in React applications
  • 💚 @zag-js/vue - Vue composition for consuming machines in Vue applications
  • 🎷 @zag-js/solid - Solid.js utilities for consuming machines in Solid.js applications

Usage

import { normalizeProps, useMachine } from "@zag-js/react"
import * as toggle from "@zag-js/toggle-group"
import { useId } from "react"

export function ToggleGroup() {
  const [state, send] = useMachine(toggle.machine({ id: useId() }))
  const api = toggle.connect(state, send, normalizeProps)

  return (
    <div {...api.getRootProps()}>
      <button {...api.getItemProps({ value: "bold" })}>B</button>
      <button {...api.getItemProps({ value: "italic" })}>I</button>
      <button {...api.getItemProps({ value: "underline" })}>U</button>
    </div>
  )
}

Guiding Principles

  • All component machines and tests are modelled according to the WAI-ARIA authoring practices
  • Write end-to-end tests for every component based on the WAI-ARIA spec. Regardless of the framework, users expect component patterns to work the same way!
  • All machines should be light-weight, simple, and easy to understand. Avoid using complex machine concepts like spawn, nested states, etc.

Fun Facts

Zag means to take a sharp change in direction. This clearly describes our approach of using state machines to power the logic behind UI components.

Teasers

  • When you see someone using classic react, vue or solid to build an interactive UI component that exists in Zag, tell them to "zag it!" ⚡️

  • Anyone using Zag will be called a "zagger" 💥

  • The feeling you get when you use Zag will be called "zagadat!" 🚀

  • The Zag community will be called "zag nation" 🔥


Commands

Build commands

Our build is managed with esbuild and turborepo to provide fast, concurrent builds across the packages.

  • build : Build the CJS, ESM and DTS files. This is the actual production build that we run in the CI.

Examples

Since zag is framework agnostic, we need a way to test it within a framework. The examples/ directory includes starter projects for the frameworks we support.

  • start-react : Starts the Next.js TypeScript project
  • start-vue : Starts the Vue 3 TypeScript project
  • start-solid : Starts the Solid TypeScript project

E2E Tests

We've setup end-to-end tests for every machine we built. We use Playwright for testing and we ensure that the component works the same way regardless of the framework.

  • e2e-react : Starts the E2E tests for the React project
  • e2e-vue : Starts the E2E tests for the Vue project
  • e2e-solid : Starts the E2E tests for the Solid project

Contributing new machines/features

  • generate-machine : Generates a new machine package in the packages/ directory. It sets up the required files and structure for new machine.
  • generate-util : Generates a new utility package in the packages/utilities directory.

Other commands

  • test : Run the tests for all packages
  • lint : Lint all packages

Website

  • start-website: Starts the website

Inspirations


Contributions

Looking to contribute? Look for the Good First Issue label.

🐛 Bugs

Please file an issue for bugs, missing documentation, or unexpected behavior.

💡 Feature Requests

Please file an issue to suggest new features. Vote on feature requests by adding a 👍. This helps maintainers prioritize what to work on.


License

MIT © Segun Adebayo

NPM DownloadsLast 30 Days