Capítulo 99 de 108

Getting Started (Registry)

Core Idea

Full walkthrough for standing up a custom registry: define registry.json, add items, serve them (static build or dynamic route handlers), test with CLI commands, and publish for others to consume.

Key Concepts

  • registry.json: The entry point at the registry root; contains name, homepage, and either a flat items array or include (composed from multiple nested registry.json files).
  • Single vs. include structure: Small registries put everything in one root registry.json; larger ones split by directory (components/ui/registry.json, hooks/registry.json) and compose via root include. Included files may omit name/homepage; only root needs them. With include, file paths are relative to the declaring registry.json, not the project root.
  • npx shadcn build: Generates static registry JSON files (default public/r/[name].json); resolves include and flattens output (no include in generated JSON). --output changes the target dir.
  • Dynamic serving via shadcn/registry: Install shadcn as a runtime dep; use loadRegistry() (serves the catalog) and loadRegistryItem(name) (serves one item, throws RegistryItemNotFoundError on 404) in route handlers — both resolve include at request time without needing shadcn build.
  • Content negotiation: CLI requests send User-Agent: shadcn and Accept: application/vnd.shadcn.v1+json, application/json;q=0.9; a server can inspect these to serve JSON to the CLI/MCP while serving HTML/docs to browsers from the same root URL — enables "branded registry URLs" like shadcn add https://ui.example.com.
  • URL vs. namespace testing: list/search/view/add all accept either a direct catalog/item URL, or a configured @namespace (added via npx shadcn registry add @acme=URL_TEMPLATE).

Code Examples

{
  "$schema": "https://ui.shadcn.com/schema/registry.json",
  "name": "acme",
  "homepage": "https://acme.com",
  "items": [
    {
      "name": "button",
      "type": "registry:ui",
      "title": "Button",
      "description": "A simple button component.",
      "files": [{ "path": "components/ui/button.tsx", "type": "registry:ui" }]
    }
  ]
}
  • O que demonstra: estrutura mínima e válida de registry.json com um item.
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 })
    }
    return Response.json({ error: "Failed to load registry item." }, { status: 500 })
  }
}
  • O que demonstra: rota dinâmica Next.js servindo item individual sob demanda via loadRegistryItem, sem precisar rodar shadcn build.

Reference Tables

CLI test commandPurposeURL formNamespace form
listdiscover catalog itemslist <catalog-url>list @acme
search --querysearch catalogsearch <catalog-url> --query buttonsearch @acme --query button
viewinspect one itemview <item-url>view @acme/button
addinstall one itemadd <item-url>add @acme/button

Anti-patterns

  • Mixing path conventions between single-file and include registries: with include, paths are relative to the declaring file, not project root — a common source of broken builds.
  • Forgetting registryDependencies for cross-item deps: components referencing other registry items must declare it, or the CLI won't resolve/install them.
  • Skipping title/description: recommended so LLMs (and MCP) can understand the component's purpose.
  • Imports not using @/registry path: registry items should import via @/registry/default/hello-world/hello-world, not relative paths, to survive relocation.

Key Takeaways

  1. Start from the official registry-template GitHub repo if building a new registry from scratch, or add registry.json to an existing public repo to make it GitHub-installable (see GitHub registries doc).
  2. Every file entry needs path and type; the type determines default install location, and target can override it (required for registry:page/registry:file).
  3. Static build (shadcn build) is simplest for stable content; dynamic route handlers via shadcn/registry loaders suit registries with request-time logic (auth, personalization).
  4. To get listed under @namespace shortcuts without pasting a URL, submit to the official Registry Index (open-source, publicly accessible registries only).
  5. Place registry items under registry/[STYLE]/[NAME], organized internally into components, hooks, lib.

Connects To

  • registry-json (ch107): full schema for registry.json referenced throughout this guide.
  • registry-item-json (ch108): full schema for individual items and their files[].type.
  • registry-namespace (ch104): consumer-side namespace configuration tested here.
  • registry-mcp (ch103): MCP requires the same registry.json at the root that this guide sets up.