117k

入门指南

学习如何设置并运行你自己的组件注册表。

本指南将带你完成设置你自己的注册表的过程。它假设你已经有一个项目,其中包含你想要分发的组件、hooks、工具或其他文件。

如果你已经有一个现有的公开 GitHub 仓库,只需在根目录添加一个 registry.json 文件,就可以将其转换为一个 注册表。 详情请参见 GitHub Registries

如果你是从头开始创建一个新的注册表项目,可以使用注册表模板作为起点。我们已经为你做好了配置。

要求

你可以自由设计并发布你的自定义注册表,只要你愿意。唯一的要求是,你的注册表目录和注册表条目必须符合注册表模式规范注册表条目模式规范

你的注册表可以是 Next.js、Vite、Vue、Svelte、PHP,或者任何其他框架,只要它支持通过 HTTP 提供 JSON 即可。它也可以是一个公开的 GitHub 仓库,只要根目录下有一个 registry.json 文件。

如果你想查看一个注册表示例,我们提供了一个模板项目供你作为起点使用。

registry.json

registry.json 是注册表的入口文件。它包含注册表的名称、主页,并定义注册表中所有存在的条目。

你的注册表必须在注册表端点根目录下存在此文件(或 JSON 数据)。注册表端点是你托管注册表的 URL。

下面是一个 registry.json 文件示例:

registry.json
{
  "$schema": "https://ui.shadcn.com/schema/registry.json",
  "name": "acme",
  "homepage": "https://acme.com",
  "items": [
    {
      "name": "button",
      "type": "registry:ui",
      "title": "Button",
      "description": "一个简单的按钮组件。",
      "files": [
        {
          "path": "components/ui/button.tsx",
          "type": "registry:ui"
        }
      ]
    }
  ]
}

构建你的注册表结构

你可以通过以下两种方式之一来组织你的源注册表:

  • 在单个根目录 registry.json 中定义所有条目。
  • 使用带有 include 的根目录 registry.json 来组合多个 registry.json 文件。

选项 A:单个 registry.json

在项目根目录创建一个 registry.json 文件。将所有注册表条目添加到 items 数组中。这是定义注册表最简单的方式。

