1689 字
约 5 分钟
1
shadcn/ui 入门(基于 Radix UI)

shadcn/ui 入门(基于 Radix UI)

面向 VOZEB PRO 项目,讲解 shadcn/ui 是什么、如何工作、如何与 Radix UI 配合,以及在本项目中的配置与使用。


0. 什么时候使用 shadcn/ui?

适合使用 shadcn/ui 的场景:

  • ✅ 需要一个 AntD 没有或不方便的组件,且想要轻量、好定制的方案 — 比如本项目里就用到了 shadcn 的 Select
  • ✅ 希望拥有组件源码、能自由修改 — 正是 shadcn 的核心特点(复制进项目,随便改)
  • 配合 Tailwind CSS 4 使用 — shadcn 组件本体就是 Tailwind 工具类写的,和项目的样式统一
  • ✅ 需要 Radix 原语的高级交互行为 — 如无障碍(键盘操作、ARIA)完善的下拉、弹窗、气泡等
  • ✅ 想要 React Server Components(RSC) 支持 — 本项目的 components.json 里开启了 rsc: true

不建议使用(或需要权衡)的场景:

  • ⚠️ 当 Ant Design(antd)已覆盖需求时 — 本项目的整体 UI 以 antd 为主,antd 主题统一、组件齐全。同一个功能别用两套组件库各实现一遍,会增加维护负担
  • ⚠️ 需要跨组件整体风格一致的复杂后台 — antd 的主题体系更完整(表格、表单联动、国际化等),shadcn 偏"一个组件一个组件"颗粒度
  • ⚠️ 某些依赖 Radix 原生 API但项目已经用 antd 的等价物时,不要重复引入

在 VOZEB PRO 中的定位: 以 antd 为主体 UI,shadcn/ui(Tailwind + Radix)作为补充,用于项目里已有的少量组件(如 @/components/ui/select.tsx 等)。新需求优先看 antd 有没有等价组件,没有再考虑 shadcn。


1. 什么是 shadcn/ui

shadcn/ui 不是传统的组件库,而是一套可复制粘贴到你自己项目里的组件源码集合

它不通过 npm install 把打包好的组件装进 node_modules,而是把每个组件的完整源代码(TSX + Tailwind 样式 + Radix 依赖)直接写到你的 src/components/ui/ 目录里,让你拥有这些组件的完全所有权,可以随意修改。

核心哲学:复制,而不是依赖(Open source, Copy and paste)。

优势

  • 自由定制:组件源码在你项目里,想怎么改就怎么改
  • 无版本锁定:不随着上游更新被迫升级
  • 样式统一:基于 Tailwind CSS,和你的设计系统一致
  • 无障碍:底层由 Radix UI 提供完整的无障碍(Accessibility)支持

与 AntD 的区别

VOZEB PRO 主要用 Ant Design,而 shadcn/ui 是另一套组件体系:

维度 Ant Design shadcn/ui
引入方式 npm 安装后调用 复制源码到项目
定制性 通过主题变量/覆盖 直接改源码
样式方案 自带样式 Tailwind 工具类
底层 蚂蚁自研内核 Radix UI

两者可以在同一项目中共存,按场景各取所需。


2. 底层:Radix UI 是什么

Radix UI 是一套无样式(headless)的原语组件库,提供交互行为和无障碍支持,但不带任何视觉样式

常见的 Radix 原语:

  • Dropdown — 下拉菜单
  • Dialog — 弹窗
  • Popover — 气泡
  • Toast — 通知
  • Tabs — 标签页
  • Accordion — 手风琴
  • Tooltip — 提示

Radix 的职责

// Radix 只提供行为和结构(无样式)
<Dropdown.Root>
  <Dropdown.Trigger>点击</Dropdown.Trigger>
  <Dropdown.Portal>
    <Dropdown.Content>...
  </Dropdown.Portal>
</Dropdown.Root>

shadcn/ui 的职责

shadcn/ui 把 Radix 的"行为"包装上一层 Tailwind 视觉样式,成为可直接使用的完整组件:

// shadcn 组件 = 好看的样式 + Radix 行为
<DropdownMenu>
  <DropdownMenuTrigger>点击</DropdownMenuTrigger>
  <DropdownMenuContent>
    <DropdownMenuItem>选项</DropdownMenuItem>
  </DropdownMenuContent>
</DropdownMenu>

一句话总结:Radix 负责"能点、能弹、键盘能操作",shadcn/ui 负责"看起来好看"。


