shadcn/ui
一个用于构建 UI、组件和设计系统的框架。组件通过 CLI 作为源代码添加到用户的项目中。
重要提示: 使用项目的包管理器运行所有 CLI 命令:
npx shadcn@latest、pnpm dlx shadcn@latest或bunx --bun shadcn@latest——根据项目的packageManager而定。以下示例使用npx shadcn@latest,但请替换为项目正确的运行器。
当前项目上下文
!`npx shadcn@latest info --json`
上面的 JSON 包含项目配置和已安装的组件。使用 npx shadcn@latest docs <component> 获取任何组件的文档和示例 URL。
原则
- 优先使用现有组件。 在编写自定义 UI 之前,使用
npx shadcn@latest search检查注册表。也要检查社区注册表。 - 组合,而非重造。 设置页面 = Tabs + Card + 表单控件。仪表盘 = Sidebar + Card + Chart + Table。
- 优先使用内置变体,而非自定义样式。 例如
variant="outline"、size="sm"等。 - 使用语义化颜色。 使用
bg-primary、text-muted-foreground——绝不使用像bg-blue-500这样的原始值。
关键规则
这些规则始终强制执行。每条规则都链接到一个包含错误/正确代码示例的文件。
样式与 Tailwind → styling.md
className用于布局,而非样式。 永远不要覆盖组件的颜色或排版。- 不使用
space-x-*或space-y-*。 使用flex配合gap-*。对于垂直堆叠,使用flex flex-col gap-*。 - 当宽度和高度相等时,使用
size-*。 使用size-10而非w-10 h-10。 - 使用
truncate简写。 而非overflow-hidden text-ellipsis whitespace-nowrap。 - 不要手动覆盖
dark:颜色。 使用语义化标记(bg-background、text-muted-foreground)。 - 使用
cn()处理条件类名。 不要编写手动模板字面量三元表达式。 - 不要在覆盖层组件上手动设置
z-index。 Dialog、Sheet、Popover 等组件会自行处理堆叠。
表单与输入 → forms.md
- 表单使用
FieldGroup+Field。 永远不要使用原始div配合space-y-*或grid gap-*进行表单布局。 InputGroup使用InputGroupInput/InputGroupTextarea。 永远不要在InputGroup内部使用原始Input/Textarea。- 输入框内的按钮使用
InputGroup+InputGroupAddon。 - 选项集(2-7 个选项)使用
ToggleGroup。 不要使用Button循环并手动管理激活状态。 - 对于相关复选框/单选按钮的分组,使用
FieldSet+FieldLegend。 不要使用包含标题的div。 - 字段验证使用
data-invalid+aria-invalid。 在Field上使用data-invalid,在控件上使用aria-invalid。对于禁用状态:在Field上使用data-disabled,在控件上使用disabled。
组件结构 → composition.md
- 项目总是放在其 Group 内。
SelectItem→SelectGroup。DropdownMenuItem→DropdownMenuGroup。CommandItem→CommandGroup。 - 对于自定义触发器,使用
asChild(radix) 或render(base)。 从npx shadcn@latest info检查base字段。→ base-vs-radix.md - Dialog、Sheet 和 Drawer 总是需要 Title。 为了可访问性,必须使用
DialogTitle、SheetTitle、DrawerTitle。如果视觉上隐藏,使用className="sr-only"。 - 使用完整的 Card 组合。
CardHeader/CardTitle/CardDescription/CardContent/CardFooter。不要把所有内容都放在CardContent中。 - Button 没有
isPending/isLoading。 使用Spinner+data-icon+disabled进行组合。 TabsTrigger必须放在TabsList内部。 永远不要在Tabs中直接渲染触发器。Avatar总是需要AvatarFallback。 用于图片加载失败的情况。
使用组件,而非自定义标记 → composition.md
- 在编写自定义标记之前,优先使用现有组件。 在编写带样式的
div之前,检查组件是否存在。 - 标注使用
Alert。 不要构建自定义样式的 div。 - 空状态使用
Empty。 不要构建自定义空状态标记。 - Toast 遵循项目 base。 对于 Base UI 项目,使用
toast组件中的toast。对于 Radix 和 React Aria 项目,使用sonner中的toast()。 - 使用
Separator替代<hr>或<div className="border-t">。 - 使用
Skeleton作为加载占位符。不要使用自定义的animate-pulsediv。 - 使用
Badge替代自定义样式的 span。
图标 → icons.md
Button中的图标使用data-icon。 在图标上使用data-icon="inline-start"或data-icon="inline-end"。- 不要在组件内的图标上使用尺寸类。 组件通过 CSS 处理图标大小。不要使用
size-4或w-4 h-4。 - 将图标作为对象传递,而非字符串键。 使用
icon={CheckIcon},而不是字符串查找。
聊天与消息 → chat.md
- 聊天 UI 组合聊天原语。 对话使用
MessageScroller,行使用Message,表面使用Bubble。永远不要手写气泡div或原始滚动容器。 MessageScroller拥有滚动行为。 流式跟随、锚定和跳转至最新(MessageScrollerButton)都是内置的。不要编写useStickToBottom/ResizeObserver钩子。- 附件使用
Attachment;系统注释和分隔线使用Marker。 不要使用Item卡片或Separator加标签。
CLI
- 永远不要手动解码预设代码或构建预设 URL。 使用
npx shadcn@latest preset decode <code>、preset url <code>或preset open <code>。对于项目感知的预设检测,使用npx shadcn@latest preset resolve。 - 直接通过 CLI 应用预设代码。 对于现有项目,使用
npx shadcn@latest apply <code>;在初始化时,使用npx shadcn@latest init --preset <code>。
关键模式
这些是区分正确 shadcn/ui 代码的最常见模式。对于边界情况,请参见上面链接的规则文件。
// 表单布局:使用 FieldGroup + Field,而不是 div + Label。
<FieldGroup>
<Field>
<FieldLabel htmlFor="email">电子邮件</FieldLabel>
<Input id="email" />
</Field>
</FieldGroup>
// 验证:在 Field 上使用 data-invalid,在控件上使用 aria-invalid。
<Field data-invalid>
<FieldLabel>电子邮件</FieldLabel>
<Input aria-invalid />
<FieldDescription>无效的电子邮件地址。</FieldDescription>
</Field>
// 按钮中的图标:使用 data-icon,没有尺寸类。
<Button>
<SearchIcon data-icon="inline-start" />
搜索
</Button>
// 间距:使用 gap-*,而不是 space-y-*。
<div className="flex flex-col gap-4"> // 正确
<div className="space-y-4"> // 错误
// 等宽高等:使用 size-*,而不是 w-* h-*。
<Avatar className="size-10"> // 正确
<Avatar className="w-10 h-10"> // 错误
// 状态颜色:使用 Badge 变体或语义化标记,而不是原始颜色。
<Badge variant="secondary">+20.1%</Badge> // 正确
<span className="text-emerald-600">+20.1%</span> // 错误
组件选择
| 需求 | 使用 |
|---|---|
| 按钮/操作 | Button 配合适当的 variant |
| 表单输入 | Input、Select、Combobox、Switch、Checkbox、RadioGroup、Textarea、InputOTP、Slider |
| 2-5 个选项之间的切换 | ToggleGroup + ToggleGroupItem |
| 数据展示 | Table、Card、Badge、Avatar |
| 导航 | Sidebar、NavigationMenu、Breadcrumb、Tabs、Pagination |
| 覆盖层 | Dialog (模态框)、Sheet (侧面板)、Drawer (底部面板)、AlertDialog (确认框) |
| 反馈 | toast (Base UI)、sonner (Radix/Aria)、Alert、Progress、Skeleton、Spinner |
| 命令面板 | Dialog 内部的 Command |
| 图表 | Chart (包装 Recharts) |
| 布局 | Card、Separator、Resizable、ScrollArea、Accordion、Collapsible |
| 空状态 | Empty |
| 菜单 | DropdownMenu、ContextMenu、Menubar |
| 工具提示/信息 | Tooltip、HoverCard、Popover |
| 聊天/对话 UI | MessageScroller、Message、Bubble、Attachment、Marker |
关键字段
注入的项目上下文包含以下关键字段:
aliases→ 使用实际的别名前缀进行导入(例如@/、~/),切勿硬编码。isRSC→ 当为true时,使用useState、useEffect、事件处理程序或浏览器 API 的组件需要在文件顶部添加"use client"。在建议指令时始终引用此字段。tailwindVersion→"v4"使用@theme inline块;"v3"使用tailwind.config.js。tailwindCssFile→ 定义自定义 CSS 变量的全局 CSS 文件。始终编辑此文件,切勿创建新文件。style→ 组件视觉处理(例如nova、vega)。base→ 原语库(radix或base)。影响组件 API 和可用属性。iconLibrary→ 确定图标导入。lucide使用lucide-react,tabler使用@tabler/icons-react等。切勿假定为lucide-react。resolvedPaths→ 组件、工具函数、钩子等的确切文件系统目标。framework→ 路由和文件约定(例如 Next.js App Router 与 Vite SPA)。packageManager→ 用于任何非 shadcn 依赖项安装(例如pnpm add date-fns与npm install date-fns)。preset→ 当前项目已解析的预设代码和值。当你只需要预设信息时,使用npx shadcn@latest preset resolve --json。
有关完整字段参考,请参见 cli.md — info 命令。
组件文档、示例和用法
运行 npx shadcn@latest docs <component> 获取组件文档、示例和 API 参考的 URL。获取这些 URL 以获取实际内容。
npx shadcn@latest docs button dialog select
在创建、修复、调试或使用组件时,始终先运行 npx shadcn@latest docs 并获取 URL。 这可以确保你使用的是正确的 API 和使用模式,而不是猜测。
工作流程
- 获取项目上下文 —— 已在上面注入。如果需要刷新,请再次运行
npx shadcn@latest info。 - 首先检查已安装的组件 —— 在运行
add之前,始终检查项目上下文中的components列表或列出resolvedPaths.ui目录。不要导入尚未添加的组件,也不要重新添加已安装的组件。 - 查找组件 ——
npx shadcn@latest search。 - 获取文档和示例 —— 运行
npx shadcn@latest docs <component>获取 URL,然后获取它们。使用npx shadcn@latest view浏览尚未安装的注册表项目。要预览对已安装组件的更改,请使用npx shadcn@latest add --diff。 - 安装或更新 ——
npx shadcn@latest add。在更新现有组件时,首先使用--dry-run和--diff预览更改(参见下面的更新组件)。 - 修复第三方组件中的导入 —— 从社区注册表(例如
@bundui、@magicui)添加组件后,检查添加的非 UI 文件中是否存在硬编码的导入路径,如@/components/ui/...。这些路径可能与项目的实际别名不匹配。使用npx shadcn@latest info获取正确的ui别名(例如@workspace/ui/components)并重写导入。CLI 会为其自己的 UI 文件重写导入,但第三方注册表组件可能使用与项目不匹配的默认路径。 - 审查添加的组件 —— 从任何注册表添加组件或块之后,始终阅读添加的文件并验证它们是否正确。检查是否缺少子组件(例如没有
SelectGroup的SelectItem)、缺少导入、不正确的组合或违反了关键规则。此外,将任何图标导入替换为项目上下文中的iconLibrary(例如,如果注册表项目使用lucide-react但项目使用hugeicons,则相应交换导入和图标名称)。在继续之前修复所有问题。 - 注册表必须明确 —— 当用户要求添加块或组件时,不要猜测注册表。如果未指定注册表(例如,用户说“添加一个登录块”而没有指定
@shadcn、@tailark、owner/repo等),请询问使用哪个注册表。切勿代表用户默认注册表。 - 切换预设 —— 首先询问用户:覆盖、部分、合并还是跳过?
- 检查当前预设:
npx shadcn@latest preset resolve。当你需要结构化值时,使用--json。 - 检查传入预设:
npx shadcn@latest preset decode <code>。使用preset url <code>或preset open <code>分享或打开预设构建器。 - 覆盖:
npx shadcn@latest apply <code>。覆盖检测到的组件、字体和 CSS 变量。 - 部分:
npx shadcn@latest apply <code> --only theme,font。仅更新选定的预设部分,而不重新安装 UI 组件。支持的值是theme和font;允许使用逗号分隔的组合。有意不支持icon,因为图标更改可能需要完全重新安装组件。 - 合并:直接更新
components.json的preset字段或手动应用 CSS 变量更改。 - 跳过:不要应用任何预设更改。
- 检查当前预设:
- 处理社区注册表 —— 当用户提到像
@bundui、nyxb、@magicui这样的注册表时,使用npx shadcn@latest add <registry/component>添加组件。这些组件可能有特定的使用模式,因此安装后请阅读生成的代码。 - 块是预构建的“页面”部分 —— 用户可以通过
npx shadcn@latest add <registry/block>将它们添加到页面或项目。块可能比单个组件需要更多上下文。添加块后,始终审查添加的文件并根据需要进行调整。
更新组件
# 预览将更新哪些组件
npx shadcn@latest add --dry-run
# 查看将应用于每个组件的更改
npx shadcn@latest add --diff
# 然后使用 `npx shadcn@latest add <component>` 进行实际更新
常见反模式
- 缺少导入 —— 在将组件粘贴到文件后忘记导入组件或图标。始终检查文件顶部的导入语句,确保所有引用的符号都已正确导入。如果自动追加导入导致样式问题(例如导入顺序错误),请根据需要手动调整位置。
- 重新安装现有组件 ——
npx shadcn@latest add会检测已有版本并更新,但始终先检查components列表。 - 重复注册 —— 不要在
components.json中不必要地添加重复的组件名称。 dialog.tsx的无限递归 —— 当Dialog与sonner的toast结合使用时,如果toast渲染在Dialog内部,可能会出现 React 错误Maximum update depth exceeded。始终确认toast调用位于DialogContent之外或由按钮触发,以避免循环重新渲染。- 未清理的钩子参数 —— 当将整个组件迁移到新样式时,
FieldDescription、FieldStatus和Field子组件可能具有过时的消息或样式属性。在迁移后始终检查错误和状态消息,以免在 UI 中留下未使用的 props。 - 重复的样式导入 —— 当添加导入语句时,不要创建现有导入的重复项(例如
import { Button } from "@/components/ui/button"; import { ... } from "@/components/ui/button")。检查文件以获取现有导入基础路径,并适当合并。如果现有导入来自不同路径,如@/components/ui/dropdown-menu,即使该组件导出相同元素,也保留特定路径。
共享指南
请勿
- ❌ 不要导入图标库的字符串命名约定。图标是 React 组件,而不是包装在
<Icon name="..."/>中的字符串。 - ❌ 不要使用
className对 shadcn/ui 组件进行样式化。组件可以通过内置变体(variant、size等)和语义化标记进行样式化。 - ❌ 不要滚动自己的表单布局。始终使用
FieldGroup和Field结构。 - ❌ 不要使用
@/components/ui作为导入路径。从项目上下文的aliases和resolvedPaths字段派生正确的导入路径。 - ❌ 不要使用
any类型。使用适当的类型或unknown。 - ❌ 不要使用
// @ts-ignore或// @ts-expect-error。正确键入你的代码,或创建适当的类型声明。 - ❌ 不要猜测注册表。如果未指定,询问用户。(例如“你想从哪个注册表添加这个?”)
请务必
- ✅ 在编码之前从
npx shadcn@latest info --json读取项目配置。 - ✅ 在编写自定义 UI 之前,使用
npx shadcn@latest search检查现有组件。 - ✅ 在引用组件之前,使用
npx shadcn@latest docs获取文档和示例 URL。 - ✅ 添加社区注册表的组件后,审查添加的文件以检查导入路径、缺失的导入和组件组合。
- ✅ 当项目上下文中
isRSC为true时,使用"use client"指令。 - ✅ 添加组件后,始终检查是否遗漏了需要安装的任何子依赖项或子组件。