registry.json
{
  "$schema": "https://ui.shadcn.com/schema/registry.json",
  "name": "acme",
  "homepage": "https://acme.com",
  "items": [
    {
      "name": "button",
      "type": "registry:ui",
      "title": "Button",
      "description": "一个简单的按钮组件。",
      "files": [
        {
          "path": "components/ui/button.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "hello-world",
      "type": "registry:block",
      "title": "Hello World",
      "description": "一个简单的 hello world 组件。",
      "registryDependencies": ["button"],
      "files": [
        {
          "path": "registry/default/hello-world/hello-world.tsx",
          "type": "registry:component"
        }
      ]
    }
  ]
}

registry.json 文件必须符合注册表模式规范

选项 B:使用 include

对于更大的注册表,你可以使用 include 将你的源注册表组合自多个 registry.json 文件。

registry.json
components
└── ui
    ├── button.tsx
    ├── input.tsx
    └── registry.json
hooks
├── registry.json
├── use-media-query.ts
└── use-toggle.ts

根目录的 registry.json 定义了注册表元数据,并包含嵌套的 注册表文件。

registry.json
{
  "$schema": "https://ui.shadcn.com/schema/registry.json",
  "name": "acme",
  "homepage": "https://acme.com",
  "include": [
    "components/ui/registry.json",
    "hooks/registry.json"
  ]
}

被包含的 registry.json 文件是用于组合的有效注册表文件,可以省略 namehomepage。只有根级 registry.json 必须定义注册表元数据。

components/ui/registry.json
{
  "$schema": "https://ui.shadcn.com/schema/registry.json",
  "items": [
    {
      "name": "button",
      "type": "registry:ui",
      "files": [
        {
          "path": "button.tsx",
          "type": "registry:ui"
        }
      ]
    },
    {
      "name": "input",
      "type": "registry:ui",
      "files": [
        {
          "path": "input.tsx",
          "type": "registry:ui"
        }
      ]
    }
  ]
}
hooks/registry.json
{
  "$schema": "https://ui.shadcn.com/schema/registry.json",
  "items": [
    {
      "name": "use-toggle",
      "type": "registry:hook",
      "files": [
        {
          "path": "use-toggle.ts",
          "type": "registry:hook"
        }
      ]
    },
    {
      "name": "use-media-query",
      "type": "registry:hook",
      "files": [
        {
          "path": "use-media-query.ts",
          "type": "registry:hook"
        }
      ]
    }
  ]
}

使用 include 时,文件路径相对于声明该条目的 registry.json 文件。

添加条目

创建一个 UI 组件

添加你的第一个条目。下面是一个简单 <Button /> 组件的示例:

components/ui/button.tsx
import * as React from "react"
 
export function Button(props: React.ComponentProps<"button">) {
  return (
    <button
      {...props}
      className="rounded-md bg-neutral-900 px-4 py-2 text-sm font-medium text-white"
    />
  )
}
components
└── ui
    └── button.tsx

将条目添加到注册表

要将组件添加到注册表,请在 registry.json 中添加一个条目定义。 如果你正在使用 include,请将条目添加到拥有该组件的被包含 registry.json 文件中。 例如,将 UI 组件添加到 components/ui/registry.json

registry.json
{
  "$schema": "https://ui.shadcn.com/schema/registry.json",
  "name": "acme",
  "homepage": "https://acme.com",
  "items": [
    {
      "name": "button",
      "type": "registry:ui",
      "title": "按钮",
      "description": "一个简单的按钮组件。",
      "files": [
        {
          "path": "components/ui/button.tsx",
          "type": "registry:ui"
        }
      ]
    }
  ]
}

你需要通过添加 nametypetitledescriptionfiles 来定义注册表条目。

对于你添加的每个文件,都必须指定文件的 pathtype。在单文件注册表中,path 相对于项目根目录。使用 include 时,path 相对于声明该条目的 registry.json 文件。type 是文件类型。

你可以在注册表条目模式文档中查看更多关于注册表条目模式和文件类型的介绍。

提供你的注册表

你可以将注册表作为静态 JSON 文件或通过动态路由处理器来提供。

选项 A:静态 JSON 文件

运行构建命令以生成静态注册表 JSON 文件。

pnpm dlx shadcn@latest build

如果你的源注册表使用了 includeshadcn build 会解析被包含的注册表并将扁平化后的注册表写入输出目录。生成的 registry.json 不包含 include

如果你在 Next.js 中运行注册表,可以通过运行 next 服务器来提供这些文件。对于其他框架,命令可能会有所不同。

pnpm dev

你的文件现在将托管在 http://localhost:3000/r/[NAME].json,例如 http://localhost:3000/r/button.json

选项 B:动态路由处理器

如果你希望在请求时直接从源 registry.json 提供注册表 JSON,请使用 shadcn/registry 中的生产端加载器 API。

shadcn 安装为运行时依赖:

pnpm add shadcn

使用 loadRegistry 来提供注册表目录。

app/r/registry.json/route.ts
import { loadRegistry } from "shadcn/registry"
 
export async function GET() {
  try {
    const registry = await loadRegistry()
 
    return Response.json(registry)
  } catch (error) {
    console.error(error)
 
    return Response.json({ error: "Failed to load registry." }, { status: 500 })
  }
}

使用 loadRegistryItem 来提供单个注册表条目。

app/r/[name].json/route.ts
import { loadRegistryItem, RegistryItemNotFoundError } from "shadcn/registry"
 
export async function GET(
  _request: Request,
  context: {
    params: Promise<{
      name: string
    }>
  }
) {
  const { name } = await context.params
 
  try {
    const item = await loadRegistryItem(name)
 
    return Response.json(item)
  } catch (error) {
    if (error instanceof RegistryItemNotFoundError) {
      return Response.json(
        { error: `Registry item "${name}" was not found.` },
        { status: 404 }
      )
    }
 
    console.error(error)
 
    return Response.json(
      { error: "Failed to load registry item." },
      { status: 500 }
    )
  }
}

这两个加载器在返回 JSON 之前都会解析 include,因此路由处理器可以使用相同的源 registry.json 结构,而无需运行 shadcn build

测试你的注册表

在你的注册表开始提供服务后,使用其他开发者也会使用的相同 CLI 命令来测试它。

使用 URL

将目录 URL 用于发现条目的命令,例如 listsearch。将条目 URL 用于读取或安装特定条目的命令,例如 viewadd

列出条目

首先确认可以发现注册表目录。

pnpm dlx shadcn@latest list http://localhost:3000/r/registry.json

搜索条目

按查询搜索注册表。

pnpm dlx shadcn@latest search http://localhost:3000/r/registry.json --query button

查看条目

然后按名称查看某个注册表条目。

pnpm dlx shadcn@latest view http://localhost:3000/r/button.json

添加条目

要测试安装流程,请在你希望安装该条目的项目中运行 add

pnpm dlx shadcn@latest add http://localhost:3000/r/button.json

使用命名空间

添加注册表

你也可以使用命名空间来测试你的注册表。在带有 components.json 文件的项目中,将你的注册表 URL 模板添加到项目中。

pnpm dlx shadcn@latest registry add @acme=http://localhost:3000/r/{name}.json

{name} 占位符必须解析为一个条目的 JSON 文件。例如,@acme/button 会解析为 http://localhost:3000/r/button.json。目录仍然单独托管在 http://localhost:3000/r/registry.json

列出条目

然后列出你注册表中的条目。

pnpm dlx shadcn@latest list @acme

搜索条目

按查询搜索注册表。

pnpm dlx shadcn@latest search @acme --query button

查看条目

按名称查看某个注册表条目。

pnpm dlx shadcn@latest view @acme/button

添加条目

要测试安装流程,请在你希望安装该条目的项目中运行 add

pnpm dlx shadcn@latest add @acme/button

更多信息请参阅命名空间注册表文档。

发布你的注册表

为了让你的注册表可供其他开发者使用,请将你的项目发布到一个 公开 URL。部署完成后,用户可以直接通过项目项 URL 安装条目,或者 他们也可以将你的注册表作为命名空间添加到他们的项目中。

分享命名空间设置说明

如果你希望用户通过类似 @acme/button 这样的命名空间安装条目,请告诉 他们将你的注册表 URL 模板添加到他们的项目中。CLI 解析注册表 条目时,{name} 占位符会被替换为条目名称。

该模板必须能够解析到条目的 JSON 文件。例如,@acme/button 会解析到 https://acme.com/r/button.json。你的注册表目录仍应单独 托管在 https://acme.com/r/registry.json

他们可以通过 CLI 添加该命名空间。

pnpm dlx shadcn@latest registry add @acme=https://acme.com/r/{name}.json

或者,他们也可以在自己的 components.json 文件中的 registries 字段下手动添加。

components.json
{
  "registries": {
    "@acme": "https://acme.com/r/{name}.json"
  }
}

然后,用户就可以通过命名空间从你的注册表中使用条目。

pnpm dlx shadcn@latest add @acme/button

将你的命名空间添加到注册表索引

如果你的注册表是开源且公开可用的,你可以将你的 命名空间提交到官方注册表索引。这样用户就可以通过名称而不是粘贴完整的 URL 模板来 添加你的命名空间。

有关提交要求,请参阅 注册表索引 文档。

指南

构建注册表组件时,请遵循以下指南:

  • 将你的注册表条目放在 registry/[STYLE]/[NAME] 目录中。我这里以 default 作为示例。只要它嵌套在 registry 目录下,名称可以是任意的。
  • 对于 blocks,以下属性是必需的:namedescriptiontypefiles
  • 建议为你的注册表条目添加合适的名称和描述。这有助于 LLM 理解该组件及其用途。
  • 确保在 registryDependencies 中列出所有注册表依赖项。注册表依赖项是一个条目地址,例如 button@acme/input-formacme/ui/buttonhttp://localhost:3000/r/editor.json
  • 确保在 dependencies 中列出所有依赖项。依赖项是注册表中包的名称,例如 zodsonner 等。要设置版本,可以使用 name@version 格式,例如 zod@^3.20.0
  • 导入应始终使用 @/registry 路径。 例如 import { HelloWorld } from "@/registry/default/hello-world/hello-world"
  • 理想情况下,将你的文件放在注册表条目中的 componentshookslib 目录下。