The shadcn registry, explained

A shadcn registry is usually explained as a way to distribute components. It is also the cleanest way to distribute a design system, and that second use is the one almost nothing is written about. Here is both, with a real theme item you can inspect.

Updated July 27, 2026

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 isWho reads it
registry.jsonThe index: a name, a homepage, and the list of items you publishThe build step that emits your item files; registry explorers and the CLI when resolving a namespace
registry-item.jsonOne installable thing: its files, dependencies, and any CSS, Tailwind or font it bringsThe CLI, on every shadcn add
The two schemas, and what each is responsible for.

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:ui for a component, registry:block for a composed multi-file block, registry:theme for a token set, registry:style for a whole starting style, plus hook, lib, page and file variants.
  • `files` — the source that gets written, each with its own type and optional target path.
  • `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, light and dark.
  • `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."
}
A registry:theme item. 27 theme variables, 28 light, 28 dark — the full item is at /r/ambient-sage.json.

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 render

Ambient Sage's actual tokens — the same values its exports use.

Color tokensSemantic roles with HEX / HSL / CMYK

Color tokens

Ambient Sage

light · HEX · HSL · CMYK

Core

#F3F4EF

background

H 72 · C0, 0, 2, 4

#1A1C17

foreground

H 84 · C7, 0, 18, 89

#E5E6E0

card

H 70 · C0, 0, 3, 10

#ECEEE8

muted

H 80 · C1, 0, 3, 7

#D8D9D2

border

H 69 · C0, 0, 3, 15

Brand

#FEE951

primary

H 53 · C0, 8, 68, 0

#1A1C17

primary-fg

H 84 · C7, 0, 18, 89

#E5E6E0

secondary

H 70 · C0, 0, 3, 10

#F7E464

accent

H 52 · C0, 8, 60, 3

#FEE951

ring

H 53 · C0, 8, 68, 0

Semantic

#C0392B

destructive

H 6 · C0, 70, 78, 25

#FFFFFF

destructive-fg

H 0 · C0, 0, 0, 0

#2D7238

success

H 130 · C61, 0, 51, 55

#C97D12

warning

H 35 · C0, 38, 91, 21

#545651

muted-fg

H 84 · C2, 0, 6, 66

Charts

#FEE951

chart-1

H 53 · C0, 8, 68, 0

#4A8FD4

chart-2

H 210 · C65, 33, 0, 17

#6BBF8A

chart-3

H 142 · C44, 0, 28, 25

#E07498

chart-4

H 340 · C0, 48, 32, 12

#E8A24B

chart-5

H 33 · C0, 30, 68, 9

Type scaleHeading, body, and mono in the kit's fonts

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

400500600700

Aa

Plus Jakarta Sans · Body

400500600700

ABCDEFGHIJKLM NOPQRSTUVWXYZ

abcdefghijklmnopqrstuvwxyz

0123456789 & @ # % →

Radius & spacingCorner radius, elevation, and spacing steps

Tokens

Ambient Sage primitives

density: balanced

Radius scale

sm · 0.375rem
md · 0.75rem
lg · 1.25rem
xl · 1.75rem

Component radius

button
card
input

Elevation

level 1
level 2
level 3
level 4

Spacing · base 4px

1x
2x
3x
4x
6x
8x
The same item, rendered. Every value above is a real token in a live registry item, not an illustration.

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.json
One URL carries the whole system.

Publishing your own, minimally

  1. 1

    Write one item file

    Start with a single registry-item.json. You do not need registry.json, a build step, or a monorepo to be useful — an index matters once you have several items and want a namespace.

  2. 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. 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. 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.