# Shadcn/UI - 构建自己的组件库

shadcn/ui (opens new window) 是一套可访问、可定制的组件和代码分发平台,它把组件源代码交给项目,让开发者构建自己的组件库。

传统组件库通常以 npm 包的形式发布。我们安装依赖、导入组件,再通过组件提供的 Props 和 CSS 覆盖样式。当设计或交互超出组件库的能力时,往往只能继续包装组件、提高 CSS 选择器优先级,或者同时引入多套 API 不一致的组件库。

shadcn/ui 要解决的就是这个问题:它不把组件实现封装在 node_modules 中,而是通过 CLI 将组件源代码复制到项目。我们可以直接修改这些代码,使组件适应自己的设计系统和业务需求。

shadcn/ui 当前版本 v4.21.0

# 与 Base UI、React Aria、Radix UI 的关系

shadcn/ui 负责组件的样式、组合方式和代码分发,Base UI、React Aria、Radix UI 则提供无样式或低样式的交互原语,比如:处理键盘操作、焦点管理、ARIA 属性和弹层定位等底层行为。

Shadcn/Create 目前支持三种 Base:

Base 依赖 说明
Base UI @base-ui/react 面向 Web 应用和设计系统的无样式、可访问组件
React Aria react-aria-components Adobe 提供的可访问组件和交互原语
Radix UI radix-ui 注重开发效率、可维护性和可访问性的 UI 原语

选择哪一种 Base,决定 CLI 生成的组件使用哪套底层实现。它们不是同时安装的必选依赖,并且不是每个组件都依赖底层原语;Button 这类组件可能只需要普通 HTML 元素,Dialog、Select 等复杂组件才需要 Base 提供交互能力。

# 安装

对于新项目,可以先在 Shadcn/Create 中预览并生成完整配置;对于已经开始的项目,可以直接运行 shadcn CLI 初始化。

# Shadcn/Create

打开 Shadcn/Create (opens new window),可以实时预览 Style、Base Color、Theme、字体、图标和圆角等配置。这里的选择不只改变颜色,CLI 还会根据 Preset 生成对应的组件结构、样式和依赖。

# Style

Style 决定组件的整体视觉语言,包括间距、圆角、形状和排版。当前共有 8 种:

Style 标识符 说明
Vega vega 干净、中性,接近经典 shadcn/ui 风格
Nova nova 减少内边距和外边距
Maia maia 圆润,并使用更宽松的间距
Lyra lyra 方正、锐利,适合等宽字体
Mira mira 面向高信息密度界面的紧凑风格
Luma luma 流畅、明亮、柔和
Sera sera 强调编辑设计和字体排版
Rhea rhea 接近 Luma,但布局更加紧凑

# Base Color

Base Color 决定界面的中性色基础,例如背景、文本、边框和弱化内容的色调。当前共有 7 种:

Base Color 标识符 色调
Neutral neutral 无明显冷暖倾向的中性灰
Stone stone 偏暖的石灰色
Zinc zinc 偏冷的锌灰色
Mauve mauve 带紫色倾向的灰色
Olive olive 带橄榄绿倾向的灰色
Mist mist 带雾蓝倾向的灰色
Taupe taupe 带棕色倾向的灰色

# Theme

Theme 决定主题颜色。除上面提到的中性色之外还有 17 种彩色:

Theme Color 标识符 色调
Amber amber 琥珀色
Blue blue 蓝色
Cyan cyan 青色
Emerald emerald 翡翠绿色
Fuchsia fuchsia 紫红色
Green green 绿色
Indigo indigo 靛蓝色
Lime lime 青柠色
Orange orange 橙色
Pink pink 粉色
Purple purple 紫色
Red red 红色
Rose rose 玫瑰红色
Sky sky 天蓝色
Teal teal 蓝绿色
Violet violet 紫罗兰色
Yellow yellow 黄色

# 字体

Shadcn/Create 可以分别设置正文字体(Body)和标题字体(Heading)。标题字体可以继承正文字体,也可以单独选择;生成项目时,CLI 会安装对应字体并写入主题配置。

# Icon 库

Shadcn/Create 支持下面 5 个 Icon 库:

