Chapter 02

安装与配置

从零跑通 init,读懂 components.json 的每个字段,把 Tailwind、路径别名、CSS 变量与暗色模式一次配到位。

2.1 三种项目起点

shadcn/ui 支持所有主流 React 框架。CLI 会自动检测你的框架并适配。

# 方式 A:Next.js(App Router)
$ npx create-next-app@latest my-app
$ cd my-app && npx shadcn@latest init

# 方式 B:Vite + React
$ npm create vite@latest my-app -- --template react-ts
$ cd my-app && npx shadcn@latest init

# 方式 C:已有项目直接 init
$ npx shadcn@latest init
Vite 需要装 Tailwind

Next.js 的 create-next-app 可选内置 Tailwind;Vite 模板不带,需先 npm i -D tailwindcss @tailwindcss/vite(v4)并在 vite.config.ts 加插件。init 时 CLI 会提示缺什么。

2.2 init 交互项详解

style(风格)
new-york 更紧凑、图标用 lucide、阴影更实;default 更宽松。选定后写入 components.json,影响后续所有 add 的组件外观。新项目推荐 new-york。
baseColor(基色)
中性灰阶:zinc / slate / stone / gray / neutral。它决定 --background、--muted 等中性变量的色相。本教程主题即 zinc。
CSS variables
选 yes 则颜色用 CSS 变量(hsl(var(--primary))),便于主题切换;选 no 则直接写死 Tailwind 颜色类。强烈建议 yes。
tailwind config 路径
你的 tailwind.config.ts(v3)或全局 CSS(v4)位置,CLI 需据此注入映射。
components 别名
组件写入目录,默认 @/components,UI 组件落在 @/components/ui
utils 别名
cn() 所在文件,默认 @/lib/utils
React Server Components
Next.js App Router 场景下标记是否 RSC 环境,影响组件是否加 "use client"

2.3 components.json 全字段

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

Tailwind v4 用 CSS 里的 @theme 取代了 JS 配置文件,所以 tailwind.config 字段为空字符串,所有配置放到 css 指向的全局 CSS 里。v3 项目则填写 config 路径。

2.4 路径别名

组件都用 @/ 引用,必须在两处一致配置:

// tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": { "@/*": ["./src/*"] }
  }
}
// vite.config.ts —— Vite 还需让打包器认识别名
import path from "path";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [react(), tailwindcss()],
  resolve: { alias: { "@": path.resolve(__dirname, "./src") } },
});

2.5 CSS 变量注入(Tailwind v4)

init 会往全局 CSS 写入令牌。v4 使用 @import "tailwindcss" + @theme inline 桥接变量:

/* src/app/globals.css */
@import "tailwindcss";
@custom-variant dark (&:is(.dark *));

:root {
  --radius: 0.625rem;
  --background: oklch(1 0 0);
  --foreground: oklch(0.145 0 0);
  --primary: oklch(0.205 0 0);
  --primary-foreground: oklch(0.985 0 0);
  --border: oklch(0.922 0 0);
}

.dark {
  --background: oklch(0.145 0 0);
  --foreground: oklch(0.985 0 0);
  --primary: oklch(0.985 0 0);
}

@theme inline {
  --color-background: var(--background);
  --color-foreground: var(--foreground);
  --color-primary: var(--primary);
  --radius-lg: var(--radius);
}

这样 bg-backgroundtext-primary 这些工具类就映射到了你的变量,改一处变量即全局换肤(第 3 章展开)。

2.6 lib/utils.ts 的 cn()

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

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

clsx 负责条件拼接(cn("p-2", isActive && "bg-primary")),twMerge 负责去重冲突(cn("p-2", "p-4")"p-4")。第 4 章会大量用它。

2.7 暗色模式基础

shadcn 的暗色靠给 <html>class="dark"。Next.js 用 next-themes 最省事:

$ npm i next-themes
// components/theme-provider.tsx
"use client";
import { ThemeProvider as NextThemesProvider } from "next-themes";

export function ThemeProvider({ children, ...props }) {
  return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}

// app/layout.tsx 里包裹
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
  {children}
</ThemeProvider>
Vite / 非 Next 项目

没有 next-themes 也能手写:document.documentElement.classList.toggle("dark"),并把偏好存 localStorage。原理都是切换 html 上的 .dark class。

2.8 验证安装

$ npx shadcn@latest add button
# 若成功打印 "Created components/ui/button.tsx" 即配置完成
// 放一个按钮测试
import { Button } from "@/components/ui/button";
export default function App() {
  return <Button>It works</Button>;
}

2.9 小结与下一步