返回项目目录
开发工具精选项目

Headless UI

Tailwind Labs 提供的无样式、可访问 UI 组件库,为 React 与 Vue 处理键盘、焦点和复杂交互行为。

主要语言TypeScript
开源许可MIT
项目类型开发工具
维护状态稳定维护
OVERVIEW

项目概述

Headless UI 提供 Dialog、Menu、Combobox、Listbox、Popover、Tabs、Switch 等无视觉预设组件。它负责语义、状态、焦点管理和键盘交互,团队则使用 Tailwind CSS、普通 CSS 或其他方案实现自己的设计系统。

Headless UI 是 Tailwind Labs 维护的一套无样式、可访问 UI 组件,分别通过 @headlessui/react 与 @headlessui/vue 发布。它不提供按钮颜色、圆角、间距或品牌视觉,而是封装 Dialog、Dropdown Menu、Popover、Combobox、Listbox、Tabs、Disclosure、Switch 等控件最容易出错的语义、焦点、键盘导航、选择状态和关闭行为。组件通常拆分为 Button、Panel、Items、Option 等可组合 Parts,并通过 Render Prop、状态属性或 data-* Attribute 暴露交互状态,让开发者用 Tailwind CSS、CSS Module、CSS-in-JS 或普通 CSS 完成外观。当前 React 稳定版为 2.2.10,Vue 稳定版为 1.7.23。

React 版本2.2.10
Vue 版本1.7.23
许可证MIT
FEATURES

主要特点

Headless UI 聚焦复杂控件的行为与无障碍基础,让团队可以在不继承默认主题的前提下构建设计系统。

01

完全无样式

组件不附带固定颜色、尺寸、间距和主题,团队可以从零实现品牌视觉而无需覆盖第三方 CSS。

02

可访问性交互基础

为复杂控件处理 ARIA 语义、键盘导航、关闭行为、选中状态和焦点移动等基础逻辑。

03

React 与 Vue 支持

官方分别维护 @headlessui/react 和 @headlessui/vue,让两个生态都能使用相近的 Headless Component 思路。

04

可组合 Parts

DialogPanel、MenuButton、MenuItems、ComboboxInput 等子组件可自由组合 DOM 层级与内容。

05

Dialog 焦点管理

Dialog 自动使用 Portal、限制焦点留在弹层内、将外部内容设为 Inert,并处理 Escape 和点击外部关闭。

06

键盘导航

Menu、Listbox、Combobox、Tabs 和 Radio Group 等组件内置方向键、Home/End、Enter、Escape 与字符搜索行为。

07

状态暴露

通过 Render Prop 和 data-open、data-focus、data-selected、data-disabled 等属性提供可靠的样式钩子。

08

受控与非受控模式

根据组件提供 Value、DefaultValue、Open 和 OnChange 等模式,与表单或应用状态灵活集成。

09

自定义渲染元素

使用 as Prop 或 Fragment 改变底层元素,避免为了行为组件添加不需要的包装层。

10

Transition 支持

组件可暴露进入、离开和开关状态,便于使用 Tailwind Utility 或普通 CSS 编写动画。

11

表单组件

Checkbox、Combobox、Fieldset、Input、Listbox、Radio Group、Select、Switch 与 Textarea 覆盖常见输入需求。

12

Tailwind CSS 友好

官方示例直接使用 Utility Class 和 Data Variant,但 Headless UI 本身不强制安装 Tailwind CSS。

13

TypeScript 类型

官方包提供 TypeScript API 和泛型支持,有助于约束 Value、事件与自定义元素组合。

USE CASES

适用场景

它适合已经拥有视觉规范、使用 React 或 Vue,并希望避免从零实现焦点和键盘交互的产品团队。

自有设计系统

复用稳定的交互与无障碍行为,同时由 Design Token、Tailwind CSS 或 CSS Variables 决定全部视觉。

SaaS 与管理后台

快速构建 Dropdown Menu、Dialog、Combobox、Listbox、Tabs 和 Switch 等高频应用控件。

搜索与命令选择

使用 Combobox 实现自动补全、实体选择、过滤列表和远程搜索,并保留完整键盘操作。

响应式导航

组合 Disclosure、Popover、Menu 和 Transition 构建桌面与移动导航,不继承固定主题。

电商筛选与配置

通过 Listbox、Radio Group、Checkbox 和 Dialog 实现规格、排序、筛选和移动端抽屉。

品牌化营销页面

在 Accordion、Tabs、Popover 和 Modal 中使用高度定制的视觉与动画,同时保留语义和键盘行为。

React/Vue 多产品体系

在交互理念相近的前提下分别使用官方 React 与 Vue 包,但需要维护各自实现和版本差异。