选择 Icon 库后,CLI 会在生成组件时使用对应的图标和依赖。

# Radius

Radius 控制组件的基础圆角。Card、Input、Button 和 Popover 等组件的圆角都会根据这个值按比例计算。

Radius 标识符
Default default 使用当前 Style 的默认圆角
None none 0
Small small 0.45rem
Medium medium 0.625rem
Large large 0.875rem

Menu 控制 Sidebar 等菜单区域的颜色、外观和选中状态:

配置项 选项 说明
Color Default 使用当前主题的默认菜单颜色
Color Inverted 使用与页面明暗关系相反的菜单颜色
Appearance Solid 使用实色背景
Appearance Translucent 使用半透明背景
Accent Subtle 使用较弱的选中和悬停效果
Accent Bold 使用更明显的选中和悬停效果

Color 和 Appearance 会组合成 defaultinverteddefault-translucentinverted-translucent 四种配置。选择 Translucent 时,Accent 会固定为 Subtle,不能选择 Bold。

# 生成项目

完成配置后,点击 Get Code,可以为新项目生成初始化命令,也可以将 Preset 应用到已有项目。

# New Project

New Project 提供以下选项:

选项 说明
Template 选择 Next.js、Vite、TanStack Start、React Router、Laravel 或 Astro
Base 选择 Base UI、React Aria 或 Radix UI
Use pointer on buttons 为可用的 Button 和 role="button" 元素添加 cursor: pointer
Create a monorepo 创建 Monorepo,仅支持部分 Template
Enable RTL support 添加从右到左(Right-to-Left)布局支持
Package Manager 选择 pnpm、npm、yarn 或 bun

以 pnpm、Next.js 和 Radix UI 为例,生成的命令结构如下:

$ pnpm dlx shadcn@latest init --preset <preset-code> --base radix --template next
1

其中 <preset-code> 是 Create 页面根据 Style、Base Color、Theme、图表颜色、字体、图标库、圆角和 Menu 等选项生成的 Preset Code。

# Existing Project

对于之前创建的 shadcn/ui 项目,可以通过 Apply Preset 修改其配置

Apply Preset 说明 CLI 参数
Full preset 应用组件、主题和字体等完整 Preset
Theme only 只应用颜色、圆角和阴影等主题 Token,不修改组件 --only theme
Fonts only 只应用正文和标题字体,不修改组件 --only font

应用完整 Preset:

$ pnpm dlx shadcn@latest apply --preset <preset-code>
1

如果只想修改主题:

$ pnpm dlx shadcn@latest apply --preset <preset-code> --only theme
1

# CLI

对于已经开始的项目,在项目根目录运行:

$ pnpm dlx shadcn@latest init
1

CLI 会检查当前框架和 Tailwind CSS 配置,通过交互式问题确定组件 Base、样式和颜色,然后完成以下工作:

  • 创建 components.json,记录样式、Tailwind CSS 文件、Base Color、图标库和路径别名。
  • 创建包含 cn 函数的工具文件。
  • 在全局 CSS 中配置主题变量。
  • 安装组件需要的依赖。

components.json 是 shadcn CLI 的配置文件。后续执行 addapplymigrate 等命令时,CLI 会通过它确定组件保存位置和生成方式。下面是其中最重要的配置:

