Ark UI logo

Ark UI

基于 Zag.js 状态机的跨框架无样式可访问组件库

ReactHeadless UI状态机Zag.js

Ark UI 是 Chakra UI 团队打造的无样式组件库,基于 Zag.js 有限状态机构建 45+ 可访问组件原语,支持 React、Vue、Solid、Svelte 四框架统一的组件行为抽象。

Ark UI

基于 Zag.js 状态机的跨框架无样式可访问组件库,为 React、Vue、Solid、Svelte 提供统一的组件行为抽象

技术简介说明

Ark UI 是由 Chakra UI 团队(Segun Adebayo)打造的无样式(headless)组件库,提供 45+ 个可访问的 UI 组件原语。其核心差异化在于基于 Zag.js 有限状态机构建所有组件逻辑,使得同一套状态机代码能够跨 React、Vue、Solid、Svelte 四个框架运行,实现真正的"一次编写,处处使用"。

Ark UI 的设计哲学是将交互行为与视觉样式彻底解耦——组件只负责可访问性、键盘导航、焦点管理、状态逻辑,样式完全由开发者控制。它与 Park UI(基于 Panda CSS 的样式层)搭配使用,可以快速构建完整的设计系统。

核心价值

  • 状态机驱动:每个组件的交互逻辑由 Zag.js 有限状态机建模,行为可预测、易调试
  • 跨框架一致性:同一套状态机逻辑适配 React / Vue / Solid / Svelte,API 统一
  • 无样式设计:零样式注入,完全由开发者控制 CSS,适配任意样式方案
  • 可访问性优先:WAI-ARIA 模式内置,键盘导航、焦点管理、屏幕阅读器支持开箱即用
  • 设计系统友好:通过 data-scope / data-part / data-state 属性暴露组件结构,方便样式化

基本信息