EVALUATION

优点与注意事项

技术选型不仅要看能力,也要理解它带来的团队成本。

主要优点

Headless UI 擅长的地方

  • 不附带默认主题,不需要大量 CSS Override 就能匹配自有品牌。
  • 封装复杂控件的键盘、焦点、Portal 和选择行为,降低重复造轮子风险。
  • 组件 Parts 可自由组合,DOM 结构与视觉实现控制权较高。
  • 同时支持 React 与 Vue,适合 Tailwind Labs 生态中的不同前端栈。
  • Data Attribute 与 Render Prop 让交互状态可以直接映射到样式。
  • 与 Tailwind CSS 配合自然,也能使用普通 CSS、CSS Module 或 CSS-in-JS。
  • Dialog、Combobox、Menu、Listbox 等覆盖产品应用中最复杂且常见的控件。
  • 官方 TypeScript 类型改善编辑器提示和状态 Value 的约束。
  • MIT 许可证宽松,适合商业产品、内部设计系统和开源项目。

需要注意

采用前应考虑的问题

无样式意味着工作没有结束

必须自行实现布局、主题、动画、Focus Ring、Disabled、Loading、Error 和高对比度状态。

可访问性仍需产品验证

组件提供可靠基础,但错误 Label、缺失说明、错误 DOM 组合或自定义样式仍可能破坏体验。

React 与 Vue 版本不同步

React 当前为 2.x,Vue 仍为 1.x,组件集合、Props 和文档示例不能直接跨框架照搬。

组件目录相对聚焦

它不是包含 Data Grid、Date Picker、Rich Text Editor 和图表的全功能企业组件库。

自定义 as 组件需要转发能力

自定义组件必须正确转发 Ref、事件、ARIA 和其他 Props,否则 Headless UI 无法管理焦点和交互。

弹层布局仍由应用负责

Dialog 的层级、宽高、滚动容器、Backdrop 和 Z-Index 需要正确设计,尤其要测试长内容与移动键盘。

受控状态要避免重复来源

Open、Value 和 Query 等状态应明确由组件还是业务层管理,避免表单、URL 和本地 State 相互覆盖。

动画需要关注卸载时机

Transition、CSS 动画和条件渲染必须协调,否则可能出现焦点提前丢失、离场动画被截断或不可见内容仍可交互。

SSR 框架需要客户端边界

交互组件依赖状态、事件和浏览器焦点;在 Next.js 等环境中通常应放入 Client Component 边界。

维护节奏相对稳定

仓库近期更新频率不高,采用前应核对 Issues、目标框架新版本兼容性和所需组件的修复状态。

没有原生移动端支持

Headless UI 面向 Web DOM,不适用于 React Native、Flutter 或原生 iOS/Android View。

仍需自动化无障碍测试

应结合 Axe、键盘测试和真实屏幕阅读器验证业务组合,而不是只依赖组件库的默认行为。

QUICK START

快速开始

下面分别展示 React 与 Vue 的安装方式,并用 Dialog、Menu 和状态属性完成可访问交互与自定义样式。

1在 React 项目中安装
bash
npm install @headlessui/react
2创建可访问的 React Dialog
tsx
import {
  Description,
  Dialog,
  DialogBackdrop,
  DialogPanel,
  DialogTitle,
} from "@headlessui/react";
import { useState } from "react";

export function DeleteDialog() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        删除项目
      </button>

      <Dialog
        open={isOpen}
        onClose={() => setIsOpen(false)}
        className="relative z-50"
      >
        <DialogBackdrop className="fixed inset-0 bg-black/40" />

        <div className="fixed inset-0 flex items-center justify-center p-4">
          <DialogPanel className="w-full max-w-md rounded-xl bg-white p-6">
            <DialogTitle className="text-lg font-semibold">
              确认删除
            </DialogTitle>
            <Description className="mt-2 text-sm text-zinc-600">
              删除后无法恢复。
            </Description>

            <div className="mt-6 flex justify-end gap-3">
              <button onClick={() => setIsOpen(false)}>取消</button>
              <button data-autofocus>确认删除</button>
            </div>
          </DialogPanel>
        </div>
      </Dialog>
    </>
  );
}
3使用状态属性设计 Menu
tsx
import {
  Menu,
  MenuButton,
  MenuItem,
  MenuItems,
} from "@headlessui/react";