{
  "style": "radix-nova",
  "tailwind": {
    "css": "src/app/globals.css",
    "baseColor": "neutral",
    "cssVariables": true
  },
  "iconLibrary": "lucide",
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils",
    "ui": "@/components/ui"
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# components.json

components.json 各配置项的作用如下:

配置项 类型或可选值 说明
$schema URL 指定 JSON Schema,用于校验配置和提供编辑器提示
style radix-*base-*aria-* 组件使用的 Base 和 Style,例如 radix-nova
tailwind.config 路径 Tailwind CSS 配置文件路径;Tailwind CSS v4 项目留空
tailwind.css 路径 导入 Tailwind CSS 的全局 CSS 文件路径
tailwind.baseColor neutralstonezincmauveolivemisttaupe 生成默认主题 Token 时使用的 Base Color
tailwind.cssVariables truefalse 是否使用 CSS Variables 生成语义化主题 Token
tailwind.prefix 字符串 为生成的 Tailwind CSS Utility Class 添加前缀
rsc truefalse 是否支持 React Server Components;启用后 CLI 会为客户端组件添加 use client
tsx truefalse 生成 .tsx TypeScript 组件,或 .jsx JavaScript 组件
iconLibrary lucidehugeiconstablerphosphorremixicon 生成组件时使用的 Icon 库
aliases.components 组件路径 Components 的导入路径
aliases.utils 工具类路径 cn 等 Utility Function 的导入路径
aliases.ui UI组件路径 UI 组件的导入路径和生成目录
aliases.lib 库路径 format-dategenerate-id 等通用函数的导入路径
aliases.hooks hook 路径 use-media-query 等 Hooks 的导入路径
menuColor defaultinverteddefault-translucentinverted-translucent Menu 的颜色和背景外观
menuAccent subtlebold Menu 选中和悬停状态的强调程度
rtl truefalse 是否生成支持从右到左布局的组件
registries 对象 配置带命名空间的公共或私有 Registry

# 主题

shadcn/ui 推荐使用 CSS Variables 管理主题。组件不直接依赖具体颜色,而是使用 backgroundforegroundprimarymuted 等语义化 Token:

<div className="bg-background text-foreground">
  <button className="bg-primary text-primary-foreground">保存</button>
</div>
1
2
3

全局 CSS 会分别在 :root.dark 中定义亮色与暗色主题:

:root {
  --background: oklch(1 0 0);
  --foreground: oklch(0.145 0 0);
  --primary: oklch(0.205 0 0);
  --primary-foreground: oklch(0.985 0 0);
}

.dark {
  --background: oklch(0.145 0 0);
  --foreground: oklch(0.985 0 0);
  --primary: oklch(0.922 0 0);
  --primary-foreground: oklch(0.205 0 0);
}
1
2
3
4
5
6
7
8
9
10
11
12
13

primaryprimary-foreground 是一组 Token:前者控制背景,后者控制该背景上的文字和图标。

Card、Popover、Secondary、Muted、Accent 和 Sidebar 也使用相同的命名规则。

在根元素上添加 dark class 后,组件会自动使用 .dark 中的变量。因此切换暗色模式的本质,是控制根元素的 class,而不是逐个修改组件样式。

在 Shadcn/Create 中点击 Get Code,切换到 Theme,可以预览并复制当前配置生成的 Theme CSS Variables。

创建项目时,CLI 会把这些变量写入 components.jsontailwind.css 指定的全局 CSS 文件;Vite 项目通常是 src/index.css,Next.js 项目通常是 app/globals.csssrc/app/globals.css

如果不想使用 CSS Variables,可以在初始化时添加 --no-css-variables

$ pnpm dlx shadcn@latest init --no-css-variables
1

这时 CLI 会把具体的 Tailwind CSS 颜色类写入组件。

# 组件

# 添加组件

使用 add 命令把组件及其依赖添加到项目:

# 添加一个组件
$ pnpm dlx shadcn@latest add button

# 一次添加多个组件
$ pnpm dlx shadcn@latest add button dialog input
1
2
3
4
5

生成的文件通常位于 components/uisrc/components/ui,具体位置由 components.jsonaliases.ui 决定。组件进入项目后就是普通的 TypeScript 和 React 代码,可以直接修改:

import { Button } from "@/components/ui/button";

export function SaveButton() {
  return <Button>保存</Button>;
}
1
2
3
4
5

# 查看和更新组件

本地组件可能已经被修改过,覆盖前应该先查看上游版本与本地文件的差异:

$ pnpm dlx shadcn@latest add button --diff
1

确认可以覆盖后,再使用 --overwrite 重新添加:

$ pnpm dlx shadcn@latest add button --overwrite
1

--overwrite 会覆盖本地组件代码,不会自动合并我们已经做过的修改,因此更新前最好先提交代码或保存差异。

# 删除组件

shadcn CLI 没有 remove 命令。因为组件源代码已经属于项目,删除组件时直接删除对应文件,再移除只被该组件使用的依赖即可。删除前应该先搜索组件的导入位置,避免留下失效引用。

# References