3. shadcn/ui 如何工作

完整的依赖链是:

shadcn/ui  (带样式的组件)
    │ 包装样式
Radix UI    (无样式原语/行为)
    │ 提供交互与无障碍
React DOM   (渲染到浏览器)

关键文件

一个 shadcn 组件通常由三部分组成:

  1. 源码 — 在 src/components/ui/<name>.tsx
  2. 工具函数 — 依赖 cn()(来自 src/lib/utils.ts)
  3. 配置文件components.json(定义别名、样式等)

4. 本项目中的配置

VOZEB PRO 的 shadcn/ui 配置在 web/components.json:

{
    "$schema": "https://ui.shadcn.com/schema.json",
    "style": "radix-nova",
    "rsc": true,
    "tsx": true,
    "tailwind": {
        "config": "",
        "css": "src/app/globals.css",
        "baseColor": "neutral",
        "cssVariables": true,
        "prefix": ""
    },
    "iconLibrary": "lucide",
    "rtl": false,
    "aliases": {
        "components": "@/components",
        "utils": "@/lib/utils",
        "ui": "@/components/ui",
        "lib": "@/lib",
        "hooks": "@/hooks"
    },
    "registries": {
        "@magicui": "https://magicui.design/r/{name}"
    }
}

几个关键字段说明:

字段 含义
style 组件风格(这里是 radix-nova)
rsc 是否支持 React Server Components
tailwind.css Tailwind 样式入口(指向 globals.css)
baseColor 基础配色(neutral)
iconLibrary 图标库(lucide)
aliases 路径别名,@/components/ui 是组件目录

依赖的工具函数

shadcn 组件依赖 cn() 来合并样式,见 web/src/lib/utils.ts:

import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
    return twMerge(clsx(inputs));
}

另外,globals.css 里通过 @import "shadcn/tailwind.css" 引入了 shadcn 所需的 Tailwind 基础样式变量。


5. 如何添加一个新组件

使用 shadcn 的 CLI(在本项目 devDependencies 中):

# 在 web/ 目录下执行
npx shadcn@latest add button
npx shadcn@latest add dialog

执行后会:

  1. 把组件源码写入 src/components/ui/button.tsx
  2. 自动安装所需依赖(如 Radix 原语)
  3. 更新 globals.css 中的必要样式变量

也可以安装多个:

npx shadcn@latest add button dialog dropdown-menu

注:本项目的 components.json 还配置了 Magic UI 注册源,可 npx shadcn@latest add @magicui/<name> 引入 Magic UI 的动效组件。


6. 在项目中使用

@/components/ui/select.tsx(本项目已有的组件)为例,使用方式:

import {
    Select,
    SelectContent,
    SelectItem,
    SelectTrigger,
    SelectValue,
} from "@/components/ui/select";

export function CitySelector() {
    return (
        <Select>
            <SelectTrigger className="w-[180px]">
                <SelectValue placeholder="选择城市" />
            </SelectTrigger>
            <SelectContent>
                <SelectItem value="bj">北京</SelectItem>
                <SelectItem value="sh">上海</SelectItem>
            </SelectContent>
        </Select>
    );
}

配合 cn() 做条件样式

import { cn } from "@/lib/utils";

<div className={cn("p-4", disabled && "opacity-50 pointer-events-none")}>...</div>


7. shadcn/ui + Tailwind 4 的样式变量

shadcn/ui 使用 CSS 变量来定义设计令牌(design tokens),在 globals.css 中通过 Tailwind CSS 4 的 @theme 定义,例如:

@import "tailwindcss";
@import "tw-animate-css";
@import "shadcn/tailwind.css";

@theme inline {
    --color-background: var(--background);
    --color-foreground: var(--foreground);
    --color-primary: var(--primary);
    /* ... */
}

这样在组件里就能用语义化颜色类:

<button class="bg-primary text-primary-foreground">按钮</button>

暗色模式则通过 dark: 变体切换变量:

<div class="bg-background text-foreground dark:bg-background dark:text-foreground"></div>


8. 参考链接

  • shadcn/ui 官方文档:https://ui.shadcn.com/docs
  • Radix UI 原语:https://www.radix-ui.com/primitives
  • Magic UI:https://magicui.design
  • Tailwind CSS:https://tailwindcss.com/docs
shadcn/ui 入门(基于 Radix UI)
http://www.clxhxhhr.top/posts/470/
作者
clxstart
发布于
2026-09-07
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。