export function AccountMenu() {
  return (
    <Menu>
      <MenuButton className="rounded-md border px-3 py-2">
        账户
      </MenuButton>

      <MenuItems
        anchor="bottom end"
        className="mt-2 w-44 rounded-lg border bg-white p-1 shadow-lg"
      >
        <MenuItem>
          <a
            href="/profile"
            className="block rounded px-3 py-2 data-focus:bg-zinc-100"
          >
            个人资料
          </a>
        </MenuItem>
        <MenuItem>
          <button className="w-full rounded px-3 py-2 text-left data-focus:bg-zinc-100">
            退出登录
          </button>
        </MenuItem>
      </MenuItems>
    </Menu>
  );
}
4在 Vue 项目中安装
bash
npm install @headlessui/vue
5创建 Vue Switch
vue
<script setup lang="ts">
import { ref } from "vue";
import { Switch } from "@headlessui/vue";

const enabled = ref(false);
</script>

<template>
  <Switch
    v-model="enabled"
    class="group inline-flex h-6 w-11 items-center rounded-full bg-zinc-300 data-checked:bg-indigo-600"
  >
    <span class="sr-only">启用通知</span>
    <span
      class="size-4 translate-x-1 rounded-full bg-white transition group-data-checked:translate-x-6"
    />
  </Switch>
</template>
6执行组件质量检查
bash
npm run lint
npm run typecheck
npm test
npm run build

# 使用键盘完成全流程,并测试 VoiceOver、NVDA 或其他屏幕阅读器
# 验证缩放、移动端滚动、高对比度和 Reduced Motion

下一步:先确认项目使用 React 还是 Vue,再阅读对应版本文档;视觉样式必须覆盖默认、Hover、Focus、Active、Disabled、Open 和错误状态,并配合键盘与屏幕阅读器测试。

ALTERNATIVES

类似项目

这些站内项目覆盖无样式组件原语、源码组件方案、样式工具和 Headless UI 支持的框架;外部项目则提供其他可访问性抽象。

COMPARISON

Headless UI vs Radix UI

Headless UI 与 Radix UI 都提供无视觉预设的可访问组件,但 Headless UI 强调 React/Vue、较直接的高层组件和 Tailwind CSS 工作流;Radix Primitives 更偏 React 设计系统的低层行为原语与细粒度组合。

比较维度Headless UIRadix UI
核心定位无样式、高层可访问组件无样式、低层可组合 React 原语
框架支持官方支持 React 与 Vue官方 Primitives 主要面向 React
组件粒度常见控件 API 较直接,Parts 数量适中Parts 更细,适合高度定制的设计系统封装
视觉样式完全无样式,官方示例偏 Tailwind CSS完全无样式,可使用任意 CSS 方案
状态样式Render Prop 与 data-* Attribute丰富的 data-state、data-side 等 Attribute
弹层定位提供 Anchor 等常用定位能力Popover、Tooltip、Menu 等定位和碰撞配置更细
组件范围覆盖常用表单、Menu、Dialog、Popover 与 TabsPrimitive 数量更多,包含 Tooltip、Toast、Scroll Area 等
Tailwind 集成Tailwind Labs 维护,示例和状态 Variant 配合自然社区常与 Tailwind 和 shadcn/ui 组合
Vue 选择有官方 Vue 包,但版本线独立没有同仓库官方 Vue Primitives
生态关系常直接用于应用或自有 Design System是 shadcn/ui 等源码组件体系的重要行为基础
许可证MITMIT
更适合React/Vue 团队快速构建品牌化常用控件React 团队构建细粒度、复杂交互的设计系统
如何选择

如果团队使用 React 或 Vue,希望用较直接的组件 API 快速实现自有视觉,并与 Tailwind CSS 紧密配合,Headless UI 通常更省步骤;如果只面向 React,需要更丰富的 Primitive、精细弹层控制,或计划基于 shadcn/ui 构建设计系统,Radix UI 往往更合适。最终应以所需组件清单、框架版本、键盘交互和移动端行为制作验证原型。

VERIFICATION

资料核验

版本、维护信息与本页采用的官方资料来源。

最后核验2026 年 8 月 20 日
核验版本React 2.2.10 / Vue 1.7.23
内容维护Tailwind Labs
项目维护状态稳定维护

npm 官方元数据核验的 @headlessui/react 稳定版为 2.2.10,@headlessui/vue 稳定版为 1.7.23,两者均采用 MIT 许可证。官方文档确认组件完全无样式并聚焦可访问交互。官方仓库未归档,最近可见提交日期为 2026 年 4 月 13 日,因此标记为稳定维护。

维护状态核验2026 年 7 月 26 日 · 最近可见代码活动:2026 年 4 月 13 日

官方仓库未归档,核验时最近可见的代码活动日期为 2026 年 4 月 13 日。项目更新频率较低但仍有维护迹象,因此标记为稳定维护,而不是活跃更新。

查看官方仓库