字节笔记本

2026年2月22日

shadcn/ui - 基于 Radix UI 和 Tailwind CSS 的 React 组件库

shadcn/ui 是一个开源的 React 组件库,提供美观、可访问且可定制的 UI 组件,可以直接复制粘贴到你的项目中。它基于 Radix UI 原语构建,使用 Tailwind CSS 进行样式设计,采用独特的"复制并拥有"模式,让开发者完全掌控组件代码。

核心特点

shadcn/ui 与传统组件库最大的区别在于它不通过 npm 安装依赖,而是将组件代码直接复制到你的项目中。这种方式带来以下优势:

  • 完全定制:你可以修改组件的每一个方面,没有抽象层限制
  • 代码所有权:组件成为你代码库的一部分,便于长期维护
  • 无依赖负担:不需要担心组件库版本升级带来的兼容性问题
  • TypeScript 原生支持:所有组件都使用 TypeScript 编写

CLI 命令

初始化项目

bash
# 交互式初始化
npx shadcn@latest init

# 使用默认配置初始化
npx shadcn@latest init -d

# 指定模板和主题色
npx shadcn@latest init --template next --base-color zinc

# 支持 RTL(从右到左)
npx shadcn@latest init --rtl

添加组件

bash
# 添加单个组件
npx shadcn@latest add button

# 添加多个组件
npx shadcn@latest add button card dialog

# 添加所有组件
npx shadcn@latest add --all

# 从自定义注册表添加
npx shadcn@latest add @acme/custom-button

创建新项目

bash
# 使用 Next.js 模板创建
npx shadcn@latest create my-app --template next

# 使用 Vite 模板
npx shadcn@latest create my-vite-app --template vite

# 使用 TanStack Start
npx shadcn@latest create my-start-app --template start

检查更新

bash
# 检查所有组件更新
npx shadcn@latest diff

# 检查特定组件
npx shadcn@latest diff button

组件示例

Button 组件

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

export function ButtonDemo() {
  return (
    <div className="flex flex-wrap gap-4">
      <Button variant="default">Default</Button>
      <Button variant="destructive">Destructive</Button>
      <Button variant="outline">Outline</Button>
      <Button variant="secondary">Secondary</Button>
      <Button variant="ghost">Ghost</Button>
      <Button variant="link">Link</Button>
      <Button size="sm">Small</Button>
      <Button size="lg">Large</Button>
      <Button disabled>Disabled</Button>
    </div>
  )
}

Card 组件

tsx
import {
  Card,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"
import { Button } from "@/components/ui/button"

export function CardDemo() {
  return (
    <Card className="w-[380px]">
      <CardHeader>
        <CardTitle>Create Project</CardTitle>
        <CardDescription>
          Deploy your new project in one-click.
        </CardDescription>
      </CardHeader>
      <CardContent>
        <form>
          <div className="grid w-full gap-4">
            <div className="flex flex-col space-y-1.5">
              <Label htmlFor="name">Name</Label>
              <Input id="name" placeholder="Name of your project" />
            </div>
          </div>
        </form>
      </CardContent>
      <CardFooter className="flex justify-between">
        <Button variant="outline">Cancel</Button>
        <Button>Deploy</Button>
      </CardFooter>
    </Card>
  )
}

Dialog 对话框

tsx
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"

export function DialogDemo() {
  return (
    <Dialog>
      <DialogTrigger asChild>
        <Button variant="outline">Edit Profile</Button>
      </DialogTrigger>
      <DialogContent className="sm:max-w-[425px]">
        <DialogHeader>
          <DialogTitle>Edit profile</DialogTitle>
          <DialogDescription>
            Make changes to your profile here.
          </DialogDescription>
        </DialogHeader>
        <div className="grid gap-4 py-4">
          <div className="grid grid-cols-4 items-center gap-4">
            <Label htmlFor="name" className="text-right">Name</Label>
            <Input id="name" defaultValue="Pedro Duarte" className="col-span-3" />
          </div>
        </div>
        <DialogFooter>
          <Button type="submit">Save changes</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}

表单集成

tsx
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { 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: "Username must be at least 2 characters.",
  }),
})

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>Username</FormLabel>
              <FormControl>
                <Input placeholder="johndoe" {...field} />
              </FormControl>
              <FormDescription>
                This is your public display name.
              </FormDescription>
              <FormMessage />
            </FormItem>
          )}
        />
        <Button type="submit">Submit</Button>
      </form>
    </Form>
  )
}

配置文件

components.json

json
{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "new-york",
  "tailwind": {
    "config": "tailwind.config.js",
    "css": "src/app/globals.css",
    "baseColor": "neutral",
    "cssVariables": true,
    "prefix": ""
  },
  "rsc": true,
  "tsx": true,
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils",
    "ui": "@/components/ui",
    "lib": "@/lib",
    "hooks": "@/hooks"
  },
  "iconLibrary": "lucide"
}

工具函数

typescript
// lib/utils.ts
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

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

MCP 服务器集成

shadcn CLI 包含 MCP 服务器,支持 AI 编码助手交互:

bash
# 为 Claude Code 初始化 MCP
npx shadcn@latest mcp init --client claude

# 为 Cursor 初始化
npx shadcn@latest mcp init --client cursor

# 为 VS Code 初始化
npx shadcn@latest mcp init --client vscode

自定义注册表

你可以创建和发布自己的组件注册表:

json
{
  "$schema": "https://ui.shadcn.com/schema/registry.json",
  "name": "acme",
  "homepage": "https://acme.com",
  "items": [
    {
      "name": "fancy-button",
      "type": "registry:ui",
      "description": "A fancy button with animations",
      "dependencies": ["framer-motion"],
      "files": [
        {
          "path": "components/ui/fancy-button.tsx",
          "type": "registry:ui"
        }
      ]
    }
  ]
}

构建注册表:

bash
npx shadcn@latest build ./registry.json --output ./public/r

技术栈支持

  • React 19:完全支持最新 React 特性
  • Tailwind CSS v4:支持新版本 CSS 配置
  • Next.js:原生支持 App Router 和 Server Components
  • Vite:支持 Vite 项目
  • TanStack Start:支持 TanStack Start 框架

总结

shadcn/ui 通过其独特的"复制并拥有"模式,为 React 开发者提供了灵活且强大的 UI 组件解决方案。CLI 工具简化了项目设置、组件管理和更新流程,使跨项目保持一致性变得容易。无论是直接使用 UI 原语、与 React Hook Form 集成处理复杂表单,还是通过自定义注册表扩展团队特定组件,shadcn/ui 都能满足现代 React 应用开发的需求。

分享: