From Registry Entry to Project Code

In shadcn/ui, add is not simply a download command. It is the point where a registry description, a dependency graph, and a project's own configuration meet. This chapter traces that operation from discovery to written source so the reader can see what information must travel with an item, why the CLI resolves more than the named file, and where preview and human review belong before project-owned code changes.

That process matters because ownership begins before a file appears in a source tree. A component arrives with assumptions about dependencies, imports, styling, and destination. If those assumptions are not represented, the receiving project must reconstruct them by hand. Shadcn/ui's registry and CLI make those assumptions explicit enough to resolve and adapt, while still leaving the resulting source visible to the project.

A registry item is a contract, not just a file

A registry has a catalog and items. The catalog gives a registry an identity and describes what it offers. An item identifies a resource and supplies the information needed to install it: its type, files, package dependencies, relationships to other registry items, and optional contributions to CSS, Tailwind configuration, tokens, environment variables, fonts, or documentation. That is a lot more context than a URL pointing to one component file, but each part answers a practical installation question.

The item type tells the CLI how to understand the resource. A small UI primitive, a multi-file block, a hook, a page, a font, and a design-system base do not all belong in the same destination or require the same handling. A page or arbitrary file can specify an explicit target, while a UI file can use a target derived from the consuming project's configuration. The type is therefore not a label for browsing alone. It participates in the placement decision.

Files carry the content boundary. Dependencies name packages that the project must install, while registry dependencies point to other items that the resolver must obtain. A block can therefore declare both kinds of relationship: it may need a package for a particular behavior and several smaller UI items that should be installed alongside it. CSS variables and CSS rules carry design-system effects that cannot be expressed by a component file alone. Environment variables can describe development configuration, and fonts can carry the metadata needed to install and wire them into a project.

The schema also separates where a file lives in the registry from where it should land in the project. A registry author can describe a source file relative to the registry and, when necessary, provide a target such as a route or configuration file. Target placeholders can refer to the user's configured component, UI, library, or hook directories. The registry can remain portable because it does not have to hard-code whether the consuming project uses src, a package workspace, or another supported alias arrangement.

Larger registries can be composed from nested registry definitions. The root catalog can include smaller catalogs and the build or loader process can flatten them into a resolved view. This lets maintainers organize a growing source registry without making consumers understand its internal organization. The important reader-facing result is simpler: a catalog entry is the public description, while the registry's internal file structure is an authoring concern.

The first-party registry illustrates why this contract is useful. A simple UI item may point to one source file and one package dependency. A larger dashboard block can point to a page, supporting components, data, and a long chain of UI dependencies. The consumer does not have to manually copy each part in the right order. But that convenience is possible only because the item records the relationships. The registry is carrying a small description of how the resource belongs to a project.

This contract also defines a limit. A valid item can tell the CLI what it claims to contain and where the installer may place it; it cannot prove that the implementation is well designed, secure, or appropriate for the receiving application. Descriptions and categories can improve discovery, but they are not independent review. The schema makes distribution more legible. It does not turn metadata into a quality certificate.

The add pipeline turns the contract into adapted source

The operation becomes easier to understand when its stages are kept distinct. A developer begins with an item name, a URL, a local item, a GitHub address, or a namespaced resource. The CLI resolves that address and determines which registry source should answer it. It then fetches the item representation, checks that it matches the expected structure, follows the registry dependencies, adapts the resolved result to the project, and either previews or writes the changes.

sequenceDiagram participant D as Developer participant C as shadcn CLI participant R as Registry participant V as Resolver participant P as Project D->>C: Request add item C->>P: Read project configuration C->>R: Fetch item payload R-->>C: Return registry item JSON C->>V: Validate and resolve item V->>R: Fetch declared dependencies R-->>V: Return dependency items V->>V: Order, merge, and deduplicate tree V-->>C: Return resolved installation tree C->>P: Transform and validate targets P-->>C: Preview or write files and CSS C-->>D: Report changes and notes

The first stage is source resolution. A bare built-in name is interpreted through the configured style and registry, while a namespace is resolved through the project's registry configuration. A direct URL or local item takes a different route. This distinction matters because the source identity must stay attached to the item: two registries may use the same item name, but they need not mean the same code or have the same authentication context.

