# shadcn Registry: Add Third-Party Components Safely

> Use a shadcn registry from another library in your shadcn/ui project: namespaced registries, auth headers, checking what it writes, and the files that clash.

- Author: [Serban Rusu](https://wingo-ui.com/blog/authors/serban), Founder of Wingo UI
- Published: Oct 13, 2026
- Category: [Component guides](https://wingo-ui.com/blog/category/components)
- Reading time: 8 min
- Canonical: https://wingo-ui.com/blog/shadcn-registry-third-party-components

## TL;DR

Add the library under registries in components.json as @name with a URL template, plus an Authorization header read from an environment variable if it has paid items, then install with npx shadcn@latest add @name/item. Before you run it, use shadcn view and add --dry-run to see every file it will create or overwrite. In an existing shadcn/ui project, keep your lib/utils.ts, watch for files with the same name such as button.tsx, and never paste another library's tokens over yours.

A shadcn registry is how components from other libraries reach a shadcn/ui project. You add the registry to `components.json`, run `npx shadcn@latest add @name/item`, and the CLI writes the files, installs npm packages and pulls in the items they depend on. That works well until a third-party item lands on a file you already have, a token it expects is missing, or a key it needs is not set. This tutorial covers the consumer side: what a registry item contains, how namespaced registries and auth headers work, how to see what an install will change before you run it, what clashes in an existing project, and when to move to a CLI that records versions.

We ran everything below on October 13, 2026 in a fresh Next.js 16.4 app set up with shadcn 4.21.4 (the radix-nova preset) and our own registry, Wingo UI release 1.8.0, with version 1.0.3 of our CLI. Building a shadcn custom registry of your own is a different job, and the [shadcn registry docs](https://ui.shadcn.com/docs/registry) cover it: you describe items in a `registry.json`, run `shadcn build` and host the JSON it writes.

## What is in a shadcn registry item?

One JSON file per item, in the `registry-item.json` schema that shadcn publishes. Our [Button](https://wingo-ui.com/components/button) is served at `/r/button.json`. Trimmed to the fields that decide what lands in your project:

```bash
curl -s https://wingo-ui.com/r/button.json \
  | jq '{type, dependencies, registryDependencies, files: [.files[] | {path, type}], cssVars, envVars}'
```

```json
{
  "type": "registry:ui",
  "dependencies": ["@radix-ui/react-slot@^1.3.3", "motion@^12.43.0"],
  "registryDependencies": [
    "https://wingo-ui.com/r/utils.json",
    "https://wingo-ui.com/r/motion.json",
    "https://wingo-ui.com/r/tones.json",
    "https://wingo-ui.com/r/ui-config.json"
  ],
  "files": [{ "path": "components/ui/button.tsx", "type": "registry:ui" }],
  "cssVars": null,
  "envVars": null
}
```

- `files` carry the full source in a `content` field, and `path` decides where it goes after the CLI applies your aliases.
- `dependencies` are npm packages the CLI installs.
- `registryDependencies` are other items, by name or URL, resolved recursively. This is where most surprises come from: one item can bring a whole tree.
- `cssVars` and `css` let an item add CSS variables and rules to your stylesheet. Ours leave them empty, which matters later.
- `envVars` are written to `.env.local` or `.env`; the [schema docs](https://ui.shadcn.com/docs/registry/registry-item-json) say existing variables are not overwritten.

## How do I add a shadcn namespaced registry?

List it in the components.json registries object, the `registries` key. The key of each entry is the namespace, and `{name}` in the URL becomes the item name. A registry with only free items needs nothing more:

```json
{
  "registries": {
    "@wingo-ui": "https://wingo-ui.com/r/{name}.json"
  }
}
```

For private or paid items, use the object form with a header. This is the form our [installation docs](https://wingo-ui.com/docs/installation#shadcn) give for Pro items:

```json
{
  "registries": {
    "@wingo-ui": {
      "url": "https://wingo-ui.com/r/{name}.json",
      "headers": { "Authorization": "Bearer ${WINGO_UI_API_KEY}" }
    }
  }
}
```

Put the key in `.env.local`, which the `.gitignore` from create-next-app already excludes:

```bash
# .env.local
WINGO_UI_API_KEY=wui_...
```

The [shadcn namespace docs](https://ui.shadcn.com/docs/registry/namespace) say `${VAR}` works in URLs, headers and `params`, and is expanded from the environment. Two behaviors we hit in testing:

- **No variable, no install.** With the header form and `WINGO_UI_API_KEY` unset, `npx shadcn@latest view @wingo-ui/button` stopped with "Registry "@wingo-ui" requires the following environment variables", even though the button is free. Keep the plain URL form until you have a key.
- **A paid item without the right key** fails with the server's status. `view @wingo-ui/calendar` on the plain URL returned "Failed to fetch from registry (402)", because the [Calendar](https://wingo-ui.com/components/calendar) is part of Wingo UI Pro.

## How do I install a component by name?

Name the namespace and the item. Several items can go in one command:

```bash
npx shadcn@latest add @wingo-ui/calendar
npx shadcn@latest add @wingo-ui/button @wingo-ui/ui-config
```

A full URL works for public items without any `components.json` entry: `npx shadcn@latest add https://wingo-ui.com/r/button.json`. The namespace form is better once you install more than one item, because you configure auth once and `shadcn search @wingo-ui -q "date"` can browse the registry.

The demo below is the Calendar that `add @wingo-ui/calendar` installs.

> Live demo (Calendar): Click a day to pick it, use the arrows to change the month (or swipe on a phone), and click the month name to jump to another month or year. Try it at [Calendar](https://wingo-ui.com/components/calendar) and install it with `npx wingo-ui@latest add calendar`.

## How do I check what a registry will write before running it?

Use three commands, cheapest first. All of them read the registry and write nothing.

```bash
npx shadcn@latest view @wingo-ui/button                    # metadata, dependencies, every file's source
npx shadcn@latest add @wingo-ui/button --dry-run           # the resolved file list: create or overwrite
npx shadcn@latest add @wingo-ui/button --diff lib/utils.ts # one file: yours against theirs
```

`--dry-run` is the one to read, because it resolves the whole dependency tree. In our project, which already had the shadcn/ui button and card, it printed:

```text
┌ shadcn add @wingo-ui/button (dry run)
│
├ Files (6) +4 new, ~2 overwrite
│ ~ lib/utils.ts              overwrite
│ + hooks/use-media-query.ts  create
│ + lib/tones.ts              create
│ + lib/ui-config.tsx         create
│ + lib/motion.ts             create
│ ~ components/ui/button.tsx  overwrite
│
├ Dependencies (4)
│ + clsx@^2.1.1
│ + tailwind-merge@^3.7.0
│ + motion@^12.43.0
│ + @radix-ui/react-slot@^1.3.3
│
⚠ 2 files will be overwritten.
```

One button, six files, two of them yours. `--view <path>` prints a file's content and `--diff <path>` compares it with what you have. Read every line marked `~` before you install, and commit first so `git diff` can undo anything.

## What clashes in an existing shadcn/ui project?

Four things, in the order you will meet them.

### lib/utils.ts

Most of our items depend on a `utils` item that writes `lib/utils.ts`, so the CLI asks "The file utils.ts already exists. Would you like to overwrite?" shadcn 4.21.4 set up our project with a one-line file, `export { cn } from "cn"`; ours is the older `clsx` plus `tailwind-merge` version. Answer no. Our components only import `cn` from `@/lib/utils`, and the Wingo UI button type-checked against shadcn's one-line file.

### Files with the same name

shadcn/ui ships 62 `ui` items as of 4.21.4, and third-party libraries reuse those names because they install into `components/ui` too. Our Button and shadcn's share `button.tsx` with different APIs: ours has `variant` values `solid`, `soft`, `outline`, `ghost` and `link`, and `tone="danger"` where shadcn uses `variant="destructive"`.

Dependencies make this bigger than it looks. The Calendar depends on our Button, so installing it also overwrites your button, and answering no leaves a calendar that passes `variant="soft"` and `radius="full"` to a Button that does not accept them. The [Data Table](https://wingo-ui.com/components/data-table) resolves to 47 items, itself included; 28 of them are files in `components/ui`, and 20 of those share a name with a shadcn/ui component, from `avatar.tsx` to `tooltip.tsx`.

The fix is a folder per library. Point the second library at its own `ui` alias, so `components/ui` stays shadcn's. Our [Claude Code shadcn setup](https://wingo-ui.com/blog/claude-code-shadcn-mcp-setup) walks through that layout with `components/wingo`.

### CSS variables

Both libraries define `--background`, `--accent`, `--radius` and other shared names, with different meanings:

- **`--accent`** is a light gray in shadcn/ui (`oklch(0.97 0 0)` in our radix-nova project), used for highlighted items. Our stylesheet defines it as an orange tint, and our components never use `bg-accent`.
- **`--radius`** is `0.625rem` in that project. Ours is `0.375rem`, kept for older code, and paired with `--radius-md: calc(var(--radius) - 2px)`, which makes `rounded-md` 4px.

Paste our tokens over yours and shadcn/ui's highlights turn orange while its corners shrink. The opposite problem appears with the shadcn CLI: our items set `cssVars` to null, so nothing adds the tokens they use. Classes like `shadow-raised` and `z-modal` then resolve to nothing. The safe merge is `npx wingo-ui@latest init`. In our project it added 43 variables, 17 dark values and 43 `@theme` mappings and changed no existing line, so `--accent` and `--radius` kept shadcn's values. Our components use fixed corner sizes (a medium button is `rounded-[10px]`), so your `--radius` does not change them.

### Base UI styles and asChild

shadcn styles named `base-*` use Base UI, and the CLI rewrites `asChild` into Base UI's `render` prop as it installs. We set `"style": "base-nova"` and ran `add @wingo-ui/dialog --dry-run --view components/ui/dialog.tsx`: `<DialogClose asChild>` came out as `<DialogClose render={<Button ... />}>`, but our Dialog still imports Radix, which has no `render` prop. In a Base UI project, install with our CLI, which writes files as published.

## When should you migrate from the shadcn CLI to the Wingo UI CLI?

When you want updates. The shadcn CLI records nothing about what it installed, so a later release can only reach your files through `add --overwrite`, which drops your edits. Our CLI writes `wingo-ui.json` with each item's version and keeps the installed copy in `.wingo-ui/base`, which is what lets `update` run a 3-way merge. To migrate from shadcn ui's CLI to ours for the items you installed from us, you do not reinstall anything:

```bash
npx wingo-ui@latest init         # wingo-ui.json, missing tokens, keeps lib/utils.ts
npx wingo-ui@latest add button   # record what you already have
npx wingo-ui@latest outdated
```

`init` does not take over `components.json`. It reads one field from it, `tailwind.css`, to find your stylesheet, takes the `@/*` alias from `tsconfig.json`, and writes its own folders to `wingo-ui.json`, so change `aliases.ui` there if our files live outside `components/ui`. It found shadcn's `export { cn } from "cn"` and kept it.

`add` then compares each file with the current release. In our project, where the button and its helpers had come in through `shadcn add @wingo-ui/button`, all five files reported `identical` and were recorded at 1.0.0, and `outdated` listed them as up to date.

A file you edited reports `conflict` instead and is not recorded. Save your copy, take ours with `--overwrite`, then put yours back:

```bash
cp components/ui/button.tsx /tmp/button.mine.tsx
npx wingo-ui@latest add button --overwrite
cp /tmp/button.mine.tsx components/ui/button.tsx
npx wingo-ui@latest diff button   # should show only your edits
```

That is exact when your copy came from the current release. If it is older, `diff` also shows the upstream changes you never received as if they were yours, so review them there.

While you are in there, move default-value edits out of the file. We had changed `radius = "md"` to `"full"` inside `button.tsx`; the same result as a [UIProvider](https://wingo-ui.com/components/ui-config) default leaves the file untouched, so the next update has nothing to merge:

```tsx
// app/providers.tsx
"use client";

import type { ReactNode } from "react";
import { UIProvider } from "@/lib/ui-config";

export function Providers({ children }: { children: ReactNode }) {
  return <UIProvider defaults={{ button: { radius: "full" } }}>{children}</UIProvider>;
}
```

How the merge itself works, and a git script that does the same for stock shadcn/ui files, is in our guide to [updating shadcn components without losing edits](https://wingo-ui.com/blog/update-shadcn-components-without-losing-edits). The commands are on the [updates docs page](https://wingo-ui.com/docs/updates).

## Where should I start?

Add the registry with the plain URL, run `npx shadcn@latest add @wingo-ui/button --dry-run` in your project and read the list it prints. If button.tsx shows up as an overwrite, choose a folder for the second library before you install anything. Calendar and Data Table are part of Wingo UI Pro, on the [pricing page](https://wingo-ui.com/pricing); the Button and UIProvider are free. For how the two libraries differ beyond the registry, read [Wingo UI vs shadcn/ui](https://wingo-ui.com/blog/wingo-ui-vs-shadcn), and more deep dives live under [Component guides](https://wingo-ui.com/blog/category/components).

## Components in this post

- [Button](https://wingo-ui.com/components/button) (Free): The React button component every screen starts with: five variants, six tones, all sizes, and a loading state that never jumps. Install: `npx wingo-ui@latest add button`
- [Calendar](https://wingo-ui.com/components/calendar) (Pro): A React calendar to pick one day, several days or a range, with disabled days, event dots, several months and a quick month / year jump. Install: `npx wingo-ui@latest add calendar`
- [Data Table](https://wingo-ui.com/components/data-table) (Pro): A React data table on TanStack Table: search, filters, sorting, bulk actions, pinning, resizing, virtualization and cards on phones. Install: `npx wingo-ui@latest add data-table`
- [UIProvider](https://wingo-ui.com/components/ui-config) (Free): A React config provider for Wingo UI: app-wide default props for every component, the Intl locale and a reduced-motion policy. Install: `npx wingo-ui@latest add ui-config`

## FAQ

### What is a shadcn registry?

A server that publishes components as JSON files in the shadcn registry-item format: the source of each file, the npm packages it needs and the other items it depends on. The shadcn CLI reads those files and writes the source into your project.

### How do I add a third-party registry to components.json?

Add an entry under registries, such as "@wingo-ui": "https://wingo-ui.com/r/{name}.json". For private or paid items use the object form with url and headers, and write the key as ${VAR} so the CLI reads it from the environment or .env.local.

### Why does the shadcn CLI fail with a missing environment variable error?

A registry in components.json uses ${VAR} in its URL or headers and the variable is not set. Set it in .env.local or your shell, or use the plain URL form while you only install free items.

### Should I let a third-party registry overwrite lib/utils.ts?

Usually no. If your file already exports cn, components that import cn from @/lib/utils keep working. In our test the Wingo UI button type-checked against shadcn's one-line lib/utils.ts that re-exports cn from the cn package.

### Can I switch from the shadcn CLI to a library's own CLI later?

Yes. With Wingo UI, run npx wingo-ui@latest init in the same project, then npx wingo-ui@latest add for the items you installed. Files that match the current release are recorded as installed, so later updates can merge into them.

---

Source: https://wingo-ui.com/blog/shadcn-registry-third-party-components
