What a registry is
A shadcn registry is JSON served over HTTP. That is the whole idea. There is no package to publish, no runtime, no service to sign up for: you host files at URLs, and the shadcn CLI fetches one, reads what it declares, and writes the result into the consuming project.
That is what makes it different from a component library. A library is a dependency — you install it, you import from it, and its code lives behind a version number in node_modules. A registry hands you source. After shadcn add, the files are yours: in your repo, in your diff, editable without a fork. The trade is the usual one — you own the maintenance and you lose automatic updates — and it is the trade the whole shadcn approach is built on.
"Registry" means two things in conversation
People say it for both the *index* (a collection you point the CLI at, with a namespace) and a single *item* (one installable thing at one URL). The docs keep them separate — registry.json for the first, registry-item.json for the second — and mixing them up is why setup guides seem to contradict each other.
The two files
| What it is | Who reads it | |
|---|---|---|
registry.json | The index: a name, a homepage, and the list of items you publish | The build step that emits your item files; registry explorers and the CLI when resolving a namespace |
registry-item.json | One installable thing: its files, dependencies, and any CSS, Tailwind or font it brings | The CLI, on every shadcn add |
You can publish a single item and never write an index at all. That is the fastest useful thing you can do with a registry, and it is what the rest of this guide builds toward — but the fields are worth knowing first.
The fields of registry-item.json that matter
- `name` — the identifier used in install commands and in
registryDependencies. - `type` — what kind of thing this is:
registry:uifor a component,registry:blockfor a composed multi-file block,registry:themefor a token set,registry:stylefor a whole starting style, plus hook, lib, page and file variants. - `files` — the source that gets written, each with its own
typeand optionaltargetpath. - `dependencies` — npm packages the item needs, installed for you.
- `registryDependencies` — other registry items this one requires, by name or URL. The CLI resolves them, which is why installation order sorts itself out.
- `cssVars` — CSS custom properties, split into
theme,lightanddark. - `css` — arbitrary CSS rules, for anything variables cannot express.
- `font` — fonts the item needs, so type is not a separate manual step.
- `categories`, `docs`, `meta` — metadata for explorers, post-install instructions, and anything of your own.
The three that get overlooked are cssVars, css and font, because almost every tutorial demonstrates a component. Those three are what turn a registry from a component-distribution mechanism into a design-system-distribution mechanism.
A registry item does not have to contain a component. It can contain a design system.
Shipping a whole theme as one item
Here is a real one, live right now — the item behind the free Ambient Sage kit, trimmed for length. Nothing in it is a component:
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "ambient-sage",
"type": "registry:theme",
"title": "Ambient Sage",
"description": "A warm-sage neutral-surface kit with a single vivid yellow accent.",
"categories": ["minimal", "warm-neutral", "flat", "calm", "yellow-accent"],
"cssVars": {
"theme": {
"font-heading": "'Plus Jakarta Sans', sans-serif",
"font-mono": "'JetBrains Mono', monospace",
"radius-button": "0.75rem",
"radius-card": "1.25rem",
"shadow-sm": "none",
"duration": "180ms",
"ease": "cubic-bezier(0.4,0,0.2,1)"
},
"light": {
"background": "72 19% 95%",
"foreground": "84 10% 10%",
"card": "70 11% 89%",
"primary": "54 98% 66%"
},
"dark": {
"background": "84 10% 10%",
"foreground": "68 13% 88%",
"card": "80 9% 14%"
}
},
"font": { "heading": "Plus Jakarta Sans", "mono": "JetBrains Mono" },
"docs": "Use the semantic tokens; do not add a second accent."
}Three things are worth noticing. cssVars.theme carries the non-colour half of a design system — radii, shadows, motion timing, type faces — which is where most "theme generators" stop and start losing the design. light and dark are separate objects rather than one palette and an inversion. And docs carries a written instruction, which the CLI surfaces after install and a coding agent can read.
Token specimen · real values
Ambient Sage
Live renderAmbient Sage's actual tokens — the same values its exports use.
Color tokens
Ambient Sage
Core
background
H 72 · C0, 0, 2, 4
foreground
H 84 · C7, 0, 18, 89
card
H 70 · C0, 0, 3, 10
muted
H 80 · C1, 0, 3, 7
border
H 69 · C0, 0, 3, 15
Brand
primary
H 53 · C0, 8, 68, 0
primary-fg
H 84 · C7, 0, 18, 89
secondary
H 70 · C0, 0, 3, 10
accent
H 52 · C0, 8, 60, 3
ring
H 53 · C0, 8, 68, 0
Semantic
destructive
H 6 · C0, 70, 78, 25
destructive-fg
H 0 · C0, 0, 0, 0
success
H 130 · C61, 0, 51, 55
warning
H 35 · C0, 38, 91, 21
muted-fg
H 84 · C2, 0, 6, 66
Charts
chart-1
H 53 · C0, 8, 68, 0
chart-2
H 210 · C65, 33, 0, 17
chart-3
H 142 · C44, 0, 28, 25
chart-4
H 340 · C0, 48, 32, 12
chart-5
H 33 · C0, 30, 68, 9
Typography
Ambient Sage
Scale: compact-product
Density: balanced
Heading · Plus Jakarta Sans · 1.875rem
Sample headline
Subheading · Plus Jakarta Sans · 1.375rem
A warm-sage neutral-surface mobile kit with a single vivid yellow accent, flat tonal cards, and oversized display numerals.
Body · Plus Jakarta Sans · 1rem
Ambient Sage uses a near-white warm-sage canvas (#f3f4ef) with card panels distinguished only by a tonal shift to #e5e6e0, never by shadows or borders. A single vivid yellow (#fee951) is the only saturated color and appears sparingly at component scale as orbs, button fills, and focus rings. Primary data values render as oversized bold hero numerals with a small superscript unit. Typography is a friendly rounded geometric (Plus Jakarta Sans) with no uppercase and no tight tracking, while JetBrains Mono is reserved for hex codes and technical strings. Generous rounding and luminance-only contrast give the whole system a calm, minimal feel.
Mono · JetBrains Mono · 0.8125rem
npx shadcn add ambientsage.json
Aa
Plus Jakarta Sans · Heading
Aa
Plus Jakarta Sans · Body
ABCDEFGHIJKLM NOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
0123456789 & @ # % →
Tokens
Ambient Sage primitives
Radius scale
Component radius
Elevation
Spacing · base 4px
Installing it is one command, and it repaints an existing project rather than adding to it: every component already referencing bg-primary or text-muted-foreground picks up the new values without being edited.
npx shadcn add https://identityforge.io/r/ambient-sage.jsonPublishing your own, minimally
- 1
Write one item file
Start with a single
registry-item.json. You do not needregistry.json, a build step, or a monorepo to be useful — an index matters once you have several items and want a namespace. - 2
Serve it at a stable URL
Any static host works. What matters is that the URL does not move: it is the install command, so it ends up pasted in READMEs, prompts and agent configs.
https://identityforge.io/r/<slug>.json - 3
Set the right content type and CORS
Serve
application/json. If anything in a browser will fetch it — a registry explorer, a preview tool — send permissive CORS headers too. This is the most common reason an item that looks correct will not install. - 4
Test with the real CLI
Install into a scratch project before you publish the URL anywhere. A schema mistake surfaces here in seconds and in a bug report in weeks.
npx shadcn add https://example.com/r/thing.json
Version the URL, not the file
Because the URL is the contract, changing what it returns changes what everyone installs next. If you need a breaking change, publish a new path and leave the old one serving. Silently editing an item in place is the registry equivalent of force-pushing.
Namespaces and private registries
A raw URL is fine for one item. Once you publish several, a namespace is nicer: configure the registry once in components.json and install by short name, with multiple registries side by side.
Private registries are supported and the CLI accepts four authentication shapes: a bearer token (OAuth 2.0), an API key, basic authentication, and a query parameter. Which you use is a hosting decision, not a shadcn one.
The query-parameter option is the one to think twice about
A token in a URL ends up in shell history, in CI logs, in the components.json someone commits, and in any proxy log along the way. Prefer a bearer token or an API key header, and if you must use a query parameter, treat that token as compromised the moment it is used and rotate it on a schedule.
How installation order gets resolved
registryDependencies lets an item depend on other items, including across registries. The CLI resolves the graph and installs in dependency order, so a block that needs a button gets the button first, whether or not you asked for it.
The failure mode to know about is a cycle: two items that each declare the other. Nothing resolves, and the error points at the resolution step rather than at your JSON. If an install hangs or fails on an item that looks fine, draw the dependency arrows on paper before debugging anything else.
Where to find public registries
This is the most-asked and least-answered question about registries, and it has three real answers. shadcn maintains an open-source registry index of registries available out of the box. registry.directory is an independent explorer that lets you browse and preview items before installing. And a growing number of individual projects — this site included — publish a stable item URL and simply document it.
The third category is easy to overlook and worth checking first: if a project you already like has a theme or a component set you want, look for a /r/ path before assuming you have to copy CSS by hand.
Why this matters for agent-built UI
A coding agent given three brand colours has three values and roughly twenty-five undefined ones: hover and active states, muted text, borders, ring, destructive, chart series, and every one of those again in dark mode. It fills them competently and differently each time, which is exactly the drift a design system is supposed to prevent.
A registry item closes that gap in one command, and it is portable in a way nothing else in this space is: v0 accepts a registry as a design-system source, Bolt can install one from its in-browser terminal, and Cursor or Claude Code can run the same command in your repo. One URL, four tools, no per-tool integration. The written half — what never to do, which variant a destructive action gets — belongs alongside it in a DESIGN.md, because cssVars answers *what colour* and only prose answers *what never*.
Inspect a real theme registry item
Every public kit here publishes a stable registry-item.json with 27 theme variables and 28 semantic roles in light and dark. Install one, or read the JSON to see how a theme item is put together.
FAQ
What is a shadcn registry?
A set of JSON files served over HTTP that the shadcn CLI can install from. registry.json is the index of what you publish; each registry-item.json describes one installable item — its files, dependencies, and optionally CSS variables, Tailwind config and fonts. There is no package to publish and no service to sign up for.
Can a registry item install a theme instead of a component?
Yes. Use type: registry:theme and put your tokens in cssVars, split into theme, light and dark. Add font for typefaces and css for anything variables cannot express. The item then repaints an existing project rather than adding files to it, because components already referencing semantic tokens pick up the new values.
Do I need to publish to npm?
No. A registry is served over HTTP, so any static host works. npm is a different distribution model with different trade-offs — a versioned dependency in node_modules rather than source written into your repo.
Where can I find publicly available shadcn registries?
Three places: shadcn's open-source registry index of registries available out of the box, registry.directory as an independent explorer for browsing and previewing items, and individual projects that publish a stable item URL and document it. Check the third before assuming you have to copy CSS by hand.
How does the CLI decide what order to install things in?
registryDependencies declares which other items an item needs, and the CLI resolves that graph and installs in dependency order. The failure case is a cycle — two items each declaring the other — which surfaces as a resolution error rather than a JSON error, so check the dependency arrows first.
Can a registry be private?
Yes. The CLI supports a bearer token (OAuth 2.0), an API key, basic authentication, and a query parameter. Prefer a header-based method: a token in a query string ends up in shell history, CI logs and any proxy log in between.
Why won't my registry item install?
Most often the response is not served as application/json, or a browser-based tool is blocked by missing CORS headers. After that, check the item against the registry-item.json schema, and test with npx shadcn add <url> against a scratch project before publishing the URL anywhere.