Radix UI
无样式、可访问且高度可组合的 React UI 原语,为设计系统处理焦点、键盘和复杂交互。
项目概述
Radix Primitives 提供 Dialog、Select、Dropdown Menu、Tabs 等低层 React 组件,不预设视觉样式,却封装了 WAI-ARIA 语义、焦点管理、键盘导航与弹层定位等复杂行为。
Radix UI 的 Primitives 是采用 MIT 许可证的低层 React 组件库,目标是帮助团队构建高质量、可访问的应用和设计系统。它把 Dialog、Popover、Select、Dropdown Menu 等复杂控件拆成可组合的 Parts,处理 WAI-ARIA 语义、键盘导航、焦点管理、Portal 和碰撞检测,同时不提供固定视觉样式。官方推荐安装可 Tree Shaking 的统一 radix-ui 包,也允许按 Primitive 单独安装;团队可以使用普通 CSS、Tailwind CSS、CSS-in-JS 或其他方案完成自己的视觉层。
主要特点
Radix UI 专注于把难以正确实现的交互与可访问性行为沉淀为可组合原语。
WAI-ARIA 行为基础
在适用场景遵循 WAI-ARIA Authoring Practices,处理角色、属性、键盘交互与辅助技术预期。
焦点管理
Dialog、Alert Dialog、Popover 等组件提供焦点进入、循环、关闭恢复和外部交互等复杂基础行为。
完全无样式
组件不携带品牌视觉,可通过 className 配合 CSS、Tailwind、CSS Modules 或 CSS-in-JS 自由设计。
可组合 Parts
Root、Trigger、Portal、Overlay、Content、Title 等部件允许团队按实际结构组装并封装自己的组件 API。
受控与非受控模式
多数有状态 Primitive 默认支持 defaultValue 或 defaultOpen,也可以通过 value、open 和回调接入应用状态。
asChild 组合
asChild 可把 Radix 行为和属性合并到自定义元素或路由链接,避免无意义包装并保留语义化 DOM。
状态与布局数据属性
data-state、data-side、data-align 等属性把运行状态暴露给样式层,可用于状态样式和进入、退出动画。
弹层与碰撞处理
Popover、Tooltip、Menu 等 Primitive 支持 Portal、边界碰撞、对齐、偏移和可用空间变量。
一致的 TypeScript API
组件采用相似的 Parts、受控属性和事件命名,并提供完整类型,便于设计系统包装和编辑器提示。
渐进式安装
可安装 Tree Shaking 的统一 radix-ui 包,也可只安装 @radix-ui/react-dialog 等独立包。
SSR 与现代 React
Primitives 支持服务端渲染,可用于 Next.js、Remix 等 React 框架,并兼容现代 React 版本。
适用场景
团队已有视觉规范,但不希望重复实现弹层、焦点和键盘交互时,Radix Primitives 很有价值。
企业设计系统
设计系统团队可以包装稳定交互原语,统一视觉、令牌、API、文档与应用级可访问性规范。
高度品牌化的 SaaS
无需接受现成组件库的视觉语言,也不必从头实现 Select、Dialog、Menu 和 Tooltip 等复杂行为。
复杂表单和设置界面
Checkbox、Radio Group、Select、Slider、Switch、Tabs 和 Label 能组成键盘友好的输入流程。
导航与操作菜单
Dropdown Menu、Context Menu、Menubar 和 Navigation Menu 覆盖方向键、Typeahead、子菜单和焦点切换。
弹层密集的 Web 应用
Dialog、Alert Dialog、Popover、Hover Card 和 Tooltip 可统一 Portal、关闭行为与层级交互。
渐进改造现有 React 项目
可以只替换最难维护的一个控件,并沿用原有样式体系,不要求一次引入完整设计语言。
优点与注意事项
技术选型不仅要看能力,也要理解它带来的团队成本。
主要优点
Radix UI 擅长的地方
- 封装键盘导航、焦点管理和 WAI-ARIA 语义等高风险交互细节
- 完全无样式,可适配任何品牌视觉和主流 React 样式方案
- Parts 架构粒度细,便于包装成团队自己的设计系统组件
- 受控与非受控模式兼顾快速使用和复杂状态管理
- data-state 等属性让状态样式与动画不必复制组件逻辑
- 统一包可 Tree Shaking,也保留单独安装 Primitive 的灵活性
- TypeScript API、文档和组件间约定一致,学习可迁移
需要注意
采用前应考虑的问题
Dialog Overlay 默认不会自动铺满视口。定位、尺寸、滚动、层级和动画等功能性 CSS 也需要团队实现。
开发者仍需提供标签、标题、说明、错误信息和合理内容顺序,并测试组合后的完整任务流程。
Radix Primitives 不是跨框架 Web Components。Vue、Svelte 或纯 HTML 项目需要选择对应生态方案。
一个完整 Dialog 通常包含多个显式部件。产品团队应在设计系统层包装常用结构,而不是让每个页面重复组装。
作为 Child 的自定义组件必须透传 Radix 注入的 Props,并在需要时正确转发 Ref,否则事件、ARIA 或焦点行为会失效。
分别安装多个 @radix-ui 包时应同步更新,避免共享依赖重复和版本组合问题;一般优先使用统一 radix-ui 包。
弹层进入 body 后会涉及 z-index、滚动锁定、嵌套弹层、主题作用域和测试环境,应建立应用级 Overlay 规范。
onPointerDownOutside、onOpenAutoFocus 等事件允许深度控制,但取消默认行为可能破坏键盘和辅助技术预期。
颜色、排版、间距、图标、响应式规范和组件视觉状态都由团队负责;需要开箱即用外观时应评估 Radix Themes 或上层组件库。
快速开始
下面使用统一 radix-ui 包创建一个带完整焦点管理、标题和说明的 Dialog,并用 CSS 定义视觉与动画。
bashnpm install radix-uitsximport { Dialog } from "radix-ui";
import "./dialog.css";
export function ProfileDialog() {
return (
<Dialog.Root>
<Dialog.Trigger className="button">编辑资料</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="dialog-overlay" />
<Dialog.Content className="dialog-content">
<Dialog.Title className="dialog-title">
编辑资料
</Dialog.Title>
<Dialog.Description className="dialog-description">
修改姓名并保存。完成后焦点会回到打开按钮。
</Dialog.Description>
<label className="field">
<span>姓名</span>
<input name="name" autoComplete="name" />
</label>
<div className="actions">
<Dialog.Close className="button secondary">
取消
</Dialog.Close>
<button className="button" type="submit">
保存
</button>
</div>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}css.dialog-overlay {
position: fixed;
inset: 0;
background: rgb(0 0 0 / 0.5);
animation: fade-in 160ms ease-out;
}
.dialog-content {
position: fixed;
top: 50%;
left: 50%;
width: min(28rem, calc(100vw - 2rem));
max-height: calc(100vh - 2rem);
overflow: auto;
transform: translate(-50%, -50%);
border-radius: 1rem;
background: white;
padding: 1.5rem;
box-shadow: 0 24px 80px rgb(0 0 0 / 0.25);
}
.dialog-content[data-state="closed"] {
animation: fade-out 120ms ease-in;
}
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
}tsximport { useState } from "react";
import { Dialog } from "radix-ui";
export function ControlledDialog() {
const [open, setOpen] = useState(false);
async function handleSubmit(event: React.FormEvent) {
event.preventDefault();
await saveProfile();
setOpen(false);
}
return (
<Dialog.Root open={open} onOpenChange={setOpen}>
<Dialog.Trigger>编辑</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay />
<Dialog.Content>
<Dialog.Title>编辑资料</Dialog.Title>
<form onSubmit={handleSubmit}>
<button type="submit">保存</button>
</form>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}tsximport { forwardRef } from "react";
import { Dialog } from "radix-ui";
const PrimaryButton = forwardRef<
HTMLButtonElement,
React.ComponentPropsWithoutRef<"button">
>((props, ref) => (
<button {...props} ref={ref} className="primary-button" />
));
PrimaryButton.displayName = "PrimaryButton";
export function Trigger() {
return (
<Dialog.Trigger asChild>
<PrimaryButton>打开设置</PrimaryButton>
</Dialog.Trigger>
);
}tsximport { Dialog } from "radix-ui/dialog";
import { Tooltip } from "radix-ui/tooltip";
// 统一包支持子路径导入,便于明确模块边界。
export { Dialog, Tooltip };bashnpm run dev
# 发布前运行项目自己的类型、测试和构建脚本
npm run typecheck
npm test
npm run build下一步:Radix 提供的是可访问性基础而不是最终保证。必须为控件提供可理解的 Label、Title 和 Description,并在真实页面中测试键盘、屏幕阅读器、触摸、缩放和高对比度模式。
类似项目
React 是 Radix 的运行基础,shadcn/ui 则是本站中最常见的 Radix 上层源码组件方案;其他无样式原语列为外部参考。
React
用于构建 Web 和原生用户界面的组件化 JavaScript 库。
查看项目shadcn/ui
把可访问、可组合的组件源码直接加入项目,由团队完整拥有和定制的 UI 分发平台。
查看项目Motion
面向 React 和 JavaScript 的生产级动画库,统一处理弹簧、手势、布局、滚动和退出动画。
查看项目Base UI
由 MUI 团队维护的无样式 React 组件库,强调可组合 API、可访问性和低层控制。
访问官网React Aria
Adobe 提供的可访问 React Hooks 与组件体系,覆盖国际化、交互和跨设备输入。
访问官网Headless UI
Tailwind Labs 提供的无样式、可访问组件,支持 React 与 Vue。
访问官网Radix UI vs shadcn/ui
Radix Primitives 与 shadcn/ui 经常组合使用,但两者不在同一抽象层:Radix 提供无样式行为原语,shadcn/ui 则分发带 Tailwind 视觉和应用源码所有权的完整组件。
| 比较维度 | Radix UI | shadcn/ui |
|---|---|---|
| 核心定位 | 无样式、可访问的 React 行为原语 | 开放源码组件与 Registry 分发平台 |
| 安装结果 | 组件实现保留在版本化依赖包中 | 组件源码被复制到应用目录 |
| 视觉样式 | 不提供,需要团队从零设计 | 提供 Tailwind CSS 和主题变量的优质默认值 |
| 底层基础 | Radix 自己实现 Primitive 行为 | 可选择 Base UI、Radix 或 React Aria |
| 定制方式 | 组合 Parts、Props、事件与任意样式方案 | 直接修改应用中的组件结构、行为和 Tailwind 类 |
| 升级方式 | 更新 npm 依赖并处理 API 变化 | 审阅差异后手动合并或覆盖组件源码 |
| 维护责任 | 原语内部修复由 Radix 包更新提供 | 复制后的组件由应用团队直接维护 |
| 更适合 | 从零建立独特设计系统和低层组件 API | 快速获得完整组件并继续深度品牌化 |
如果团队正在构建设计系统基础层,需要完全控制视觉和上层组件 API,Radix Primitives 更适合作为行为底座;如果希望快速得到可直接用于产品的组件,并愿意维护复制后的源码,shadcn/ui 更高效。选择 shadcn/ui 时仍可以把 Radix 作为底层 Base,两者并非必须二选一。
资料核验
版本、维护信息与本页采用的官方资料来源。
本页依据 Radix Primitives 官方介绍、可访问性、样式、组合、SSR 与 Dialog 文档,以及 npm Registry 和官方源码仓库整理。统一包与各 Primitive 独立包的版本号不同,页面中的版本指 radix-ui 聚合包。
官方仓库未归档,核验时最近可见的代码活动日期为 2026 年 7 月 28 日。该状态表示项目近期仍有公开维护活动,不代表固定发布频率或长期支持承诺。