Build Registry 完全指南
从零开始理解 shadcn/ui 组件注册系统
本文档面向初学者,帮助你理解 shadcn/ui 的组件注册和构建系统,以及如何复刻类似的系统。
📚 目录
什么是 Registry
Registry(注册表) 是 shadcn/ui 的核心机制,它是一个组件元数据的集合,记录了:
- 组件的名称、类型、描述
- 组件的文件路径
- 组件的依赖关系(npm 包依赖、组件间依赖)
- 组件的配置信息(Tailwind 配置、CSS 变量等)
简单来说,Registry 就像是一个组件商店的目录清单,告诉 CLI 工具:
- 有哪些组件可以使用
- 组件在哪里
- 安装组件需要什么
整体架构
┌─────────────────────────────────────────────────────────────┐
│ 开发者工作区 │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ 创建组件文件 │ │ 编写注册配置 │ │
│ │ (手动) │ │ (手动) │ │
│ └──────────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Build Registry 脚本 │
│ (自动化流程) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 1. buildRegistryIndex() - 生成组件索引 │ │
│ │ 2. buildBlocksIndex() - 生成 Blocks 索引 │ │
│ │ 3. buildRegistryJsonFile()- 生成 JSON 配置 │ │
│ │ 4. buildRegistry() - 构建发布版本 │ │
│ │ 5. syncRegistry() - 同步到 www 应用 │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 生成的产物 │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ registry.json │ │ __index__.tsx │ │
│ │ (元数据清单) │ │ (动态导入索引) │ │
│ └──────────────────┘ └──────────────────┘ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ __blocks__.json │ │ public/r/* │ │
│ │ (Blocks 列表) │ │ (发布文件) │ │
│ └──────────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────────┘核心概念
Registry Item 类型
在 shadcn/ui 中,有多种类型的 Registry Item:
| 类型 | 说明 | 示例 |
|---|---|---|
registry:ui | 基础 UI 组件 | Button, Input, Card |
registry:block | 完整的页面块/模板 | Login Form, Dashboard |
registry:hook | React Hooks | use-toast, use-mobile |
registry:lib | 工具函数 | utils.ts |
registry:example | 示例代码 | Button Demo |
registry:internal | 内部组件 | 文档展示用 |
registry:page | 页面文件 | 完整页面 |
registry:component | 组合组件 | Block 的子组件 |
Registry Item 结构
每个组件在注册表中的配置结构如下:
{
name: "button", // 组件名称
type: "registry:ui", // 类型
description: "A button component", // 描述
dependencies: ["@radix-ui/react-slot"], // npm 依赖
registryDependencies: ["utils"], // 其他组件依赖
files: [ // 文件列表
{
path: "ui/button.tsx", // 文件路径
type: "registry:ui", // 文件类型
target: "" // 可选:目标安装路径
}
],
tailwind: { // 可选:Tailwind 配置
config: { /* ... */ }
},
cssVars: { // 可选:CSS 变量
light: { /* ... */ },
dark: { /* ... */ }
}
}文件组织结构
registry/
├── index.ts # 主入口,聚合所有注册配置
├── registry-ui.ts # UI 组件注册配置
├── registry-blocks.ts # Blocks 注册配置
├── registry-hooks.ts # Hooks 注册配置
├── registry-lib.ts # 工具函数注册配置
├── registry-examples.ts # 示例注册配置
├── __index__.tsx # 🤖 自动生成:组件动态导入索引
├── __blocks__.json # 🤖 自动生成:Blocks 简化列表
└── new-york-v4/ # 实际组件文件目录
├── ui/ # UI 组件
│ ├── button.tsx
│ ├── input.tsx
│ └── ...
├── blocks/ # Blocks
│ ├── login-01/
│ │ ├── page.tsx
│ │ └── components/
│ └── ...
├── hooks/ # Hooks
├── lib/ # 工具函数
└── examples/ # 示例构建流程详解
流程图
第 1 步:buildRegistryIndex()
目的: 生成一个可以在运行时动态加载组件的索引文件
过程:
// 输入:registry/index.ts 中的配置
const registry = {
items: [
{ name: "button", files: [{ path: "ui/button.tsx" }] },
// ...
]
}
// 输出:registry/__index__.tsx
export const Index = {
"button": {
name: "button",
files: ["registry/new-york-v4/ui/button.tsx"],
component: React.lazy(() => import("@/registry/new-york-v4/ui/button.tsx")),
// ...
}
}作用:
- 允许文档网站动态预览组件
- 支持按需加载,提高性能
第 2 步:buildBlocksIndex()
目的: 生成一个精简的 Blocks 列表,用于快速查询
过程:
// 输入:所有 type: "registry:block" 的组件
const blocks = getAllBlocks(["registry:block"])
// 输出:registry/__blocks__.json
[
{
"name": "login-01",
"description": "A simple login form",
"categories": ["authentication", "login"]
},
// ...
]作用:
- 用于 Blocks 页面的快速列表展示
- 减少前端加载的数据量
第 3 步:buildRegistryJsonFile()
目的: 生成完整的组件元数据清单
过程:
-
修正路径:将相对路径转换为绝对路径
// 之前: "ui/button.tsx" // 之后: "registry/new-york-v4/ui/button.tsx" -
生成 JSON:写入
registry.json -
格式化:使用 Prettier 格式化
-
同步:复制到
www/public/r/styles/new-york-v4/registry.json
作用:
- CLI 工具通过此文件知道如何下载和安装组件
- 网站通过此文件展示组件列表和元数据
第 4 步:buildRegistry()
目的: 使用 shadcn CLI 构建可发布的组件文件
过程:
# 运行 shadcn CLI 的 build 命令
shadcn build registry.json --output ../www/public/r/styles/new-york-v4输出结构:
www/public/r/styles/new-york-v4/
├── button.json # 每个组件的独立元数据
├── input.json
├── login-01.json
└── ...每个 JSON 文件包含:
- 组件的完整代码(内联)
- 依赖信息
- 安装配置
作用:
- 用户运行
npx shadcn@latest add button时,CLI 从这里下载 - 支持离线缓存和 CDN 分发
第 5 步:syncRegistry()
目的: 同步 v4 和 www 两个应用的 registry
过程:
- 复制
v4/public/r/registries.json→www/public/r/registries.json - 运行
www项目的pnpm registry:build - 复制
www/public/r→v4/public/r
作用:
- 保持两个应用的组件库同步
www是旧版,v4是新版,需要兼容
实践指南
如何创建一个组件
👨💻 人工操作部分
步骤 1:创建组件文件
# 在 registry/new-york-v4/ui/ 目录下创建组件文件
touch registry/new-york-v4/ui/my-component.tsx// registry/new-york-v4/ui/my-component.tsx
import * as React from "react"
import { cn } from "@/lib/utils"
interface MyComponentProps extends React.HTMLAttributes<HTMLDivElement> {
variant?: "default" | "primary"
}
function MyComponent({ className, variant = "default", ...props }: MyComponentProps) {
return (
<div
className={cn("my-component", className)}
data-variant={variant}
{...props}
/>
)
}
export { MyComponent }步骤 2:在注册表中配置
编辑 registry/registry-ui.ts:
export const ui: Registry["items"] = [
// ... 其他组件
{
name: "my-component", // 组件名称(用户安装时使用)
type: "registry:ui", // 类型
description: "My custom component", // 描述
dependencies: [], // npm 依赖(如需要)
registryDependencies: ["button", "card"], // 依赖其他组件(如需要)
files: [
{
path: "ui/my-component.tsx", // 文件路径(相对于 new-york-v4/)
type: "registry:ui",
},
],
// 可选:如果需要特殊的 Tailwind 配置
tailwind: {
config: {
theme: {
extend: {
colors: {
"my-color": "#ff0000"
}
}
}
}
},
// 可选:如果需要 CSS 变量
cssVars: {
light: {
"my-var": "0 0% 100%"
},
dark: {
"my-var": "0 0% 0%"
}
}
},
]步骤 3:运行构建命令
pnpm registry:build🤖 自动化完成的部分
脚本会自动:
- ✅ 读取你的配置
- ✅ 生成
registry/__index__.tsx,添加动态导入 - ✅ 生成
registry.json,包含完整元数据 - ✅ 生成
public/r/styles/new-york-v4/my-component.json - ✅ 同步到其他目录
📦 用户如何使用
npx shadcn@latest add my-componentCLI 会:
- 读取
public/r/styles/new-york-v4/my-component.json - 检查并安装 npm 依赖
- 安装 registryDependencies(
button,card) - 复制组件代码到用户项目
- 更新 Tailwind 配置(如果需要)
如何创建一个 Block
Block 是完整的页面模板或功能块,通常包含多个文件。
👨💻 人工操作部分
步骤 1:创建 Block 目录和文件
# 创建 Block 目录
mkdir -p registry/new-york-v4/blocks/my-dashboard/{components,lib}
# 创建主页面
touch registry/new-york-v4/blocks/my-dashboard/page.tsx
# 创建组件
touch registry/new-york-v4/blocks/my-dashboard/components/sidebar.tsx
touch registry/new-york-v4/blocks/my-dashboard/components/header.tsx
# 可选:创建数据文件
touch registry/new-york-v4/blocks/my-dashboard/data.json文件结构:
blocks/my-dashboard/
├── page.tsx # 主页面文件
├── components/
│ ├── sidebar.tsx # 子组件
│ └── header.tsx # 子组件
└── data.json # 可选:数据文件步骤 2:编写代码
// page.tsx
import { Sidebar } from "./components/sidebar"
import { Header } from "./components/header"
export default function Page() {
return (
<div className="flex h-screen">
<Sidebar />
<div className="flex-1">
<Header />
<main className="p-6">
{/* 内容 */}
</main>
</div>
</div>
)
}// components/sidebar.tsx
export function Sidebar() {
return <aside className="w-64 border-r">Sidebar</aside>
}步骤 3:在注册表中配置
编辑 registry/registry-blocks.ts:
export const blocks: Registry["items"] = [
// ... 其他 blocks
{
name: "my-dashboard",
type: "registry:block",
description: "A dashboard with sidebar and header",
// npm 依赖
dependencies: [
"@tanstack/react-table",
"recharts",
],
// 组件依赖(会自动安装这些组件)
registryDependencies: [
"sidebar",
"card",
"button",
"table",
],
// 文件列表
files: [
{
path: "blocks/my-dashboard/page.tsx",
type: "registry:page", // 标记为页面文件
target: "app/dashboard/page.tsx", // 安装到用户项目的路径
},
{
path: "blocks/my-dashboard/data.json",
type: "registry:file", // 普通文件
target: "app/dashboard/data.json",
},
{
path: "blocks/my-dashboard/components/sidebar.tsx",
type: "registry:component", // 子组件
},
{
path: "blocks/my-dashboard/components/header.tsx",
type: "registry:component",
},
],
// 分类(用于筛选和展示)
categories: ["dashboard", "admin"],
// 元数据(用于文档展示)
meta: {
iframeHeight: "800px", // 预览高度
container: "w-full min-h-screen", // 容器样式
},
},
]步骤 4:运行构建
pnpm registry:build🤖 自动化完成的部分
脚本会自动:
- ✅ 将 Block 添加到
__index__.tsx - ✅ 将 Block 添加到
__blocks__.json(用于列表展示) - ✅ 生成
my-dashboard.json,包含所有文件的内联代码 - ✅ 处理依赖关系
📦 用户如何使用
npx shadcn@latest add my-dashboardCLI 会:
- 安装 npm 依赖:
@tanstack/react-table,recharts - 安装组件依赖:
sidebar,card,button,table - 复制文件到指定位置:
page.tsx→app/dashboard/page.tsxdata.json→app/dashboard/data.jsonsidebar.tsx→components/sidebar.tsxheader.tsx→components/header.tsx
人工操作 vs 自动化
🙋 必须人工完成的工作
| 任务 | 说明 |
|---|---|
| 编写组件代码 | 实际的 React 组件逻辑、样式、功能 |
| 设计组件 API | Props、事件、接口设计 |
| 编写注册配置 | 在 registry-*.ts 中添加组件元数据 |
| 确定依赖关系 | 决定需要哪些 npm 包和其他组件 |
| 设置文件路径 | 决定组件的目录结构和安装位置 |
| 编写测试和文档 | 可选但推荐 |
| 配置 Tailwind/CSS | 如果需要特殊配置 |
🤖 脚本自动完成的工作
| 任务 | 脚本 | 说明 |
|---|---|---|
| 生成动态索引 | buildRegistryIndex() | 创建 __index__.tsx |
| 生成 Blocks 列表 | buildBlocksIndex() | 创建 __blocks__.json |
| 生成 JSON 配置 | buildRegistryJsonFile() | 创建 registry.json |
| 构建发布文件 | buildRegistry() | 为每个组件生成独立 JSON |
| 同步多个应用 | syncRegistry() | 在 v4 和 www 之间同步 |
| 格式化代码 | Prettier | 自动格式化生成的文件 |
| 路径转换 | 自动 | 相对路径 → 绝对路径 |
| 内联代码 | shadcn CLI | 将组件代码嵌入 JSON |
典型工作流程
👨💻 人工:编写组件代码
↓
👨💻 人工:编写注册配置
↓
🤖 运行:pnpm registry:build
↓
🤖 自动:生成所有元数据和索引
↓
✅ 完成:组件可以被安装使用工作流程图
完整的开发到发布流程
快速参考
常用命令
# 开发模式(启动开发服务器)
pnpm dev
# 构建 registry(添加/修改组件后运行)
pnpm registry:build
# 捕获 Blocks 截图(用于文档展示)
pnpm registry:capture
# 验证 registry 配置
pnpm validate:registries
# 格式化代码
pnpm format:write关键文件速查
| 文件 | 作用 | 编辑频率 |
|---|---|---|
registry/registry-ui.ts | UI 组件注册配置 | ⭐⭐⭐ 经常 |
registry/registry-blocks.ts | Blocks 注册配置 | ⭐⭐⭐ 经常 |
registry/index.ts | 注册表入口 | ⭐ 很少 |
scripts/build-registry.mts | 构建脚本 | ⭐ 很少 |
registry/__index__.tsx | 🤖 自动生成 | ❌ 不要编辑 |
registry.json | 🤖 自动生成 | ❌ 不要编辑 |
常见问题
Q: 我创建了组件但 CLI 找不到?
- A: 确保运行了
pnpm registry:build - A: 检查
registry-*.ts中的配置是否正确
Q: 组件安装后路径不对?
- A: 检查
files[].target配置 - A: Block 的
page.tsx应设置target
Q: 依赖没有自动安装?
- A: 检查
dependencies和registryDependencies配置 - A: npm 包依赖用
dependencies - A: 其他组件依赖用
registryDependencies
Q: 如何调试构建过程?
- A: 查看脚本输出的日志
- A: 检查生成的
registry.json文件 - A: 查看
public/r/styles/new-york-v4/*.json文件
总结
核心要点
- Registry 是组件元数据的集合,不是组件本身
- 手动部分:编写代码和配置
- 自动部分:运行脚本生成所有元数据
- 两层结构:
- 开发层:
registry/new-york-v4/- 组件源码 - 配置层:
registry/registry-*.ts- 元数据配置
- 开发层:
- 三个产物:
__index__.tsx- 运行时索引registry.json- 完整配置public/r/*.json- 发布文件
复刻要点
如果你要创建类似的系统,需要:
- ✅ 定义 Schema:组件元数据的数据结构
- ✅ 组织文件结构:组件代码 + 配置分离
- ✅ 编写构建脚本:
- 读取配置
- 生成索引
- 生成 JSON
- 内联代码(可选)
- ✅ 创建 CLI 工具:读取 JSON 并安装组件
- ✅ 文档系统:展示组件列表和预览
下一步
- 📖 阅读
shadcnCLI 源码理解安装逻辑 - 🔧 尝试创建自己的组件和 Block
- 🎨 自定义构建流程以满足需求
- 📦 发布到 npm 或私有 registry
文档版本: v1.0 最后更新: 2025-10-23 作者: AI Assistant 项目: shadcn/ui Build Registry 系统解析