Zag

repository·main·Indexed 11 days ago

https://github.com/chakra-ui/zag

A collection of headless, accessible JavaScript component machines powered by state machines. Zag allows developers to write component logic once and use it across different frameworks including React, Vue, Solid, and Svelte. It provides a core finite state machine package (@zag-js/core) and a variety of specialized machine packages for UI components such as accordions, checkboxes, carousels, and cascade selects.

Tokens
414K
Snippets
1.3K
Records
1.5K
Agent score
82%

What's inside Zag

  1. What is Zag.js?

    main

    Zag is a framework-agnostic toolkit designed for implementing complex, interactive, and accessible UI components. It is built on the principles of state machines (specifically Statecharts) to manage component logic, state, and side effects reliably across different environments.

    Key characteristics include:

    • State Machine Powered: Uses statecharts to model component interactions, reducing bugs related to event coordination and state management.
    • Framework Agnostic: Component logic is modeled once and made available via adapters for React, Solid, and Vue 3.
    • Headless: The machine APIs provide logic and state without any default styling, allowing you to use any CSS or styling solution.
    • Accessibility Focused: Handles complex details like keyboard interactions, focus management, and ARIA roles/attributes out of the box.
    • Incremental Adoption: Each component machine is distributed as an individual NPM package, allowing you to install only what you need.
  2. What is @zag-js/cascade-select?

    main
    The @zag-js/cascade-select package provides the core logic for building cascade select (cascading dropdowns) components. It is implemented as a state machine designed to handle hierarchical data navigation (e.g., Continent → Country → State) with built-in support for accessibility, keyboard navigation, and various selection modes.
  3. Understand the Drawer machine package structure

    main

    The Drawer machine package is organized into specific files based on their responsibility. Use this guide to locate logic for types, state management, DOM connection, or gesture handling:

    • Types: Defined in drawer.types.ts. Contains SwipeDirection, snap point shapes, and service/API types.
    • State Machine: Defined in drawer.machine.ts. Manages states such as open, closing, closed, swiping-open, and swipe-area-dragging, along with transitions, guards, actions, and DOM subscription effects.
    • DOM Connection: Defined in drawer.connect.ts. Provides props for the DOM (CSS variables, data-* attributes, pointer handlers) and contains presentation math for translation and movement.
    • Public API: The index.ts file re-exports the machine, connect, props, types, and stack helpers.
  4. Features of @zag-js/dismissable

    main

    The @zag-js/dismissable utility provides a layering system with the following capabilities:

    • Built-in Dismiss Mechanism: Layers close when the user interacts outside the layer or when focus is programmatically moved outside the layer.
    • Focus Management: It tracks descendants to ensure focus remains within the layer, even if a previously focused descendant is removed from the DOM.
    • Nesting Support: Allows for nesting multiple layers.
    • Top-layer Dismissal: The dismiss mechanism is designed to only work for the topmost layer in a stack.
  5. Key features of the Date Picker

    main

    The @zag-js/date-picker machine provides a robust set of features for date selection:

    • Selection Modes: Supports single, multiple, and range selection.
    • Calendar View: Displays a calendar for visual date selection.
    • Date Constraints: Supports disabling specific dates and setting min/max dates.
    • Presets: Supports date range presets.
    • Localization: Works with localization, timezones, and custom calendar systems.
    • Accessibility: Provides keyboard accessibility for navigating the calendar.
    • Advanced Display: Supports week numbers and custom format/parse logic.

    Note: The date picker is built on top of the @internationalized/date library.

  6. What is prop normalization and why use `normalizeProps`?

    main

    Different frameworks have subtle differences in how they handle JSX attributes and styles. normalizeProps is a utility provided by Zag adapters to automatically convert machine-generated props into a format compatible with your specific framework.

    Examples of differences handled by normalizeProps:

    • Event Listeners:
      • React/Solid use onKeyDown.
      • Vue uses onKeydown.
    • Styles:
      • React accepts numeric values for margins: { marginBottom: 4 }.
      • Solid requires string values with units: { "margin-bottom": "4px" }.
      • Vue requires string values with units: { marginBottom: "4px" }.
  7. How the Context Menu machine works

    main

    The Context Menu machine provides accessible logic for dropdowns and context menus triggered by right-clicks or long-presses (approx. 700ms on pen/touch).

    It exports two primary functions:

    • machine: The core behavior logic.
    • connect: A function that maps the machine's behavior to JSX props and event handlers.

    To implement a context menu, you should pass a unique id to useMachine to ensure generated element IDs remain predictable. To handle the right-click trigger, use api.getContextTriggerProps() provided by the machine.

    import * as menu from "@zag-js/menu"
    
    // Inside your component:
    const service = useMachine(menu.machine, { id: 'my-menu' });
    const { api, ...props } = connect(service, menu.connect);
    
    // Apply context trigger props to your trigger element
    <div {...api.getContextTriggerProps()} />
  8. Configure Password Input security and autocompletion

    main

    The Password Input provides options for managing browser and password manager behavior:

    • Ignore password managers: Set ignorePasswordManagers: true to attempt to bypass supported managers (1Password, LastPass, Bitwarden, Dashlane, and Proton Pass). This is useful for non-login scenarios like 'secure notes' or 'confirm password' steps.
    • Autocompletion: Use the autoComplete option to specify the expected behavior:
      • new-password: For creating a new password.
      • current-password: For entering an existing password.
    // Ignore password managers
    const service = useMachine(passwordInput.machine, {
      id: useId(),
      ignorePasswordManagers: true,
    })
    
    // Manage autocompletion
    const service = useMachine(passwordInput.machine, {
      id: useId(),
      autoComplete: "new-password",
    })