项目详情
官网https://ark-ui.com
GitHubhttps://github.com/chakra-ui/ark
LicenseMIT
最新版本v5.37.x(2026年6月持续更新)
主要维护者Segun Adebayo、Christian Schröter、Esther Agbaje(Chakra UI 团队)
技术栈TypeScript、Zag.js(有限状态机)、React / Vue / Solid / Svelte
Stars5.3k+(GitHub)
npm 下载周下载量 630k+(@ark-ui/react
组件数量45+ 组件 + 15+ 工具函数

产品矩阵

Ark UI 生态包含以下主要产品:

  1. Ark UI:无样式跨框架组件库(核心产品)
  2. Zag.js:底层状态机引擎,独立可用
  3. Park UI:基于 Ark UI + Panda CSS 的带样式设计系统
  4. @ark-ui/mcp:Model Context Protocol 服务器,支持 AI 辅助开发

快速上手

安装

Ark UI 按框架提供独立包,每个组件按需导入:

# React
npm install @ark-ui/react
 
# Vue
npm install @ark-ui/vue
 
# Solid
npm install @ark-ui/solid
 
# Svelte
npm install @ark-ui/svelte

基础配置

Ark UI 无需特殊配置,直接导入使用。组件采用命名空间导入模式:

// React 示例
import { Dialog } from '@ark-ui/react/dialog'
import { Slider } from '@ark-ui/react/slider'
import { Tabs } from '@ark-ui/react/tabs'

最小示例

==== React ====

import { Dialog } from '@ark-ui/react/dialog'
import { Portal } from '@ark-ui/react/portal'
 
export function MyDialog() {
  return (
    <Dialog.Root>
      <Dialog.Trigger asChild>
        <button>打开对话框</button>
      </Dialog.Trigger>
 
      <Portal>
        <Dialog.Backdrop className="dialog-backdrop" />
        <Dialog.Positioner className="dialog-positioner">
          <Dialog.Content className="dialog-content">
            <Dialog.Title>标题</Dialog.Title>
            <Dialog.Description>描述内容</Dialog.Description>
 
            <Dialog.CloseTrigger asChild>
              <button>关闭</button>
            </Dialog.CloseTrigger>
          </Dialog.Content>
        </Dialog.Positioner>
      </Portal>
    </Dialog.Root>
  )
}

==== Vue ====

<script setup>
import { Dialog } from '@ark-ui/vue/dialog'
import { Portal } from '@ark-ui/vue/portal'
</script>
 
<template>
  <Dialog.Root>
    <Dialog.Trigger asChild>
      <button>打开对话框</button>
    </Dialog.Trigger>
 
    <Portal>
      <Dialog.Backdrop class="dialog-backdrop" />
      <Dialog.Positioner class="dialog-positioner">
        <Dialog.Content class="dialog-content">
          <Dialog.Title>标题</Dialog.Title>
          <Dialog.Description>描述内容</Dialog.Description>
 
          <Dialog.CloseTrigger asChild>
            <button>关闭</button>
          </Dialog.CloseTrigger>
        </Dialog.Content>
      </Dialog.Positioner>
    </Portal>
  </Dialog.Root>
</template>

==== Solid ====

import { Dialog } from '@ark-ui/solid/dialog'
import { Portal } from '@ark-ui/solid/portal'
 
export function MyDialog() {
  return (
    <Dialog.Root>
      <Dialog.Trigger asChild>
        <button>打开对话框</button>
      </Dialog.Trigger>
 
      <Portal>
        <Dialog.Backdrop class="dialog-backdrop" />
        <Dialog.Positioner class="dialog-positioner">
          <Dialog.Content class="dialog-content">
            <Dialog.Title>标题</Dialog.Title>
            <Dialog.Description>描述内容</Dialog.Description>
 
            <Dialog.CloseTrigger asChild>
              <button>关闭</button>
            </Dialog.CloseTrigger>
          </Dialog.Content>
        </Dialog.Positioner>
      </Portal>
    </Dialog.Root>
  )
}

Slider 示例(展示受控模式)

import { Slider } from '@ark-ui/react/slider'
import { useState } from 'react'
 
export function SliderDemo() {
  const [value, setValue] = useState([50])
 
  return (
    <Slider.Root
      value={value}
      onValueChange={(details) => setValue(details.value)}
      min={0}
      max={100}
    >
      <Slider.Label>音量</Slider.Label>
      <Slider.Control>
        <Slider.Track>
          <Slider.Range />
        </Slider.Track>
        <Slider.Thumb index={0} />
      </Slider.Control>
      <Slider.ValueText>{value[0]}%</Slider.ValueText>
    </Slider.Root>
  )
}

核心概念与架构

三层架构

Ark UI 采用清晰的三层架构分离关注点:

┌─────────────────────────────────────────┐
│         Ark UI(框架适配层)              │
│   React / Vue / Solid / Svelte 包装器    │
├─────────────────────────────────────────┤
│         Zag.js(状态机引擎层)            │
│   machine / context / connect / service  │
├─────────────────────────────────────────┤
│         @zag-js/core(有限状态机)         │
│   基于 XState FSM 的最小化状态机实现      │
└─────────────────────────────────────────┘

Zag.js 状态机模型

每个组件的交互逻辑被建模为有限状态机,包含三个核心概念:

  • Machine(机器):定义组件的所有状态、上下文和转换逻辑
  • Service(服务):通过 useMachine 初始化,创建运行中的状态机实例
  • Connect(连接器):将运行中的状态机转化为框架可用的 API(HTML 属性、事件处理器)
// Zag.js 状态机伪代码(以 NumberInput 为例)
import * as numberInput from '@zag-js/number-input'
 
// 1. 定义机器
const machine = numberInput.machine({
  context: { value: 0, min: 0, max: 100 },
})
 
// 2. 创建服务(框架适配层处理)
const service = useMachine(machine, { id: 'number-input' })
 
// 3. 连接为 API
const api = numberInput.connect(service, service, normalizeProps)
// api.value → 当前值
// api.increment() → 增加
// api.decrement() → 减少

状态机驱动的可预测行为

状态机确保组件在任何时刻的行为都是确定的:

[空闲] → 点击增加 → [递增中] → 动画完成 → [空闲]
[空闲] → 长按增加 → [持续递增] → 松开 → [空闲]
[空闲] → 获得焦点 → [聚焦] → 失去焦点 → [空闲]

框架适配策略

Ark UI 的跨框架实现策略:

  1. Zag.js 编写纯 JavaScript 状态机逻辑(与框架无关)
  2. 各框架适配器将状态机输出映射为框架特定的响应式原语
    • React → hooks(useStateuseEffect
    • Vue → $ref$computed
    • Solid → $state$derived
    • Svelte → $state$effect
  3. 确保 API 一致性:所有框架使用相同的组件名、属性名、事件名

组件解剖(Anatomy)

每个组件由多个语义化部分组成,通过 data-scopedata-part 属性暴露:

// Slider 组件解剖
<Slider.Root data-scope="slider">
  <Slider.Label data-part="label">音量</Slider.Label>
  <Slider.Control data-part="control">
    <Slider.Track data-part="track">
      <Slider.Range data-part="range" />
    </Slider.Track>
    <Slider.Thumb data-part="thumb" data-index="0" />
  </Slider.Control>
  <Slider.ValueText data-part="value-text">50%</Slider.ValueText>
</Slider.Root>

Controlled 与 Uncontrolled 模式

所有交互组件同时支持受控和非受控状态:

// 非受控
<Dialog.Root defaultOpen={false}>
 
// 受控
<Dialog.Root open={isOpen} onOpenChange={(details) => setIsOpen(details.open)}>

核心特性

1. 状态机驱动(State Machine Powered)

  • 每个组件的交互逻辑由 Zag.js 有限状态机建模
  • 行为可预测,状态转换显式定义
  • 复杂交互(如 Date Picker、Combobox)的逻辑清晰可维护
  • 状态机可独立测试,不依赖 DOM

2. 跨框架一致性(Framework Agnostic)

  • 唯一支持 React / Vue / Solid / Svelte 的 headless 组件库
  • 所有框架共享同一套状态机逻辑,行为完全一致
  • API 统一:组件名、属性名、事件名在所有框架中相同
  • 适合多框架团队维护统一的设计系统

3. 完全可访问(Accessible by Default)

  • WAI-ARIA 模式内置,符合 WCAG 2.1 标准
  • 完整的键盘导航支持(Tab、Enter、Space、Arrow 键)
  • 焦点管理自动处理,包括焦点陷阱和焦点恢复
  • 屏幕阅读器友好,正确的 rolearia-* 属性
  • 支持 prefers-reduced-motion 减少动画偏好

4. 真正的无样式(Truly Headless)

  • 零样式注入,不产生任何 CSS
  • 通过 data-scopedata-partdata-state 属性暴露组件结构
  • 支持任意 CSS 方案:Tailwind CSS、Panda CSS、CSS Modules、styled-components、Emotion、纯 CSS
  • asChild 属性支持组件合并,避免额外 DOM 节点
// Tailwind CSS 样式化
<Dialog.Content
  data-state="open"
  className="fixed inset-0 z-50 bg-white rounded-lg shadow-xl
    data-[state=open]:animate-in data-[state=closed]:animate-out"
>

5. 丰富的组件集合

45+ 个组件覆盖常见交互场景:

交互组件:Accordion、Dialog、Drawer、Menu、Popover、Tabs、Tooltip、Tour

数据输入:Checkbox、Radio Group、Select、Combobox、Slider、Switch、Number Input、Pin Input、Tags Input、Date Picker、Date Input、Color Picker、File Upload、Password Input、Editable

数据展示:Avatar、Carousel、Progress(线性/环形)、Rating Group、QR Code、Tree View、Listbox、Navigation Menu、Segment Group、Steps、Marquee

高级组件:Floating Panel、Splitter、Image Cropper、Signature Pad、Timer、Clipboard、Toggle Group、Angle Slider

工具函数:Focus Trap、Presence、Portal、Locale、Client Only、Highlight、JSON Tree View、Swap、Frame

6. 完善的 TypeScript 支持

  • 完整的 TypeScript 类型定义
  • 泛型组件支持类型推断
  • 所有回调函数的参数类型完善
  • IDE 智能提示友好
<Select.Root<{ items: Array<{ label: string; value: string }> }}>
  {/* 类型安全的 items 和 onValueChange */}
</Select.Root>

7. 服务端渲染(SSR)支持

  • 完全支持 Next.js、Nuxt、SolidStart、SvelteKit 等 SSR 框架
  • Portal 组件正确处理客户端 hydration
  • ClientOnly 组件处理浏览器专属逻辑
  • Nuxt 模块 ark-ui 官方支持

8. 动画与过渡支持

  • 内置 Presence 组件处理挂载/卸载动画
  • 通过 data-state 驱动 CSS 动画
  • 支持 Framer Motion、Vue Transition 等动画库
  • data-[state=open] / data-[state=closed] 状态样式
/* CSS 动画 */
[data-scope="dialog"][data-part="content"][data-state='open'] {
  animation: fadeIn 200ms ease-out;
}
[data-scope="dialog"][data-part="content"][data-state='closed'] {
  animation: fadeOut 200ms ease-in;
}

9. 国际化支持

  • Locale 工具组件支持多语言/多时区
  • Date Picker 支持区域化日期格式
  • 支持 RTL(从右到左)布局
  • FormatByteFormatTimeFormatRelativeTime 等格式化工具

10. 活跃维护与持续迭代

  • 2026 年保持每月多次发布的节奏
  • 持续新增组件(Date Input、Timer、Tour 等近期加入)
  • 社区响应积极,GitHub Issue 处理及时
  • Changesets 管理版本,发布流程规范

生态图

流程图画布 · 115%
Mermaid 流程图加载中...

核心生态

  • Zag.js:底层状态机引擎,可独立使用,提供框架无关的组件交互逻辑
  • Park UI:基于 Ark UI + Panda CSS 的完整设计系统,提供预样式化组件,对标 shadcn/ui
  • @ark-ui/mcp:Model Context Protocol 服务器,让 AI 编码助手理解 Ark UI 组件 API

配套工具

  • Panda CSS:Chakra UI 团队打造的原子化 CSS 引擎,与 Ark UI 深度集成
  • Nuxt 模块nuxt.com/modules/ark-ui 官方 Nuxt 集成模块
  • Storybook:项目内置 .storybook/ 配置,组件开发可视化调试

适用场景

1. 多框架设计系统

场景:团队同时维护 React、Vue、Solid 或 Svelte 项目,需要统一的组件行为和设计语言

优势

  • 唯一支持 4 个主流框架的 headless 组件库
  • 状态机逻辑跨框架一致,行为不会出现框架间差异
  • 减少多框架设计系统的维护成本

案例:大型企业的多技术栈产品矩阵、跨框架组件库团队

2. 自定义品牌设计系统

场景:需要构建完全定制的品牌设计系统,不允许第三方样式干扰

优势

  • 零样式注入,完全控制视觉表现
  • data-scope / data-part 属性暴露组件结构,样式化清晰
  • 搭配 Park UI 可快速获得基础样式,再按需定制

案例:中大型企业内部组件库、SaaS 产品的品牌化 UI

3. 高可访问性要求项目

场景:政府网站、教育平台、医疗系统,需符合 WCAG 2.1 AA/AAA 标准

优势

  • WAI-ARIA 模式内置,无需手动实现复杂交互模式
  • 状态机确保焦点管理、键盘导航的正确性
  • 组件行为经过充分测试,边界情况覆盖完善

案例:政府门户网站、在线学习平台、医疗信息系统、银行/金融平台

4. 复杂交互组件

场景:需要实现 Date Picker、Combobox、Color Picker、Image Cropper 等复杂交互组件

优势

  • 状态机建模使复杂交互逻辑清晰可维护
  • 处理了焦点陷阱、键盘导航、多状态同步等复杂场景
  • 避免重复实现容易出错的交互逻辑

案例:企业级应用后台、CMS 系统、数据可视化平台

5. Panda CSS 技术栈项目

场景:使用 Panda CSS 作为样式方案,需要无缝集成的组件库

优势

  • Ark UI 与 Panda CSS 同属 Chakra UI 团队,深度集成
  • Park UI 提供开箱即用的 Panda CSS 样式化组件
  • defineSlotRecipe 函数组织组件变体和部件样式

案例:使用 Panda CSS 的新项目、从 Chakra UI v2 迁移的项目

6. 服务端渲染项目

场景:Next.js、Nuxt、SolidStart 等 SSR 框架项目,需要 SEO 优化

优势

  • 完全支持 SSR,正确处理 hydration
  • ClientOnly 组件处理浏览器专属逻辑
  • Nuxt 官方模块支持

案例:电商网站、内容平台、营销落地页、文档站

7. AI 辅助开发工作流

场景:使用 AI 编码助手(Claude、Cursor 等)开发 UI 组件

优势

  • @ark-ui/mcp 提供 Model Context Protocol 支持
  • AI 助手可直接查询 Ark UI 组件 API 和用法
  • 完善的 llms.txt 文档供 AI 模型参考

案例:AI 驱动的快速原型开发、自动化组件生成

开发与工程化

项目结构

node_modules/
├── @ark-ui/
│   ├── react/          # React 组件包
│   │   ├── accordion/
│   │   ├── dialog/
│   │   ├── slider/
│   │   └── ...
│   ├── vue/            # Vue 组件包
│   ├── solid/          # Solid 组件包
│   └── svelte/         # Svelte 组件包
├── @zag-js/            # Zag.js 状态机(Ark UI 内部依赖)
│   ├── core/
│   ├── accordion/
│   ├── dialog/
│   └── ...

TypeScript 配置

{
  "compilerOptions": {
    "strict": true,
    "jsx": "react-jsx",
    "moduleResolution": "bundler"
  }
}

Ark UI 使用 TypeScript 严格模式,所有组件提供完善的类型定义。

测试策略

import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { Dialog } from '@ark-ui/react/dialog'
 
test('打开对话框', async () => {
  const user = userEvent.setup()
 
  render(
    <Dialog.Root>
      <Dialog.Trigger>打开</Dialog.Trigger>
      <Dialog.Positioner>
        <Dialog.Content>
          <Dialog.Title>标题</Dialog.Title>
          <Dialog.Description>描述内容</Dialog.Description>
        </Dialog.Content>
      </Dialog.Positioner>
    </Dialog.Root>
  )
 
  await user.click(screen.getByText('打开'))
  expect(screen.getByRole('dialog')).toBeInTheDocument()
})

样式集成

Tailwind CSS

// 使用 data 属性选择器
<Tabs.Root className="w-full">
  <Tabs.List className="flex border-b">
    <Tabs.Trigger
      value="tab1"
      className="px-4 py-2 data-[state=active]:border-b-2
        data-[state=active]:border-blue-500"
    >
      标签 1
    </Tabs.Trigger>
  </Tabs.List>
  <Tabs.Content value="tab1">内容 1</Tabs.Content>
</Tabs.Root>

Panda CSS(推荐)

import { defineSlotRecipe } from '@pandacss/dev'
 
const dialogRecipe = defineSlotRecipe({
  className: 'dialog',
  slots: ['trigger', 'backdrop', 'positioner', 'content', 'title', 'description', 'closeTrigger'],
  base: {
    backdrop: {
      bg: 'blackAlpha.600',
      backdropFilter: 'blur(4px)',
    },
    content: {
      bg: 'white',
      rounded: 'lg',
      shadow: 'xl',
      p: '6',
    },
    title: {
      fontSize: 'xl',
      fontWeight: 'bold',
    },
  },
})

纯 CSS(data 属性选择器)

/* 通过 data-scope 和 data-part 定位组件部件 */
[data-scope="slider"][data-part="track"] {
  height: 4px;
  background: #e2e8f0;
  border-radius: 2px;
}
 
[data-scope="slider"][data-part="range"] {
  background: #3b82f6;
  height: 100%;
  border-radius: 2px;
}
 
[data-scope="slider"][data-part="thumb"] {
  width: 16px;
  height: 16px;
  background: white;
  border: 2px solid #3b82f6;
  border-radius: 50%;
}
 
/* 通过 data-state 处理状态样式 */
[data-scope="dialog"][data-part="content"][data-state="open"] {
  opacity: 1;
  transform: scale(1);
}
 
[data-scope="dialog"][data-part="content"][data-state="closed"] {
  opacity: 0;
  transform: scale(0.95);
}

构建优化

  • Tree Shaking:每个组件独立子包(如 @ark-ui/react/dialog),只打包使用的组件
  • 代码分割:动态导入复杂组件(如 Date Picker、Color Picker)
  • Zag.js 共享:多个组件共享 Zag.js 状态机引擎,避免重复打包
// 动态导入复杂组件
const DatePicker = lazy(() => import('./DatePicker'))

调试技巧

// 1. 检查 data 属性了解组件结构
// DevTools 中查看 data-scope、data-part、data-state
 
// 2. 状态机调试(开发环境)
// Zag.js 支持状态机日志输出
 
// 3. 检查 ARIA 属性
// 验证 role、aria-* 属性是否正确
 
// 4. 键盘导航测试
// Tab / Shift+Tab 焦点移动
// Enter / Space 触发操作
// Arrow 键导航列表
// Escape 关闭弹出层

性能与安全

性能优化

  1. 状态机轻量化:Zag.js 基于 XState FSM 的最小化实现,只保留 UI 组件所需的状态机能力
  2. 按需加载:每个组件独立子包,支持 tree shaking
  3. 框架适配器薄封装:框架适配层极薄,开销可忽略
  4. Portal 优化:弹出层渲染到 DOM 根部,避免 z-index 和溢出问题
  5. 动画性能:使用 CSS transform/opacity,避免 layout 抖动

Bundle 体积

Ark UI 组件体积取决于框架适配器和 Zag.js 状态机。由于状态机逻辑共享,组件数量增加时边际成本递减。

组件大致 gzip 体积
Dialog~4-5 KB
Slider~3-4 KB
Tabs~3-4 KB
Select~5-6 KB
Date Picker~8-10 KB
Zag.js 核心(共享)~2-3 KB

注:Ark UI 的组件包含 Zag.js 状态机逻辑,体积略高于纯 React headless 库,但换来的是跨框架一致性和行为可预测性。

安全考虑

  1. XSS 防护:不直接注入 HTML,使用 dangerouslySetInnerHTML 需谨慎
  2. 焦点陷阱:Dialog、Drawer 等组件自动处理焦点陷阱,防止焦点逃逸
  3. 点击外部关闭:安全处理点击事件,避免误触发
  4. 键盘事件:防止键盘事件冒泡导致的意外行为
  5. Portal 安全:Portal 渲染到指定容器,避免 DOM 注入风险

可访问性测试

import { axe, toHaveNoViolations } from 'jest-axe'
 
test('无可访问性违规', async () => {
  const { container } = render(
    <Dialog.Root open>
      <Dialog.Positioner>
        <Dialog.Content>
          <Dialog.Title>标题</Dialog.Title>
        </Dialog.Content>
      </Dialog.Positioner>
    </Dialog.Root>
  )
  const results = await axe(container)
  expect(results).toHaveNoViolations()
})

技术对比

Headless 组件库横向对比

特性Ark UIRadix UIBase UIReact AriaHeadless UIAriakit
框架支持React / Vue / Solid / SvelteReactReactReactReactReact
组件数量45+30+25+40+1625+
底层技术Zag.js 状态机原生 React原生 ReactHooks原生 React原生 React
无障碍WAI-ARIA 内置WAI-ARIA 内置WAI-ARIA 内置最严格 WAI-ARIAWAI-ARIA 内置WAI-ARIA 内置
样式方案无样式无样式无样式无样式(提供 CSS 类)无样式无样式
TypeScript完善完善完善完善完善完善
SSR 支持完整完整完整完整完整完整
维护者Chakra UI 团队WorkOSMUI 团队AdobeTailwind Labs社区
Stars5.3k+15k+9.5k+15.1k+28.6k+8.6k+
周下载量630k+4.4M+3.7M+4.47M+5.49M+698k+

Ark UI vs Radix UI

特性Ark UIRadix UI
跨框架React / Vue / Solid / Svelte仅 React
底层实现Zag.js 状态机(可预测)原生 React hooks
组件数量45+(含 Date Picker、Color Picker 等高级组件)30+(缺少部分高级组件)
样式化方式data-scope / data-part 属性data-state 属性
生态Park UI(Panda CSS)shadcn/ui(Tailwind CSS)
维护状态活跃开发,每月多次发布维护放缓(WorkOS 收购后)
学习曲线中等(需理解状态机概念)中等(Compound Component 模式)
适用场景多框架设计系统React 单框架项目

Ark UI vs React Aria

特性Ark UIReact Aria
API 风格组件式(Compound Component)Hooks 式(useButtonuseDialog
预构建组件是,提供完整组件否,只提供 hooks,需自行构建
国际化基础支持(Locale 工具)深度支持(RTL、区域化日期/数字)
无障碍严格度优秀最严格(Adobe 级别)
开发效率更高(组件即插即用)较低(需更多代码组装)
适用场景快速构建设计系统对无障碍有合同级要求的企业项目

选择决策指南

如果你需要…选择
多框架统一设计系统(React + Vue + Solid + Svelte)Ark UI
React 项目 + Tailwind CSS + 成熟生态Radix UI / shadcn/ui
最严格的无障碍合规React Aria
最活跃的维护团队Base UI
Tailwind 优先 + 最简 APIHeadless UI
最小 bundle 体积Ariakit
开箱即用 + 复杂组件(Date Picker、Color Picker)Ark UI

最佳实践

1. 使用 Park UI 快速起步

对于新项目,推荐直接使用 Park UI(基于 Ark UI + Panda CSS)获得预样式化组件,再按需定制:

# Park UI 基于 Panda CSS
npm install @park-ui/react

2. 样式组织策略

// 推荐:使用 data-scope 和 data-part 组织样式
// CSS 文件按组件 scope 组织
// slider.css
[data-scope="slider"][data-part="root"] { ... }
[data-scope="slider"][data-part="track"] { ... }
[data-scope="slider"][data-part="range"] { ... }
[data-scope="slider"][data-part="thumb"] { ... }

3. z-index 管理

// 推荐:使用 CSS 变量统一管理 z-index
// 所有浮层组件共享基础 z-index,通过 --layer-index 偏移
const zIndexVars = {
  '--layer-index': '1000',  // 基础层
}
 
// 避免:直接设置过高的 z-index
// 不要:style={{ zIndex: 99999 }}
 
// Portal 组件使用 --layer-index 偏移
<Portal style={{ '--layer-index': '100' }}>

4. 组件封装

// 封装通用的确认对话框
function ConfirmDialog({
  open,
  onOpenChange,
  title,
  description,
  onConfirm,
  children,
}: {
  open: boolean
  onOpenChange: (details: { open: boolean }) => void
  title: string
  description: string
  onConfirm: () => void
  children: React.ReactNode
}) {
  return (
    <Dialog.Root open={open} onOpenChange={onOpenChange}>
      <Portal>
        <Dialog.Backdrop />
        <Dialog.Positioner>
          <Dialog.Content>
            <Dialog.Title>{title}</Dialog.Title>
            <Dialog.Description>{description}</Dialog.Description>
            <div className="flex justify-end gap-2 mt-4">
              <Dialog.CloseTrigger asChild>
                <button>取消</button>
              </Dialog.CloseTrigger>
              <button onClick={onConfirm}>{children || '确认'}</button>
            </div>
          </Dialog.Content>
        </Dialog.Positioner>
      </Portal>
    </Dialog.Root>
  )
}

5. 表单集成

import { useForm, Controller } from 'react-hook-form'
import { Select } from '@ark-ui/react/select'
 
function FormWithSelect() {
  const { control, handleSubmit } = useForm()
 
  return (
    <form onSubmit={handleSubmit(data => console.log(data))}>
      <Controller
        name="category"
        control={control}
        render={({ field }) => (
          <Select.Root
            items={[
              { label: '技术', value: 'tech' },
              { label: '设计', value: 'design' },
            ]}
            value={[field.value]}
            onValueChange={(details) => field.onChange(details.value[0])}
          >
            <Select.Label>分类</Select.Label>
            <Select.Control>
              <Select.Trigger>
                <Select.ValueText placeholder="请选择" />
              </Select.Trigger>
            </Select.Control>
            <Portal>
              <Select.Positioner>
                <Select.Content>
                  {/* items 自动渲染 */}
                </Select.Content>
              </Select.Positioner>
            </Portal>
          </Select.Root>
        )}
      />
    </form>
  )
}

6. 动画实现

import { Presence } from '@ark-ui/react/presence'
import { useState } from 'react'
 
function AnimatedDialog() {
  const [open, setOpen] = useState(false)
 
  return (
    <>
      <button onClick={() => setOpen(true)}>打开</button>
 
      <Presence present={open}>
        <div
          data-state={open ? 'open' : 'closed'}
          className="dialog-content
            data-[state=open]:animate-in data-[state=open]:fade-in
            data-[state=closed]:animate-out data-[state=closed]:fade-out"
        >
          对话框内容
          <button onClick={() => setOpen(false)}>关闭</button>
        </div>
      </Presence>
    </>
  )
}

7. 键盘导航测试清单

每个组件开发完成后,手动验证以下键盘操作:

  • Tab / Shift+Tab:焦点按预期顺序移动
  • Enter / Space:触发预期操作
  • Arrow 键:列表/菜单项正确导航
  • Escape:关闭弹出层/对话框
  • Home / End:跳转到列表首尾(如适用)

技术局限与边界

已知问题

  1. 社区规模较小:相比 Radix UI(15k+ stars)和 Headless UI(28.6k+ stars),Ark UI(5.3k+ stars)社区规模较小,Stack Overflow 问答较少
  2. React 非惯用写法:状态机驱动的组件在 React 中不是最惯用的写法,React 专属项目可能有更优选择
  3. 样式化方式学习成本data-scope / data-part 样式化方式相对不常见,新手需要适应
  4. 缺少原生 CSS 类名:不像 React Aria 提供原生 class 可直接覆写,Ark UI 依赖 data 属性选择器
  5. Zag.js 额外依赖:状态机引擎增加了额外的依赖层和理解成本

设计边界

  1. 无样式限制:需要自行实现所有样式,初始开发成本较高(搭配 Park UI 可缓解)
  2. 状态机复杂度:简单组件使用状态机可能过度设计,增加理解成本
  3. 框架适配延迟:新组件或功能在四个框架间的适配可能有时间差
  4. Panda CSS 绑定较深:官方推荐 Panda CSS 作为样式方案,非 Panda CSS 用户可能需要额外配置

何时不该使用

  • 纯 React 项目:如果只用 React 且不需要跨框架,Radix UI 或 Base UI 更合适(社区更大、生态更成熟)
  • 快速原型:需要开箱即用的样式,选择 MUI、Ant Design 或 shadcn/ui
  • 简单项目:无需复杂交互,直接使用 HTML + CSS 或轻量组件库
  • Tailwind 优先:Headless UI 与 Tailwind 集成更自然,shadcn/ui 基于 Radix + Tailwind
  • 最严格无障碍合规:React Aria 提供更严格的 WAI-ARIA 合规性
  • 最小 bundle 体积:Ariakit 的 bundle 更小

迁移风险

  • Zag.js 耦合:Ark UI 深度依赖 Zag.js,如果 Zag.js 维护出现问题,Ark UI 会受影响
  • 框架生态变化:Svelte 支持刚于 2025年6月推出,成熟度低于 React/Vue/Solid
  • Park UI 依赖 Panda CSS:如果团队选择 Tailwind CSS,需要额外的样式化工作

学习资源

官方资源

视频教程

在线 Playground

博客文章

生态项目

对比文章

中文资源

2026 年现状

版本与发布

  • 当前版本:v5.37.x(2026年6月),保持每月多次发布的活跃节奏
  • 近期重大更新
    • v5.36.0(2026年4月):新增 DateInput 组件、多触发器支持、Splitter 嵌套
    • v5.35.0(2026年3月):Drawer 滑动手势、Pin Input 重构、Toast 优先级队列
    • v5.37.0(2026年5月):浮动组件 data-side 属性、Splitter CSS 单位支持
  • Svelte 支持:2025年6月正式推出 @ark-ui/svelte,支持 Svelte 5 响应式原语

生态发展

  • Park UI 成熟:作为 shadcn/ui 的 Panda CSS 替代品,Park UI 持续增长
  • @ark-ui/mcp:推出 Model Context Protocol 服务器,支持 AI 辅助开发工作流
  • shadcn/ui 竞争:shadcn/ui 默认使用 Radix UI / Base UI,Park UI 作为 Panda CSS 生态的对标方案
  • Base UI 崛起:原 Radix 核心成员打造的 Base UI 于 2025年12月发布 v1.0,成为 Ark UI 的新竞争者

维护状态

  • Chakra UI 团队全力维护:Segun Adebayo、Christian Schröter、Esther Agbaje 持续投入
  • 开源治理规范:使用 Changesets 管理版本、Biome 统一代码风格、Renovate 自动更新依赖
  • 社区增长稳定:GitHub stars 5.3k+,npm 周下载量 630k+,社区活跃度稳步提升

推荐策略

现有项目

  • 如果已使用 Ark UI,继续使用,维护活跃且 API 稳定
  • 如果使用 Chakra UI v2,可以考虑迁移到 Ark UI + Panda CSS 架构

新项目

  • 多框架设计系统(React + Vue + Solid + Svelte):Ark UI 是唯一选择
  • 单 React 项目 + Tailwind CSS:选择 Radix UI / shadcn/ui
  • 单 React 项目 + Panda CSS:选择 Ark UI + Park UI
  • 需要复杂组件(Date Picker、Color Picker):Ark UI 组件最丰富

长期展望

Ark UI 是当前唯一真正跨框架的 headless 组件库,其状态机驱动的架构保证了行为的可预测性和跨框架一致性。尽管社区规模不及 Radix UI 和 Headless UI,但在以下领域具有独特优势:

  • 多框架设计系统的统一行为层
  • 复杂交互组件的状态管理
  • Panda CSS 生态的核心组件提供者

随着 Svelte 支持的推出和 MCP 工具的发布,Ark UI 正朝着更全面的方向发展。对于需要跨框架一致性或 Panda CSS 集成的团队,Ark UI 是不可替代的选择。


文档版本:v1.0 最后更新:2026-07-07 适用版本:Ark UI v5.37.x 参考资料