起因
React UI 库选择困难:
- Material UI:很全 + 重 + 偏 google look
- Chakra UI:现代 + 灵活但 bundle 大
- Ant Design:业务向,复杂表单强
- Radix UI:unstyled headless
- Tailwind UI:付费 component
每个都是"npm install 进项目,按 props 用"。
痛点:
- 改 design 难(要覆盖默认 style)
- 升级 lib 版本可能 break style
- bundle 总是吃满(你只用 button 但 import 全 lib)
shadcn/ui 提出不同模式:copy component code 进项目。
不是 library,是 component template + Radix 底层。
加 component
npx shadcn@latest init
# 配置 tailwind / 颜色主题
npx shadcn@latest add button
# 把 Button 源码 copy 到 components/ui/button.tsx
import { Button } from '@/components/ui/button';
<Button variant="default">Click</Button>
<Button variant="destructive">Delete</Button>
<Button variant="outline" size="sm">Cancel</Button>
UI 跟 Tailwind UI 类似(基于 Tailwind class)。
改 component 是 yours
npx shadcn@latest add card
文件 components/ui/card.tsx 现在是你的。
要改 padding / 圆角 / 加 prop → 直接改源码。
没 lib 升级问题(没 lib)。
模式核心
Radix UI (headless, accessible)
+
Tailwind CSS (styling)
+
你 own 的 code
=
shadcn/ui
- Radix 提供 a11y / behavior(focus trap / aria 等)
- Tailwind 提供 styling
- 你拥有源码 + 修改自由
优势
- 零 lock-in:lib 没了你 code 不挂
- 改 design 直接改 file
- bundle 只含你用的 component(tree-shake 友好)
- TS first-class
- 跟现代 stack(Next.js / Vite / Astro)天然 fit
劣势
- 不是 install 即用(每 component 要 add + 看 code)
- 升级要手动(lib 出新版要 copy 新 source)
- 设计语言比 Material 单调(neutral 风格,要自己丰富)
完整 setup
# Next.js + Tailwind 项目
npx shadcn@latest init
# 装常用
npx shadcn@latest add button card input label dialog dropdown-menu sheet
生成:
components/ui/
button.tsx
card.tsx
dialog.tsx
...
lib/utils.ts # cn() helper
cn() 是 clsx + tailwind-merge 包装,让 className 智能合并:
<Button className={cn('w-full', isLoading && 'opacity-50', className)}>
form 模式
shadcn + react-hook-form + zod:
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { Button } from '@/components/ui/button';
import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from '@/components/ui/form';
import { Input } from '@/components/ui/input';
const schema = z.object({
email: z.string().email(),
});
function MyForm() {
const form = useForm({ resolver: zodResolver(schema) });
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(console.log)}>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
);
}
template 嗦但极灵活 + a11y 完整 + validation 自动。
theme
globals.css:
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
/* ... */
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
/* ... */
}
}
CSS variable 驱动主题。dark mode 加 class="dark" on root。
改 brand color:改 --primary。
sonner / vaul 等
shadcn 推荐组合的几个:
- sonner:toast notification
- vaul:bottom sheet (mobile-style drawer)
- cmdk:command palette
- react-day-picker:date picker
都是高质量 headless lib,shadcn add 帮你 scaffold 集成。
v0 / 自动生成 UI
v0.dev(Vercel)用 LLM 生成 shadcn/ui-based React component:
prompt: "user profile card with avatar, name, bio, and follow button"
→ 生成 component code 用 shadcn pieces
新 component prototype 1 分钟出。
之后人 review + 调整。
与 Material / Chakra 对比
| shadcn | Material UI | Chakra | |
|---|---|---|---|
| 装 | copy code | npm install | npm install |
| 改 design | 改 code | override theme / sx | override theme |
| 学习曲线 | 中(要懂 Tailwind) | 高(API 大) | 中 |
| bundle | tree-shake 极好 | 大 | 中 |
| 设计语言 | minimal neutral | google material | 现代 |
| 适合 | custom design / 中型项目 | 企业产品 | 通用 |
我 2024+ 项目 100% shadcn。
老 Material 项目维持。
跟 Tailwind UI 对比
Tailwind UI(付费):
- 设计精良,付费许可
- 复制 HTML 进项目(不分 component)
shadcn:
- 免费
- React component(不只是 HTML)
- 跟 Radix 集成(a11y)
如果钱不是问题且要顶级设计 → Tailwind UI 块 + shadcn 组件 混用。
何时不用
- 不用 React → shadcn 是 React-only(但 Svelte/Vue 有 community port)
- 不用 Tailwind → 不适合(核心是 Tailwind class)
- 极简项目(landing page)→ Tailwind 直接写 HTML 够
真实 case
新项目 admin dashboard:
- 1 周 setup shadcn + 10+ component
- 0 设计稿,直接 v0 生成 + 微调
- bundle 200 KB(vs Material 类似项目 600 KB+)
- 想改 button radius 全局 → 改
--radiusCSS var
迭代速度 + 灵活性大幅提升。
踩过的坑
-
不识别相对路径:shadcn 默认用
@/components/ui/...alias。
要 tsconfig + vite config 配 alias。 -
冲突 className:
<Button className="w-full bg-red-500">跟内
建 variant 冲突。cn()+ tailwind-merge 解决。 -
暗色模式切换闪烁:SSR 时 server 不知道用户 prefer。Next.js
用 next-themes 处理 hydration。 -
lib 更新没拉新:shadcn 出新 version Button → 你的没自动升。
手动npx shadcn add button --overwrite或者 diff merge。 -
设计简陋:default 风格中性 → 看起来朴素。要加品牌色 / 图标 /
插图才"鲜活"。
登录后参与评论。