A registry is a JSON file and some source files on a static host. That is the whole specification, and it is why there are hundreds of them: publishing does not require a package, a build pipeline or anyone's permission.
Here is the shape, and the four things that separate a registry people install from from one they bounce off.
The file
registry.json lists items. Each item names itself, declares a type, lists its files, and declares what it needs:
The two dependency fields do different jobs. dependencies are npm packages, installed with the reader's package manager. registryDependencies are other registry items, resolved recursively - a bare name means the official registry, and a full URL means anywhere.
The types matter more than they look. registry:ui lands in the UI alias, registry:block in components, registry:hook in hooks, registry:lib in lib. Getting the type wrong puts a hook in your components folder in every project that installs it.
Building and hosting
npx shadcn build turns registry.json into one JSON file per item under public/r. From there any static host works, and the install command is just the URL:
That is the entire distribution mechanism. No npm publish, no version, no registry account.
The four things that decide whether anyone uses it
Declare every dependency honestly. An install that fails on a missing import is an install nobody retries, and it is the single most common reason a registry gets abandoned by its users. Test in a clean project, not in the repository that has everything installed already.
Ship a demo per item. A name and a file list are not enough to choose from. This is the argument for publishing to a catalogue that renders a live preview beside the code as well as hosting your own JSON, because the decision to install is made from a picture.
Read the tokens. A component that hardcodes bg-zinc-900 looks right in your theme and wrong in everyone else's, and it will never follow a theme change. Use the standard variables, which the theming guide lists.
Namespace your file names. button.tsx will overwrite the reader's existing button. A distinctive name, or a subfolder, avoids the collision the CLI guide warns about.
Private and paid registries
Nothing in the format requires the JSON to be public. The item URL can require an API key as a query parameter or a header, and the CLI substitutes environment variables from components.json, which is how a team shares configuration without sharing a key.
That is the same mechanism behind a paid registry: the index is public so people can browse, and the item's files are served only against a valid key. The private registry guide covers the team version.
What you owe your users
Two things that are not in the schema.
Keep the item stable once it exists. Because add copies rather than links, a change to your registry does not reach anyone who already installed it - but the person installing tomorrow gets whatever is there. Renaming or removing an item breaks the install command in every blog post and every README that references it.
And say when you stop. A registry that has not shipped in six months is fine to use and misleading to present as active. The directory shows last-commit dates precisely because that is the fact people need and star counts do not carry.
Where else to look
The honest list, because the answer is not always us:
| Source | Best for | Trade-off |
|---|---|---|
| shadcn registry docs | The canonical schema, types and build command | You host, document and promote it |
| A static host | Serving the JSON, free and instantly | No previews, no discovery |
| Publishing on 21st | A live preview, a page and discovery beside other registries | Review before a component is listed |
| npm | A versioned package with real upgrades | The opposite model: consumers cannot edit the code |
Taking one
Every component page has a live preview and the code. Installing goes through the shadcn CLI against our registry:
That key comes from your 21st account, and installs require a membership. Set API_KEY_21ST once in your shell and the command works for anything in the catalogue.
Browse the registry directory →
Frequently asked
- How do you create a shadcn registry?
- Write a registry.json listing items, each with a name, a type, its files, its npm dependencies and any registry dependencies. Run npx shadcn build to emit one JSON file per item, host the output as static files, and the install command is simply that URL. There is no package to publish and no version to manage.
- What is the difference between dependencies and registryDependencies?
- dependencies are npm packages installed with the consumer's package manager. registryDependencies are other registry items, resolved recursively, where a bare name means the official registry and a full URL means anywhere. Declaring them honestly is what makes an install succeed in a clean project.
- Why does the item type matter?
- Because it decides which alias the file lands in: registry:ui goes to the UI folder, registry:block to components, registry:hook to hooks, registry:lib to lib. A hook typed as a UI item ends up in the components directory of every project that installs it.
- Can a registry be private or paid?
- Yes. Nothing in the format requires public JSON: the item URL can require a token as a query parameter or a header, and the CLI substitutes environment variables, so a team shares a configuration without sharing a key. A paid registry works the same way, with a public index and gated files.



