or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

connect.mdindex.mdmachine.md
tile.json

tessl/npm-zag-js--dialog

Core logic for the dialog widget implemented as a state machine

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@zag-js/dialog@1.22.x

To install, run

npx @tessl/cli install tessl/npm-zag-js--dialog@1.22.0

index.mddocs/

Zag.js Dialog

Zag.js Dialog provides a headless, framework-agnostic dialog (modal) component implemented as a finite state machine. It delivers accessible dialog functionality following WAI-ARIA authoring practices, with complete keyboard interactions, focus management, and ARIA roles/attributes handled automatically.

Package Information

  • Package Name: @zag-js/dialog
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install @zag-js/dialog

Core Imports

import { machine, connect, anatomy, props, splitProps } from "@zag-js/dialog";
import type { Api, Props, Service, ElementIds, OpenChangeDetails } from "@zag-js/dialog";
import type { FocusOutsideEvent, InteractOutsideEvent, PointerDownOutsideEvent } from "@zag-js/dialog";

For CommonJS:

const { machine, connect, anatomy, props, splitProps } = require("@zag-js/dialog");

Basic Usage

import { machine, connect } from "@zag-js/dialog";
import { normalizeProps } from "@zag-js/react"; // or your framework adapter

function MyDialog() {
  // Create machine instance
  const [state, send] = useMachine(
    machine({
      id: "dialog-1",
      onOpenChange: (details) => {
        console.log("Dialog open:", details.open);
      },
    })
  );

  // Connect machine to UI
  const api = connect(state, normalizeProps);

  return (
    <div>
      <button {...api.getTriggerProps()}>Open Dialog</button>
      
      {api.open && (
        <div {...api.getPositionerProps()}>
          <div {...api.getBackdropProps()} />
          <div {...api.getContentProps()}>
            <h2 {...api.getTitleProps()}>Dialog Title</h2>
            <p {...api.getDescriptionProps()}>Dialog content goes here</p>
            <button {...api.getCloseTriggerProps()}>Close</button>
          </div>
        </div>
      )}
    </div>
  );
}

Architecture

Zag.js Dialog is built around several key components:

  • State Machine: Core logic implemented as a finite state machine with states (open/closed), events, effects, and actions
  • Connect API: Framework-agnostic connection layer that provides prop functions for UI elements
  • Anatomy System: Structured component parts with consistent naming and behavior
  • Accessibility Layer: Built-in ARIA roles, keyboard interactions, and focus management
  • Framework Adapters: Integration with React, Vue, Solid, Svelte through separate adapter packages

Capabilities

Dialog State Machine

Core state machine that manages dialog open/closed states, handles events, and orchestrates all dialog behaviors including accessibility features.

function machine(props: Props): Machine;

interface Machine extends StateMachine<DialogSchema> {
  state: "open" | "closed";
  send: (event: MachineEvent) => void;
}

interface MachineEvent {
  type: "OPEN" | "CLOSE" | "TOGGLE" | "CONTROLLED.OPEN" | "CONTROLLED.CLOSE";
}

Dialog State Machine

API Connection

Connection layer that transforms state machine into framework-agnostic prop functions for UI elements, providing complete accessibility and interaction handling.

function connect<T extends PropTypes>(
  service: Service<DialogSchema>,
  normalize: NormalizeProps<T>
): Api<T>;

interface Api<T extends PropTypes> {
  open: boolean;
  setOpen: (open: boolean) => void;
  getTriggerProps: () => T["button"];
  getBackdropProps: () => T["element"];
  getPositionerProps: () => T["element"];
  getContentProps: () => T["element"];
  getTitleProps: () => T["element"];
  getDescriptionProps: () => T["element"];
  getCloseTriggerProps: () => T["button"];
}

API Connection Methods

Component Anatomy

Anatomical structure defining the dialog's component parts with consistent naming and CSS class generation.

interface Anatomy {
  trigger: AnatomyPart;
  backdrop: AnatomyPart;
  positioner: AnatomyPart;
  content: AnatomyPart;
  title: AnatomyPart;
  description: AnatomyPart;
  closeTrigger: AnatomyPart;
}

const anatomy: Anatomy;

Event Types

Event types for dismissible interactions that can occur outside the dialog.

type FocusOutsideEvent = CustomEvent & {
  target: Element;
  preventDefault: () => void;
};

type InteractOutsideEvent = CustomEvent & {
  target: Element;
  preventDefault: () => void;
};

type PointerDownOutsideEvent = CustomEvent & {
  target: Element;
  preventDefault: () => void;
};

Props Utilities

Utility functions for working with dialog props in framework integrations.

/**
 * Props utility for type-safe prop definitions
 */
const props: PropsDefinition<Props>;

/**
 * Split props utility for separating dialog props from other props
 * @param allProps - Object containing all props
 * @returns Tuple with [dialogProps, otherProps]
 */
const splitProps: <T extends Partial<Props>>(
  allProps: T & Record<string, any>
) => [T, Record<string, any>];

interface PropsDefinition<T> {
  /** Array of prop keys for type validation */
  keys: (keyof T)[];
  /** Type-safe prop object */
  object: T;
}

Types

Configuration Props

interface Props {
  // Element IDs
  ids?: ElementIds;
  
  // State Management
  open?: boolean;
  defaultOpen?: boolean;
  onOpenChange?: (details: OpenChangeDetails) => void;
  
  // Behavior Configuration
  modal?: boolean;
  trapFocus?: boolean;
  preventScroll?: boolean;
  restoreFocus?: boolean;
  closeOnInteractOutside?: boolean;
  closeOnEscape?: boolean;
  
  // Accessibility
  role?: "dialog" | "alertdialog";
  "aria-label"?: string;
  
  // Focus Management
  initialFocusEl?: () => MaybeElement;
  finalFocusEl?: () => MaybeElement;
  
  // Event Handlers
  onInteractOutside?: (event: InteractOutsideEvent) => void;
  onFocusOutside?: (event: FocusOutsideEvent) => void;
  onPointerDownOutside?: (event: PointerDownOutsideEvent) => void;
  onEscapeKeyDown?: (event: KeyboardEvent) => void;
  onRequestDismiss?: () => void;
  
  // Other
  dir?: "ltr" | "rtl";
  id?: string;
  getRootNode?: () => Node | ShadowRoot | Document;
  persistentElements?: () => Element[];
}

interface ElementIds {
  trigger?: string;
  positioner?: string;
  backdrop?: string;
  content?: string;
  closeTrigger?: string;
  title?: string;
  description?: string;
}

interface OpenChangeDetails {
  open: boolean;
}

type MaybeElement = Element | null | undefined;

Service Types

interface Service extends StateMachineService<DialogSchema> {
  state: State;
  send: (event: MachineEvent) => void;
  context: Context;
  prop: (key: string) => any;
  scope: Scope;
}

interface DialogSchema {
  props: Props;
  state: "open" | "closed";
  context: {
    rendered: { title: boolean; description: boolean };
  };
  guard: "isOpenControlled";
  effect: "trackDismissableElement" | "preventScroll" | "trapFocus" | "hideContentBelow";
  action: "checkRenderedElements" | "syncZIndex" | "invokeOnClose" | "invokeOnOpen" | "toggleVisibility";
  event: MachineEvent;
}