为什么开发者在 React 项目中转向 shadcn/ui

如果你一直在使用传统的 UI 库(如 Material-UI 或 Chakra UI)构建 React 应用程序,你可能遇到过同样的困扰:与主题覆盖作斗争、处理臃肿的打包体积,以及难以自定义不完全符合设计要求的组件。越来越多的开发者在 shadcn/ui 中找到了解决方案——这是一种根本不同的 React 组件方法,正在改变我们对 UI 库的思考方式。
本文解释了为什么 shadcn/ui React 采用率正在加速增长,其基于 CLI 的组件模型如何工作,以及何时适合你的项目。我们将直接与传统库进行比较,并探讨其优势和权衡。
核心要点
- shadcn/ui 将组件源代码复制到你的项目中,而不是作为依赖项安装
- 组件基于 Radix UI 原语构建以确保可访问性,使用 Tailwind CSS 进行样式设计
- 这种方法提供完全的自定义控制并消除供应商锁定
- 最适合自定义设计系统、复杂表单和仪表板应用程序
- 需要 Tailwind CSS 专业知识和手动组件维护
- 权衡包括设置复杂性和相比成熟库有限的组件生态系统
shadcn/ui 与传统 React 库的不同之处
与你作为 npm 包安装的传统 UI 库不同,shadcn/ui 基于一个根本不同的原则运作:代码所有权。你不是从 node_modules
导入预编译的组件,而是将实际的组件源代码直接复制到你的项目中。
以下是两种方法的区别:
传统库方法:
npm install @mui/material
import { Button } from '@mui/material'
shadcn/ui 方法:
npx shadcn-ui@latest add button
import { Button } from "@/components/ui/button"
关键区别?使用 shadcn/ui,Button
组件的源代码现在存在于你的 components/ui/
目录中,而不是在 node_modules
中。你完全拥有它。
基于 CLI 的脚手架模型解释
shadcn/ui CLI 是这个系统的核心。当你运行 npx shadcn-ui@latest add button
时,它会:
- 下载 Button 组件的 TypeScript 源代码
- 将其放置在你指定的组件目录中
- 包含所有必要的依赖项和工具
- 配置适当的 TypeScript 类型
这不仅仅是复制粘贴——这是智能脚手架。CLI 处理:
- 依赖解析:自动安装所需的包,如用于样式变体的
class-variance-authority
- 配置管理:遵循你项目的 Tailwind CSS 设置和 TypeScript 配置
- 文件组织:在项目间创建一致的文件夹结构
# 在你的项目中初始化 shadcn/ui
npx shadcn-ui@latest init
# 根据需要添加单个组件
npx shadcn-ui@latest add button
npx shadcn-ui@latest add card
npx shadcn-ui@latest add form
技术基础:Radix UI、Tailwind CSS 和 TypeScript
shadcn/ui React 组件之所以如此出色,归结于它们的技术基础。每个组件都建立在三个支柱之上:
用于可访问性的 Radix UI 原语
Radix UI 提供无样式、可访问的组件原语。这意味着 shadcn/ui 组件继承了:
- 键盘导航:Tab、方向键和 escape 键处理
- 屏幕阅读器支持:适当的 ARIA 属性和语义 HTML
- 焦点管理:逻辑焦点流和适当的焦点捕获
// shadcn/ui Dialog 组件使用 Radix Dialog 原语
import * as DialogPrimitive from "@radix-ui/react-dialog"
const Dialog = DialogPrimitive.Root
const DialogTrigger = DialogPrimitive.Trigger
// 通过 Tailwind CSS 添加样式
用于样式的 Tailwind CSS
每个 shadcn/ui 组件都专门使用 Tailwind CSS 工具类。这提供了:
- 一致的设计令牌:颜色、间距和排版遵循你的 Tailwind 配置
- 响应式设计:内置响应式修饰符无缝工作
- 轻松自定义:直接编辑 Tailwind 类来更改样式
// 带有 Tailwind 样式的 Button 组件
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
outline: "border border-input bg-background hover:bg-accent",
},
},
}
)
用于开发者体验的 TypeScript
所有组件都包含完整的 TypeScript 定义,提供:
- 类型安全:在编译时捕获属性错误
- IntelliSense:组件属性和变体的自动完成
- 重构支持:安全的重命名和重构
shadcn/ui vs 传统 UI 库:技术比较
让我们在关键维度上检查 shadcn/ui 与成熟库的比较:
开发者控制和自定义
shadcn/ui:
- 完全的源代码访问权限
- 直接修改组件文件
- 没有主题提供者复杂性
- Tailwind 原生自定义
Material-UI/Chakra UI:
- 主题覆盖系统
- CSS-in-JS 抽象
- 有限的组件内部访问
- 复杂的自定义 API
打包体积和性能
shadcn/ui:
- 只包含你实际使用的组件
- 没有运行时主题处理
- 最小的 JavaScript 开销
- 默认更好的 tree-shaking
传统库:
- 经常包含未使用的组件
- 运行时主题计算
- 更大的 JavaScript 包
- Tree-shaking 效果因库而异
供应商锁定风险
shadcn/ui:
- 组件成为你代码库的一部分
- 不依赖于外部包更新
- 采用后消除迁移风险
传统库:
- 依赖于包维护者的决策
- 主要版本中的破坏性更改
- 迁移复杂性随时间增加
shadcn/ui 表现出色的实际用例
自定义设计系统
在构建独特设计系统时,shadcn/ui 提供了完美的起点。你可以:
- 修改组件变体以匹配品牌指南
- 添加自定义属性和行为
- 在应用程序中保持一致性
- 在自己的仓库中记录更改
// 为你的设计系统自定义 Button 变体
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium",
{
variants: {
variant: {
// 你的自定义品牌变体
brand: "bg-gradient-to-r from-purple-600 to-blue-600 text-white hover:from-purple-700 hover:to-blue-700",
},
},
}
)
表单密集型应用程序
对于具有复杂表单的应用程序,shadcn/ui 的表单组件与 React Hook Form 和 Zod 无缝集成:
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import * as z from "zod"
import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from "@/components/ui/form"
import { Input } from "@/components/ui/input"
import { Button } from "@/components/ui/button"
const formSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
})
export function LoginForm() {
const form = useForm({
resolver: zodResolver(formSchema),
})
const onSubmit = (values) => {
console.log(values)
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>邮箱</FormLabel>
<FormControl>
<Input type="email" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">登录</Button>
</form>
</Form>
)
}
仪表板和管理界面
仪表板应用程序受益于 shadcn/ui 的数据显示组件:
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { Badge } from "@/components/ui/badge"
export function UserDashboard({ users }) {
return (
<div className="space-y-6">
<div className="grid gap-4 md:grid-cols-3">
<Card>
<CardHeader>
<CardTitle>总用户数</CardTitle>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{users.length}</div>
</CardContent>
</Card>
</div>
<Table>
<TableHeader>
<TableRow>
<TableHead>姓名</TableHead>
<TableHead>状态</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{users.map((user) => (
<TableRow key={user.id}>
<TableCell>{user.name}</TableCell>
<TableCell>
<Badge variant={user.active ? "default" : "secondary"}>
{user.active ? "活跃" : "非活跃"}
</Badge>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
)
}
权衡和考虑因素
维护开销
代码所有权伴随着责任。你需要:
- 手动更新组件:没有来自包管理器的自动更新
- 处理安全补丁:监控像 Radix UI 这样的依赖项的更新
- 保持一致性:确保组件间的更改保持连贯
需要 Tailwind CSS 专业知识
shadcn/ui 假设你熟悉 Tailwind CSS。团队需要:
- 理解工具优先的 CSS 原则
- 了解 Tailwind 的响应式和状态修饰符
- 熟悉自定义 Tailwind 配置
初始设置复杂性
入门需要比传统库更多的配置:
- Tailwind CSS 设置和配置
- 路径别名的 TypeScript 配置
- 理解组件架构
有限的组件生态系统
与成熟的库相比,shadcn/ui 提供:
- 更少的预构建组件
- 更少的社区贡献组件
- 需要从头构建复杂组件
在你的 React 项目中开始使用 shadcn/ui
这里是一个实用的设置指南:
# 创建一个带有 TypeScript 和 Tailwind 的新 Next.js 项目
npx create-next-app@latest my-app --typescript --tailwind --eslint
# 导航到你的项目
cd my-app
# 初始化 shadcn/ui
npx shadcn-ui@latest init
# 添加你的第一个组件
npx shadcn-ui@latest add button card form
初始化过程将:
- 配置你的
tailwind.config.js
- 添加必要的 CSS 变量
- 设置组件路径别名
- 创建基本文件夹结构
结论
shadcn/ui 代表了 React UI 开发中向开发者赋权的转变。通过提供源代码所有权、可访问性优先的组件和 Tailwind CSS 集成,它解决了传统 UI 库的许多痛点。这种方法特别适用于自定义设计系统、表单密集型应用程序和熟悉 Tailwind CSS 的团队。
权衡——维护开销和所需的 Tailwind 专业知识——对大多数开发团队来说是可管理的。对于需要高度自定义、长期可维护性或摆脱供应商锁定的项目,shadcn/ui 相比传统组件库提供了令人信服的优势。
今天就通过访问官方文档并遵循安装指南开始使用 shadcn/ui 构建。CLI 使得在现有 React 项目中试验单个组件变得容易,无需承诺完全迁移。
常见问题
是的,shadcn/ui 非常适合企业应用程序,特别是那些需要自定义设计系统或严格可访问性要求的应用程序。代码所有权模型实际上相比依赖外部包更新减少了长期维护风险。
你通过再次运行 CLI add 命令手动更新组件,这将显示差异。然后你可以选择接受更新或保留你的自定义。这让你完全控制何时以及如何更新。
不可以,shadcn/ui 组件专门为 Tailwind CSS 构建。样式系统与 Tailwind 工具紧密集成。如果你偏好其他 CSS 方法,你需要完全重写组件样式。
由于组件存在于你的代码库中,你的应用程序继续正常工作。你拥有代码,可以根据需要维护、修改或替换组件,而不会有任何外部依赖项破坏你的应用程序。
shadcn/ui 基于类似的可访问性原语(Radix UI)构建,但添加了使用 Tailwind CSS 的预样式组件。无头 UI 库给你更多样式灵活性,但需要更多工作来创建生产就绪的组件。shadcn/ui 提供了一个中间地带,具有良好的默认值和易于自定义。