/

How to Add a shadcn Component With the CLI

It fetches source, not a package. What the four resolution steps are, which flags matter, and the collision that happens when two registries ship the same file name.

Serafim Korablev
Serafim Korablev
@korablev

npx shadcn add looks like a package install and is not one. It fetches source files and writes them into your repository, resolving their dependencies as it goes. Once you understand that one sentence, the whole model - why there is no version to bump, why two registries can collide, why the code is yours - follows from it.

The first run

Before anything can be added, a project needs components.json. npx shadcn@latest init writes it, asking about your style, your base colour and whether you use CSS variables.

The part worth reading rather than accepting is the aliases block. It maps components, ui, lib, hooks and utils to real paths in your project, and every file the CLI writes uses those aliases for its imports. If your project puts UI primitives somewhere unusual, this is where you say so, and getting it right now saves rewriting import paths in every component you ever add.

Adding one

bash

That takes the component from the official registry. The more interesting form is a URL, which is how everything outside the official set is installed:

bash

Quote the URL. A shell will split on the & in a query string and you will get a confusing error about a missing file. That single detail is behind a large share of "the CLI is broken" reports.

Several at once works, and mixing sources in one command works too, because each argument is resolved independently.

What actually happens

Four steps, and knowing them makes every failure legible.

The CLI fetches the item's JSON, which lists its files, its npm dependencies and its registry dependencies. It installs the npm packages with your package manager. It resolves the registry dependencies recursively, which is how adding one card can also add button and badge. Then it writes the files to the paths your aliases define, transforming the import statements to match.

If it fails, it is almost always one of three things: the URL was unquoted, the item declares a dependency the registry does not serve, or a file already exists.

The flags that matter

--overwrite. Without it, an existing file is left alone, which is why re-adding a component you have edited appears to do nothing. With it, your edits are gone. Commit before using it.

--path. Writes to a specific directory rather than the alias default. Useful when you want a component in a feature folder instead of components/ui.

--yes and --silent. For scripts and CI, where an interactive prompt is a hang.

--cwd. For monorepos, where the command needs to run against a specific package rather than the repository root.

The collision

The one real hazard of running more than one registry. add writes into the paths your aliases define, so two registries that both ship a button.tsx will overwrite each other, silently, in whichever order you installed them.

Two habits prevent every version of this. Read the diff after every add, so you see exactly which files were touched. And when you know two sources overlap, install the second with --path into its own folder rather than letting it land on top of the first.

Keys and private registries

A registry can require authentication, which is done with a query parameter or a header depending on the registry. Ours takes an API key in the URL, and the pattern that keeps it out of your shell history and your commits is an environment variable:

bash

The same substitution works inside components.json when you register a source there, which is how a team shares one configuration without sharing one key.

After the install

The component is now your file. Nothing will update it, nothing will conflict with it, and nobody will fix its bugs but you. That is the trade the whole model makes, and it is worth two habits.

Normalise the tokens in the same commit, while the file is fresh: an installed component arrives with someone else's radius, spacing and colour values, and adapting it later means adapting everything that copied it. And read what it actually does before shipping it, because the component checklist applies to a borrowed component exactly as it does to a generated one.

Where else to look

The honest list, because the answer is not always us:

SourceBest forTrade-off
shadcn CLI docsThe canonical flags and the components.json referenceOfficial registry examples only
The registry directoryFinding sources that install through the same commandOnly registries that publish an index
21st MCPThe same installs from inside the editor, with previewsInstalls require a membership
Copying files by handTotal control, no CLI at allYou resolve every dependency yourself

Taking one

bash

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

What does npx shadcn add actually do?
Four steps: it fetches the item's JSON, installs the npm packages it declares, resolves its registry dependencies recursively, and writes the files to the paths your components.json aliases define, rewriting imports to match. It is a source fetch, not a package install, which is why there is no version to bump.
Why does my install command fail with a missing file error?
The URL was probably unquoted. A shell splits on the ampersand in a query string, so half the URL becomes a second command. Always quote it, and use double quotes if the URL contains an environment variable, because single quotes send the variable name literally.
Why does re-adding a component do nothing?
Because the file already exists and the CLI leaves existing files alone. That is deliberate: it protects your edits. Use --overwrite to replace it, and commit first, because your changes go with it.
Can two registries overwrite each other's components?
Yes, silently. add writes into the paths your aliases define, so two registries that both ship button.tsx will collide in whichever order you installed them. Read the diff after every add, and use --path to send an overlapping source into its own folder.

Published

Aug 20, 2026

Read time

5 min

Tags

How-toCLIshadcnReact

Share