一个交互组件(如开关组、折叠面板)往往要在 React、Vue、Solid 多个框架里重复实现一遍行为逻辑,而真正难的是键盘操作和焦点管理。每换一个框架就重写一次,既浪费又容易出无障碍 bug。
Zag 用有限状态机(XState 思路)描述组件行为,再把『适配器』接到不同框架上。你写标记和样式,Zag 保证键盘导航、焦点与 ARIA 行为在各框架间一致。

项目基本信息

信息项详情
项目名称zag
GitHub地址https://github.com/chakra-ui/zag
项目描述Build your design system in React, Solid, Vue, Svelte or Vanilla. Powered by finite state machines
作者chakra-ui
开源协议MIT License
开源状态公开
LanguagesTypeScript
支持平台Windows / macOS / Linux
最后更新2026-10-26

一、项目介绍

zag(Build your design system in React, Solid, Vue, Svelte or Vanilla. Powered by finite state machines),框架无关的 Headless 组件状态机库。它把相关能力封装成开箱即用的形态,让开发者无需从零搭建即可直接落地,下文从优势、场景到安装使用逐一展开。

二、核心优势

  • 框架无关,同一套状态机驱动 React/Vue/Solid/Svelte
  • 行为由状态机描述,复杂交互可预测、可测试
  • 内置键盘导航、焦点管理、ARIA,符合无障碍规范
  • 完全 headless,标记与样式由你掌控

三、适用场景

  • 在多个前端框架间共享同一套组件行为
  • 搭建需要高可访问性的设计系统
  • 希望交互逻辑可单测、可维护的团队

四、安装教程

以 React 的开关组为例,安装对应组件机与框架适配器:

npm install @zag-js/toggle-group @zag-js/react

常见报错:module not found @zag-js/react。记得同时安装你所用框架的适配器(如 Vue 用 @zag-js/vue,Svelte 用 @zag-js/svelte),只装核心机是不够的。

五、使用示例

把状态机连接到你的标记:

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

export function ToggleGroup() {
  const service = useMachine(toggle.machine, { id: useId() });
  const api = toggle.connect(service, normalizeProps);
  return (
    <div {...api.getRootProps()}>
      <button {...api.getItemProps({ value: "bold" })}>B</button>
      <button {...api.getItemProps({ value: "italic" })}>I</button>
    </div>
  );
}

输入:声明机器与若干按钮项。
输出:Zag 接管方向键切换、焦点环与 ARIA 属性,按钮组行为在各框架表现一致。

六、常见问题

问:Zag 和 Radix 有什么区别?

答:Radix 主要服务 React,Zag 则把行为抽象成框架无关的状态机,可跨框架复用同一份逻辑。

问:必须用 XState 吗?

答:不需要,Zag 内部封装了自己的状态机实现,你只需调用 connect 接入。

七、总结

Zag 用状态机把『组件行为』从框架里解耦出来,是做跨框架设计系统的利器,尤其适合对一致性与无障碍要求高的场景。