How to Build and Publish Your Own shadcn/ui Registry
Updated 2026-10-27
A shadcn registry lets other developers install your components, blocks, hooks, themes and config files with one CLI command, as source code they can edit. You do not need a package on npm or a build pipeline beyond a single command. This guide walks through the files, the item types, the build step, hosting, and the official directory listing.
What a registry is made of
A registry has two kinds of JSON:
registry.jsonat the root, which names the registry and lists its items- One
registry-item.jsonper item, generated by the build, which contains the item's metadata and the full content of its files
You write registry.json and your source files. The shadcn build command reads them and produces the per-item JSON files that the CLI fetches when someone runs shadcn add.
Step 1: Lay out your source files
The shadcn docs recommend placing each item under registry/[STYLE]/[NAME], where the style folder can be any name you choose:
registry/
default/
hello-world/
hello-world.tsx
registry.json
Write imports inside registry files using the @/registry path. The CLI rewrites them to match the consumer's aliases on install.
Step 2: Write registry.json
The root file needs a name, a homepage, and either items or include:
{
"$schema": "https://ui.shadcn.com/schema/registry.json",
"name": "acme",
"homepage": "https://acme.com",
"items": [
{
"name": "hello-world",
"type": "registry:block",
"title": "Hello World",
"description": "A simple hello world component.",
"registryDependencies": ["button", "card"],
"dependencies": ["motion"],
"files": [
{
"path": "registry/default/hello-world/hello-world.tsx",
"type": "registry:component"
}
]
}
]
}
Key fields on each item:
name: unique within your registry; this is what users type after the namespacetype: decides how the item is resolved and where files land (see below)dependenciesanddevDependencies: npm packages, with optional@versionregistryDependencies: other registry items, given as plain names (button), namespaced names (@acme/input-form), URLs or GitHub addressesfiles: each with apath, atypeand, where needed, atarget
Write a clear title and description for every item. The docs point out that AI assistants using the shadcn MCP server rely on these to pick the right component.
For larger registries, split the file with include, which points to nested registry.json files. Paths in a nested file are relative to that file:
{
"$schema": "https://ui.shadcn.com/schema/registry.json",
"name": "acme",
"homepage": "https://acme.com",
"include": ["components/ui/registry.json", "hooks/registry.json"]
}
Step 3: Choose item types
The type field is the main decision for each item.
| Type | Use it for |
|---|---|
registry:ui | UI components and single-file primitives |
registry:component | Simple components |
registry:block | Complex components with multiple files |
registry:hook | React hooks |
registry:lib | Utilities and libraries |
registry:page | Pages or file-based routes (requires target) |
registry:file | Miscellaneous files such as config (requires target) |
registry:style | Registry styles such as new-york |
registry:theme | Themes (CSS variables) |
registry:font | Fonts, configured through the font field |
registry:base | An entire design system in one install |
registry:item | Universal items |
A few practical notes:
- Themes usually carry no files. They use
cssVars, grouped undertheme,lightanddark, and the CLI merges them into the user's CSS file. - Fonts became a first-class type in shadcn CLI v4. A
registry:fontitem uses thefontfield to set the family, provider, import name and CSS variable. registry:basepackages components, dependencies, CSS variables, fonts and config together, so a whole design system installs at once.
Controlling where files go
target sets the destination path. It can start with ~ for the project root, or with a placeholder that resolves through the user's components.json aliases: @components/, @ui/, @lib/ or @hooks/.
{
"path": "registry/default/example/button.tsx",
"type": "registry:ui",
"target": "@ui/button.tsx"
}
CSS, env vars and install messages
Items can also add css rules (such as @utility, @keyframes or @layer base blocks), example envVars written to .env.local without overwriting existing values, and a docs string the CLI prints after install. Keep envVars to development or placeholder values.
Step 4: Run shadcn build
npx shadcn@latest build
By default the command reads ./registry.json and writes one JSON file per item to public/r, so hello-world becomes public/r/hello-world.json. Change the destination with --output:
npx shadcn@latest build --output ./public/registry
Add it as a script so it runs before every deploy:
{
"scripts": {
"registry:build": "shadcn build"
}
}
Step 5: Test locally
Start your dev server and install from it exactly as users will:
npx shadcn@latest list http://localhost:3000/r/registry.json
npx shadcn@latest add http://localhost:3000/r/hello-world.json
Test in a fresh project with its own components.json, so you catch missing registryDependencies or wrong targets.
Step 6: Host the registry
The output is static JSON, so any static host works: Vercel, Netlify, Cloudflare Pages, GitHub Pages or an S3 bucket. If you build the registry inside a Next.js docs site, the files in public/r are served automatically. For dynamic needs, such as per-user access, the docs describe serving items from route handlers using the shadcn/registry loaders.
Once deployed, users can add your namespace to their components.json:
{
"registries": {
"@acme": "https://acme.com/r/{name}.json"
}
}
Hosting on GitHub instead
A public GitHub repo with a valid registry.json at its root is already installable through owner/repo/item addresses, with optional #ref pinning. This suits internal conventions, rules files and small utility collections that do not need a website.
Private registries
For paid or internal registries, require a token on your server and tell users to configure the registry with an Authorization or API key header read from an environment variable. Serve over HTTPS, rate-limit requests, and return a clear error message, since the CLI shows the server's message on 401 and 403 responses.
Step 7: Get listed in the official directory
Listing in the shadcn registry directory means users can type @yourname/item with no setup, because the CLI adds your registry to their components.json automatically. The requirements are:
- The registry is open source and publicly accessible
- It is valid JSON that follows the registry schema
- It is flat:
/registry.jsonand/component-name.jsonfiles sit at the root, with no nested items - File entries in
registry.jsondo not include acontentproperty
To submit, add your entry to apps/v4/registry/directory.json in the shadcn-ui/ui repository, run pnpm validate:registries, and open a pull request. Each entry has a name (your namespace), homepage, url template, description and a logo. After the PR is merged, the registry is live immediately.
If you want your registry in front of more developers, you can also submit it to the component library or block library categories here. Listings such as dashboardcn and Magic UI are good references for how public registries document their install commands.
FAQ
Do I need Next.js to host a registry?
No. The build output is plain JSON, and the items you publish can target any framework. Next.js is common only because many registries live inside a docs site.
Can I include the file contents in registry.json?
The build step embeds file contents into each generated item JSON. The root registry.json should only reference paths; the directory requirements explicitly reject content properties there.
How do users get updates to my components?
They run shadcn add again for the item, review the change with --diff, and accept or reject the overwrite. There is no automatic update, since the code lives in their project.
Can my items depend on official shadcn components?
Yes. List them by plain name in registryDependencies, such as button or card, or with the @shadcn/ namespace. The CLI installs any that are missing from the user's project before installing your item.