The next stage is validation. The CLI parses the JSON and checks it against the registry item schema before it treats the payload as an installable resource. That check catches structural problems such as an invalid item type, a missing required target for a page or file, or a registry configuration that lacks the required name placeholder. It also gives later stages a known shape. Without that boundary, every part of the installer would have to defend against arbitrary data structures while deciding what to write.

Validation is not execution. The registry response is data that the resolver reads; the CLI does not run the source code merely to inspect the item. But the eventual result is still source code, configuration, and sometimes environment settings that the project may compile or use. The safe conclusion is therefore two-part: schema validation and target checks reduce particular installation risks, while review of the actual payload remains necessary.

Why the resolver builds a graph

A named item rarely stands alone. A login block may need a Button, a Card, an Input, a Label, and a Field. Those items may themselves refer to a primitive package or another registry item. If the CLI fetched only the first JSON object, the project would receive an incomplete design and the developer would have to discover and order the rest manually.

The resolver follows registryDependencies recursively. It tracks the source associated with each item, which helps preserve identity when names overlap across registries. It then orders the items so dependencies come before the resources that use them. A graph is the correct model because the same dependency may serve several branches, a resource can depend on items from different registries, and a block may need both project files and package dependencies before it is usable.

The graph also explains why installation order has consequences. The resolver deduplicates files by their resolved target paths and merges configuration contributions such as Tailwind data, CSS variables, CSS rules, and environment variables. If two resources target the same path or define the same configuration key, the result depends on the resolution and merge rules. The namespace documentation describes later resources overriding earlier content in relevant cases. That behavior can be useful: a team can build a custom item that starts from a vendor resource and supplies a local override. It can also be surprising when two unrelated resources happen to claim the same destination.

Source tracking helps avoid treating equal names as equal meanings, but it does not make collisions harmless. A custom button from one registry and a built-in button can represent different contracts. A dependency graph with a cycle or an ambiguous match may require explicit correction. The resolver can reduce repetitive work and establish an order; it cannot decide which competing design decision is correct for the product.

How project context changes the payload

After the graph is resolved, the CLI does not necessarily write the registry's source text unchanged. It uses the project's configuration to determine destinations and to adapt conventions. Import paths can be rewritten to match the project's aliases. A project using JavaScript rather than TypeScript can receive the corresponding file extension. React Server Component settings can affect client directives. CSS-variable choices, Tailwind prefixes, icon libraries, asChild conventions, RTL support, and font handling can all participate in transformation when the payload and project support them.

This is why the same registry item can be portable without being identical in every project. Its source describes the component's intent and dependencies; the project configuration supplies local paths and conventions. In a monorepo, target aliases can route a UI primitive to the shared UI workspace while a page or feature stays in the application. In a single package, the same item may resolve to a local UI directory. The payload is shared, but the ownership destination is project-specific.

Before writing, the installer validates registry-controlled targets. A file target must remain within an acceptable project location and must not use a path that escapes the intended root. This is an important safety boundary, but it is narrower than code review. A safe destination can still contain code the team does not want. The check answers “where may this be written?” not “should this implementation be trusted?”

The final write is staged across several kinds of project change. Package dependencies must be updated, configuration and environment contributions considered, source files transformed and placed, and CSS written after the component files so a watcher sees a coherent rebuild. The exact order matters because a project can otherwise enter an intermediate state in which imports, dependencies, and styling disagree. The CLI is coordinating an ordered sequence, even though the result is ultimately a set of ordinary files and manifest changes.

Why preview belongs before ownership changes

The CLI provides dry-run, diff, and view modes because the transformation step deserves inspection. A preview can report which files would be created, overwritten, or skipped, which dependencies would be added, and what CSS, environment, or font changes are proposed. A diff can reveal that the registry source has been adapted to the project in a way that is technically valid but undesirable. A view can expose the payload before it is accepted.

These modes are especially important in an ownership-oriented system. With a runtime package, an update may be a version change in a manifest. With local source, the update may replace or merge code that a team has edited. The CLI can identify a content difference, but it cannot infer whether the local change was intentional, whether the new behavior is compatible with product requirements, or whether a registry's design choice should win. Version control and review supply that missing judgment.

The whole pipeline therefore has a clear boundary. The registry describes a resource; the resolver assembles its dependencies; the CLI adapts it to project context; the installer validates destinations and applies changes; the team owns the decision to accept the result. Once the reader sees that boundary, the next problem is no longer how the item travels. It is how the project's tokens, aliases, and workspace configuration make the adapted code fit the design system it has just entered.

References