字
字节笔记本
2026年5月3日
shadcn/ui 完整使用指南 - Next.js 集成
API中转
¥120
shadcn/ui 是一个基于 Radix UI 和 Tailwind CSS 的组件集合。与传统 npm 包不同,它将组件代码直接复制到项目中,提供完全的可定制性。
快速开始
1. 创建 Next.js 项目
bash
npx create-next-app@latest my-app
cd my-app2. 初始化 shadcn/ui
bash
npx shadcn@latest init初始化选项:
| 选项 | 说明 | 推荐 |
|---|---|---|
| 样式 | Default / New York | Default(简洁) |
| 基础颜色 | Slate / Gray / Zinc / Neutral / Stone | Slate(优雅) |
| CSS 变量 | 是否使用 CSS 变量 | 是(便于主题定制) |
安装组件
方法一:一次性安装所有组件(推荐)
bash
npx shadcn@latest add --all
npx shadcn@latest add -a # 简写形式
npx shadcn@latest add --all -y # 跳过确认提示方法二:按需安装
bash
npx shadcn@latest add button # 安装单个组件
npx shadcn@latest add button card input label # 安装多个组件方法三:使用 Bun
bash
bunx shadcn@latest add --all项目结构
text
my-app/
├── app/
│ ├── layout.tsx # 根布局
│ └── page.tsx # 首页
├── components/
│ └── ui/ # shadcn 组件存放位置
│ ├── button.tsx
│ ├── card.tsx
│ ├── input.tsx
│ └── ...
├── lib/
│ └── utils.ts # 工具函数(cn 函数)
├── styles/
│ └── globals.css # 全局样式(主题变量)
├── components.json # shadcn 配置
├── tailwind.config.ts # Tailwind 配置
└── tsconfig.json使用示例
基础用法
typescript
import { Button } from "@/components/ui/button"
import {
Card, CardContent, CardDescription, CardHeader, CardTitle
} from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
export default function Home() {
return (
<div className="container mx-auto p-8">
<Card className="max-w-md mx-auto">
<CardHeader>
<CardTitle>登录表单</CardTitle>
<CardDescription>使用 shadcn/ui 组件</CardDescription>
</CardHeader>
<CardContent>
<form className="space-y-4">
<div className="space-y-2">
<Label htmlFor="email">邮箱</Label>
<Input id="email" type="email" placeholder="输入邮箱" />
</div>
<div className="space-y-2">
<Label htmlFor="password">密码</Label>
<Input id="password" type="password" placeholder="输入密码" />
</div>
<Button className="w-full">登录</Button>
</form>
</CardContent>
</Card>
</div>
)
}客户端组件示例
typescript
"use client"
import { useState } from "react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { useToast } from "@/hooks/use-toast"
export function DemoForm() {
const [email, setEmail] = useState("")
const { toast } = useToast()
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
toast({ title: "提交成功", description: `邮箱:${email}` })
}
return (
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="email">邮箱</Label>
<Input id="email" type="email" value={email} onChange={(e) => setEmail(e.target.value)} required />
</div>
<Button type="submit">提交</Button>
</form>
)
}常用 CLI 命令
bash
npx shadcn@latest add # 查看可用组件
npx shadcn@latest view button # 查看某个组件的预览
npx shadcn@latest view button card dialog # 查看多个组件
npx shadcn@latest update # 更新组件
npx shadcn@latest add button --overwrite # 覆盖已存在的文件
npx shadcn@latest list # 查看已安装组件主题定制
修改主题颜色
通过 styles/globals.css 中的 CSS 变量自定义主题颜色,支持亮色和暗色模式。
切换深色模式
typescript
"use client"
import { useTheme } from "next-themes"
import { Button } from "@/components/ui/button"
export function ThemeToggle() {
const { theme, setTheme } = useTheme()
return (
<Button onClick={() => setTheme(theme === "dark" ? "light" : "dark")}>
切换主题
</Button>
)
}常用组件示例
Button
typescript
import { Button } from "@/components/ui/button"
<Button>默认按钮</Button>
<Button variant="destructive">危险按钮</Button>
<Button variant="outline">轮廓按钮</Button>
<Button variant="secondary">次要按钮</Button>
<Button variant="ghost">幽灵按钮</Button>
<Button variant="link">链接按钮</Button>
<Button size="sm">小按钮</Button>
<Button size="lg">大按钮</Button>Card
typescript
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "@/components/ui/card"
<Card>
<CardHeader>
<CardTitle>卡片标题</CardTitle>
<CardDescription>卡片描述</CardDescription>
</CardHeader>
<CardContent><p>卡片内容</p></CardContent>
<CardFooter><Button>操作</Button></CardFooter>
</Card>Dialog
typescript
import {
Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger,
} from "@/components/ui/dialog"
<Dialog>
<DialogTrigger asChild><Button>打开对话框</Button></DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>对话框标题</DialogTitle>
<DialogDescription>对话框描述</DialogDescription>
</DialogHeader>
<div>对话框内容</div>
</DialogContent>
</Dialog>Form(react-hook-form + zod)
typescript
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import * as z from "zod"
import { Button } from "@/components/ui/button"
import {
Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage,
} from "@/components/ui/form"
import { Input } from "@/components/ui/input"
const formSchema = z.object({
username: z.string().min(2, { message: "用户名至少2个字符" }),
})
export function ProfileForm() {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: { username: "" },
})
function onSubmit(values: z.infer<typeof formSchema>) {
console.log(values)
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>用户名</FormLabel>
<FormControl>
<Input placeholder="输入用户名" {...field} />
</FormControl>
<FormDescription>这是你的公开显示名称</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">提交</Button>
</form>
</Form>
)
}配置文件
components.json
json
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "app/globals.css",
"baseColor": "slate",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}lib/utils.ts
typescript
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}常见问题
Q1: 如何修改组件样式?
直接编辑 components/ui 目录下的组件文件,或使用 className prop 覆盖样式。
typescript
<Button className="bg-blue-500 hover:bg-blue-600">自定义样式</Button>Q2: 如何添加图标?
推荐使用 lucide-react:
bash
npm install lucide-reacttypescript
import { Mail } from "lucide-react"
<Button>
<Mail className="mr-2 h-4 w-4" />
发送邮件
</Button>Q3: 如何集成表单验证?
使用 react-hook-form + zod:
bash
npm install react-hook-form @hookform/resolvers zod核心优势
- 完全控制: 组件代码在你的项目中,可自由修改
- 类型安全: 完整的 TypeScript 支持
- 易于定制: 基于 Tailwind CSS 和 CSS 变量
- 现代化: 支持 Next.js 13+ App Router
- 可访问性: 基于 Radix UI,符合 WAI-ARIA 标准
相关资源
- 官网: https://ui.shadcn.com/
- GitHub: https://github.com/shadcn-ui/ui
- Radix UI: https://www.radix-ui.com/
- Tailwind CSS: https://tailwindcss.com/
分享: