Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

shadcn/ui — Administrator & Developer Guide

Generated May 2026 from shadcn-ui/ui at b9f78c8

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

This guide covers the complete internals of shadcn/ui (v4.3.0) — a CLI-driven component registry system that copies React UI component source code directly into your project. Unlike traditional component libraries installed as npm dependencies, shadcn/ui fetches component files from a remote registry, applies a chain of AST-based transforms to match your project's configuration, and writes the results into your source tree. You own the code from the moment it lands.

The system is organized as a pnpm monorepo managed by Turborepo. The primary deliverable is the shadcn npm package (a CLI tool), accompanied by a Next.js documentation website, integration test suites, and project scaffolding templates for ten framework configurations. Components are built on either Radix UI (v1.4.3) or Base UI (@base-ui/react v1.3.0) primitives and styled with Tailwind CSS 4.x.

This document is written for administrators deploying custom registries, developers contributing to the codebase, and architects evaluating the system's design. Written from source code.


1. Architecture Overview

1.1 What the System Does

shadcn/ui solves a specific problem in the React ecosystem: developers want pre-built, accessible UI components but also want full control over the source code. Traditional npm-installed libraries create a dependency you cannot easily customize. shadcn/ui flips this model — the shadcn CLI fetches component source from a registry, rewrites imports and styles to match your project's aliases and configuration, then writes the files directly into your codebase. After that, the CLI is no longer involved; the components are yours to modify.

The registry system supports multiple "bases" (Radix UI and Base UI), multiple style variants, icon library swapping, RTL transforms, CSS variable theming, Tailwind prefix handling, and monorepo-aware file routing. A preset system allows encoding an entire design configuration into a compact string or named preset, enabling one-command project setup.

The CLI also implements the Model Context Protocol (MCP), allowing AI coding tools (Claude Code, Cursor, VS Code Copilot, Codex, OpenCode) to programmatically add components to projects.

1.2 Component Inventory

Component Path Role
CLI Package packages/shadcn/ Published npm package; all user-facing commands
Documentation Website apps/v4/ Next.js 16 site with MDX docs and live examples
Component Registry Source apps/v4/registry/ Source files for all registry components
Integration Tests packages/tests/ End-to-end tests that build registry + run CLI
Project Templates templates/ Scaffolding templates for 10 framework configurations
AI Agent Skills skills/shadcn/ Rules and instructions for AI agents

1.3 Technology Stack

Layer Technology Version
Language TypeScript (ESM) —
CLI Framework Commander.js —
Build (CLI) tsup —
Build (Monorepo) Turborepo —
Package Manager pnpm 9.0.6
UI Primitives Radix UI 1.4.3
UI Primitives (alt) Base UI (@base-ui/react) 1.3.0
Styling Tailwind CSS 4.x
Validation Zod —
Config Loading cosmiconfig —
Website Framework Next.js 16.1.6 (Turbopack)
Documentation Engine Fumadocs (MDX) —
Testing Vitest —
Code Transform Babel, ts-morph 26.0, recast —
HTTP Client node-fetch + https-proxy-agent —
MCP Server @modelcontextprotocol/sdk —
Charts Recharts 3.8.0
State Management Jotai, nuqs —
Package Publishing Changesets —

1.4 Data Flow

The core operation — adding a component — follows this pipeline:

User runs: npx shadcn add button
        │
        ▼
┌─────────────────────┐
│  1. Config Loading   │  cosmiconfig reads components.json
│     (get-config)     │  Validates against Zod schema
└─────────┬───────────┘
          ▼
┌─────────────────────┐
│  2. Pre-flight       │  Check framework, TypeScript, Tailwind,
│     Checks           │  package manager, RSC support
└─────────┬───────────┘
          ▼
┌─────────────────────┐
│  3. Registry         │  Fetch item from https://ui.shadcn.com/r
│     Resolution       │  Recursively resolve all dependencies
└─────────┬───────────┘
          ▼
┌─────────────────────┐
│  4. Transform        │  9-stage pipeline rewrites each file:
│     Pipeline         │  imports → RSC → CSS vars → TW prefix →
│                      │  icons → menu → RTL → font → cleanup
└─────────┬───────────┘
          ▼
┌─────────────────────┐
│  5. Write Phase      │  Validate targets (isSafeTarget)
│                      │  Update dependencies (npm/pnpm/yarn/bun)
│                      │  Update tailwind config
│                      │  Update env vars
│                      │  Update fonts
│                      │  Write transformed files
│                      │  Update CSS (last — triggers rebuild)
└─────────────────────┘

2. System Requirements

2.1 Runtime Requirements

Requirement Minimum Notes
Node.js >= 20 Required for ESM support and native fetch
Package Manager pnpm 9.0.6, npm, yarn, or bun pnpm required for monorepo development; users may use any supported manager
Git Any recent version Required for shadcn init post-init step
Operating System macOS, Linux, Windows Node.js cross-platform

2.2 Supported Frameworks

The CLI detects and supports these frameworks via the project detection system in packages/shadcn/src/utils/get-project-info.ts:

Framework Template Variants
Next.js Single app, Monorepo
Vite (React) Single app, Monorepo
React Router Single app, Monorepo
TanStack Start Single app, Monorepo
Astro Single app, Monorepo

Note

Framework detection examines package.json dependencies, configuration files (e.g., next.config.js, vite.config.ts, astro.config.mjs), and directory structure to determine the active framework. The detection is automatic during shadcn init.

2.3 Development Requirements

For contributing to the shadcn/ui monorepo itself:

Requirement Detail
pnpm 9.0.6 (enforced via packageManager field)
Node.js >= 20
Turbo Installed via pnpm (devDependency)
Disk Space ~2 GB (node_modules across all workspaces)

3. Configuration Reference

3.1 components.json — Complete Schema

The components.json file is the project-level configuration for shadcn/ui. It is created by shadcn init and read by every subsequent command via cosmiconfig. The schema is validated by Zod in packages/shadcn/src/schema/.

Field Type Default Description
$schema string "https://ui.shadcn.com/schema.json" JSON Schema URL for editor autocompletion
style string — Active style variant name (e.g., "new-york-v4")
rsc boolean — Whether the project uses React Server Components
tsx boolean — Whether to output .tsx files (false = .jsx)
tailwind.config string — Path to Tailwind config file
tailwind.css string — Path to the global CSS file
tailwind.baseColor string — Base color for theming (e.g., "zinc", "slate")
tailwind.cssVariables boolean — Whether to use CSS custom properties for theming
tailwind.prefix string "" Tailwind class prefix (e.g., "tw-")
aliases.components string — Import alias for components (e.g., "@/components")
aliases.utils string — Import alias for utility functions
aliases.ui string — Import alias for UI components (defaults to {components}/ui)
aliases.lib string — Import alias for lib directory
aliases.hooks string — Import alias for hooks directory
iconLibrary string — Icon library identifier (e.g., "lucide", "icons")
rtl boolean false Enable right-to-left layout transforms
menuColor string "default" Menu color variant: "default", "inverted", "default-translucent", "inverted-translucent"
menuAccent string "subtle" Menu accent style: "subtle", "bold"
registries Record<string, string | { url: string, headers?: Record<string, string> }> — Custom registry definitions

Important

The registries field supports two forms. A simple string is treated as the registry URL. An object form allows specifying url and optional headers (useful for private registries requiring authentication). URLs support {name} and {style} template placeholders.

Example components.json

{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "new-york-v4",
  "rsc": true,
  "tsx": true,
  "tailwind": {
    "config": "tailwind.config.ts",
    "css": "app/globals.css",
    "baseColor": "zinc",
    "cssVariables": true,
    "prefix": ""
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils",
    "ui": "@/components/ui",
    "lib": "@/lib",
    "hooks": "@/hooks"
  },
  "iconLibrary": "lucide",
  "rtl": false,
  "registries": {
    "acme": "https://components.acme.com/r"
  }
}

3.2 Environment Variables

Variable Purpose Where Used
REGISTRY_URL Override the default registry base URL (https://ui.shadcn.com/r) packages/shadcn/src/registry/constants.ts
https_proxy Route all registry HTTP fetches through a proxy packages/shadcn/src/registry/fetcher.ts
SHADCN_TEMPLATE_DIR Override the template directory for project scaffolding packages/shadcn/src/utils/scaffold.test.ts
SHADCN_GITHUB_URL Override the GitHub URL used during scaffolding packages/shadcn/src/utils/scaffold.test.ts
npm_config_user_agent Read by the CLI to detect which package manager invoked it packages/shadcn/src/utils/get-package-manager.ts

Note

The SHADCN_TEMPLATE_DIR and SHADCN_GITHUB_URL variables are primarily used in test environments. The REGISTRY_URL variable is the recommended way to point the CLI at a custom or self-hosted registry during development or in air-gapped environments.

3.3 Registry URL Configuration in components.json

Registry URLs in the registries field support environment variable expansion using ${VAR} syntax. This allows storing tokens or dynamic segments in environment variables rather than hardcoding them:

{
  "registries": {
    "private": {
      "url": "https://registry.internal.com/r",
      "headers": {
        "Authorization": "Bearer ${REGISTRY_TOKEN}"
      }
    }
  }
}

The builder resolves ${REGISTRY_TOKEN} from process.env at fetch time. If the variable is not set, the CLI raises RegistryMissingEnvironmentVariablesError.

3.4 CLI Commands and Options — Complete Reference

The CLI registers 12 commands in packages/shadcn/src/index.ts. Below is every command with every option, written from the Commander.js definitions in source.

3.4.1 shadcn init

Initialize a new shadcn/ui project or reconfigure an existing one.

Option Alias Type Default Description
--template -t string — Project template to use
--base -b string — Base UI library (radix or base)
--monorepo / --no-monorepo — boolean — Enable/disable monorepo mode
--preset -p string — Named preset or preset code
--yes -y boolean false Skip confirmation prompts
--defaults -d boolean false Use default configuration
--force -f boolean false Overwrite existing configuration
--cwd -c string . Working directory
--name -n string — Project name (for new projects)
--silent -s boolean false Suppress output
--css-variables / --no-css-variables — boolean — Enable/disable CSS variables
--rtl / --no-rtl — boolean — Enable/disable RTL mode
--reinstall / --no-reinstall — boolean — Reinstall dependencies

3.4.2 shadcn add

Add components to a project from the registry.

Option Alias Type Default Description
--yes -y boolean false Skip confirmation prompts
--overwrite -o boolean false Overwrite existing files
--all -a boolean false Add all available components
--path -p string — Custom path for component files
--silent -s boolean false Suppress output
--dry-run — boolean false Show what would be added without writing
--diff — boolean false Show diff of changes
--view — boolean false View component source without adding
--cwd -c string . Working directory

3.4.3 shadcn apply

Apply a preset to an existing project.

Option Alias Type Default Description
--preset — string — Preset name or code to apply
--yes -y boolean false Skip confirmation prompts
--silent -s boolean false Suppress output
--cwd -c string . Working directory

3.4.4 shadcn build

Build a custom component registry from local source.

Option Alias Type Default Description
--output -o string ./public/r Output directory for the built registry
--cwd -c string . Working directory

3.4.5 shadcn diff

Show differences between local components and registry versions.

Option Alias Type Default Description
--yes -y boolean false Skip confirmation prompts
--cwd -c string . Working directory

Warning

The diff command is deprecated. It remains functional but may be removed in a future release.

3.4.6 shadcn docs

Display documentation for components.

Option Alias Type Default Description
--json — boolean false Output as JSON
--cwd -c string . Working directory
--base -b string — Base library filter

3.4.7 shadcn info

Display project configuration information.

Option Alias Type Default Description
--json — boolean false Output as JSON
--cwd -c string . Working directory

Search the component registry.

Option Alias Type Default Description
--query -q string — Search query
--limit -l number — Maximum results to return
--offset -o number — Offset for pagination
--cwd -c string . Working directory

3.4.9 shadcn migrate

Run migrations (icon library, radix-to-base, RTL, etc.).

Option Alias Type Default Description
--list -l boolean false List available migrations
--yes -y boolean false Skip confirmation prompts
--cwd -c string . Working directory

3.4.10 shadcn mcp

Start the MCP (Model Context Protocol) server.

Option Alias Type Default Description
--cwd -c string . Working directory

Subcommand: shadcn mcp init

Option Alias Type Default Description
--client — string — Target MCP client (claude, cursor, vscode, codex, opencode)

3.4.11 shadcn view

View component source code from the registry.

Option Alias Type Default Description
--cwd -c string . Working directory

3.4.12 shadcn registry

Registry management commands.

Option Alias Type Default Description
--cwd -c string . Working directory

3.5 Workspace Configuration (Monorepo)

In monorepo mode, the CLI detects workspace roots by examining the package manager's workspace configuration (pnpm-workspace.yaml, package.json#workspaces). It then:

  1. Creates a root-level components.json with shared settings.
  2. Routes component files to the correct workspace package based on type:
    • registry:ui items route to the UI package
    • registry:hook items route to the hooks package
    • Other items route to the consuming application
  3. Propagates design settings (menuColor, menuAccent, rtl, iconLibrary) to workspace-level components.json files.

4. Deployment & Scaling

4.1 Building the CLI Package

The CLI is built with tsup (a TypeScript bundler based on esbuild). The build configuration lives in the packages/shadcn/ workspace.

# From monorepo root
pnpm --filter shadcn build

This produces a single ESM bundle at packages/shadcn/dist/index.js with the #!/usr/bin/env node shebang, ready for npm publishing.

4.2 Publishing to npm

The project uses Changesets for version management and publishing:

# Create a changeset
pnpm changeset

# Version packages based on changesets
pnpm changeset version

# Publish to npm
pnpm pub:release    # stable release
pnpm pub:beta       # beta release

Important

The release script in the root package.json orchestrates the full release pipeline via Turbo. Always run pnpm release for production releases rather than publishing individual packages manually.

4.3 Building the Documentation Website

The docs site is a Next.js 16 application at apps/v4/:

# Development (with Turbopack)
pnpm v4:dev

# Production build
pnpm v4:build

The dev server runs on port 4000. The site uses Next.js App Router with MDX content powered by Fumadocs.

4.4 Building a Custom Component Registry

Organizations can host their own component registries. The shadcn build command compiles a registry from local source:

# Build registry to default output (./public/r)
npx shadcn build

# Build registry to custom output
npx shadcn build -o ./dist/registry

The built registry is a set of static JSON files that can be served from any HTTP server or CDN. The output structure follows the same format as the official registry at https://ui.shadcn.com/r.

Registry Item Structure

Each registry item is a JSON file containing:

{
  "name": "button",
  "type": "registry:ui",
  "files": [
    {
      "path": "ui/button.tsx",
      "content": "...",
      "type": "registry:ui",
      "target": "components/ui/button.tsx"
    }
  ],
  "dependencies": ["@radix-ui/react-slot"],
  "devDependencies": [],
  "cssVars": {},
  "css": "",
  "tailwind": {},
  "fonts": [],
  "envVars": [],
  "docs": ""
}

Registry Item Types

Type Description
registry:ui UI component
registry:theme Theme definition
registry:style Style variant
registry:base Base configuration
registry:font Font definition
registry:hook React hook
registry:lib Library utility

4.5 Deploying the Documentation Site

The Next.js docs site can be deployed to any platform that supports Next.js:

# Build for production
cd apps/v4
pnpm build

# The output is in .next/ — deploy per your hosting platform

Note

The official site at ui.shadcn.com serves both documentation and the component registry. The registry is built by scripts/build-registry.ts and output to public/r/ within the website.


5. Operations

5.1 Development Environment Setup

# Clone and install
git clone https://github.com/shadcn-ui/ui.git
cd ui
pnpm install

# Start all dev servers in parallel
pnpm dev

# Or start specific workspaces
pnpm v4:dev           # Docs website on port 4000
pnpm shadcn:dev       # CLI in dev mode

5.2 Building the Component Registry

The registry build is a critical step that compiles all component source files from apps/v4/registry/ into static JSON served by the docs site:

pnpm registry:build

This runs apps/v4/scripts/build-registry.ts, which:

  1. Reads all component definitions from apps/v4/registry/.
  2. Resolves dependencies between components.
  3. Compiles each component into a registry JSON file.
  4. Writes output to apps/v4/public/r/.

The registry source is organized into bases:

apps/v4/registry/bases/
├── base/          # Base UI primitives
│   ├── ui/        # Components
│   ├── blocks/    # Page-level compositions
│   └── examples/  # Usage examples
└── radix/         # Radix UI primitives
    ├── ui/        # Components
    ├── blocks/    # Page-level compositions
    └── examples/  # Usage examples

5.3 Running Tests

# Full test suite (builds registry, starts server, runs vitest)
pnpm test

# Run CLI unit tests only
pnpm --filter shadcn test

# Run integration tests only
pnpm --filter tests test

Warning

The full pnpm test command first builds the registry and starts a local server, then runs all Vitest suites. This can take several minutes. For iterative development, use the filtered commands above.

5.4 Template Syncing

Templates in templates/ serve as scaffolding for shadcn init. They must stay synchronized with the latest component conventions:

pnpm sync:templates

This copies shared configuration and boilerplate across all 10 template variants.

5.5 Registry Capture (Screenshots)

For documentation, the registry capture script generates screenshots of all components:

pnpm registry:capture

5.6 Linting

pnpm lint           # Check for linting issues
pnpm lint:fix       # Auto-fix where possible

5.7 Monorepo Task Graph

Turborepo manages the task dependency graph. Key tasks defined in turbo.json:

Task Dependencies Outputs
build ^build (dependent builds first) dist/**
dev — (runs in parallel) —
lint — —
test build —

6. Performance

6.1 Turbopack for Development

The documentation site (apps/v4/) uses Turbopack as its development bundler via Next.js 16. Turbopack provides near-instant hot module replacement (HMR) for the 8,550+ files in the monorepo, significantly faster than webpack for large codebases.

6.2 Registry Fetch Optimization

The registry fetcher (packages/shadcn/src/registry/fetcher.ts) optimizes network requests through:

  • Dependency deduplication: When resolving a component tree, the resolver tracks already-fetched items to avoid duplicate requests.
  • Parallel fetching: Independent dependencies are fetched concurrently.
  • Proxy support: The https_proxy environment variable routes traffic through a corporate or caching proxy, reducing latency in enterprise environments.

6.3 Component Resolution

The resolver (packages/shadcn/src/registry/resolver.ts) builds a dependency tree for each requested component:

  1. Fetch the requested item from the registry.
  2. Extract its dependencies (npm packages) and registryDependencies (other registry items).
  3. Recursively resolve all registry dependencies, deduplicating along the way.
  4. Return a flat list of all items to install, in dependency order.

This ensures that adding a complex component like data-table automatically pulls in all required sub-components (e.g., table, button, dropdown-menu) without duplicating any.

6.4 Build Performance

Turborepo provides:

  • Task caching: Build outputs are cached based on file hashes; unchanged packages are not rebuilt.
  • Parallel execution: Independent tasks run concurrently across all available CPU cores.
  • Incremental builds: Only modified packages and their dependents are rebuilt.

7. Security

7.1 Registry Fetch Security

All registry fetches use HTTPS by default. The default registry URL is https://ui.shadcn.com/r. The fetcher in packages/shadcn/src/registry/fetcher.ts enforces:

  • HTTPS transport: The registry URL scheme is not validated at the code level, but the default and all official registries use HTTPS.
  • Proxy support: When https_proxy is set, requests are routed through an https-proxy-agent, enabling corporate proxy inspection and logging.
  • Error classification: The fetcher maps HTTP status codes to specific error classes (RegistryNotFoundError for 404, RegistryUnauthorizedError for 401, RegistryForbiddenError for 403, RegistryGoneError for 410), providing clear diagnostics.

Caution

Custom registries specified in components.json can point to any URL. In shared or CI environments, audit the registries field to ensure it references trusted sources only. A compromised registry could inject malicious code into components that the CLI writes directly to your source tree.

7.2 Environment Variable Handling

Registry URLs and headers support ${VAR} expansion from process.env. The CLI raises RegistryMissingEnvironmentVariablesError if a referenced variable is not set, preventing accidental requests with empty authentication tokens.

Caution

Environment variables referenced in headers (e.g., Authorization: Bearer ${TOKEN}) are expanded at runtime and sent in HTTP requests. Store sensitive tokens in environment variables rather than hardcoding them in components.json, which may be committed to version control.

7.3 Registry Validation

The registry system validates incoming data at multiple levels:

  • Schema validation: All registry responses are validated against Zod schemas (packages/shadcn/src/schema/). Malformed responses are rejected with RegistryParseError.
  • Namespace validation: Registry namespaces (e.g., @shadcn, @v0) are validated against the configured registries. Unknown namespaces raise RegistryInvalidNamespaceError.
  • Config validation: The components.json file is validated on load. Missing or malformed configuration raises ConfigMissingError or ConfigParseError.

7.4 Safe Target Path Validation

When the CLI writes component files to disk, it validates target paths using the isSafeTarget function. This prevents path traversal attacks where a malicious registry item could specify a target like ../../../etc/passwd. The validator ensures:

  • Target paths are within the project directory.
  • Target paths do not traverse above the project root.
  • Target paths do not overwrite critical project files (e.g., package.json, config files).

Important

The isSafeTarget check is a critical security boundary. It runs before any file write operation during shadcn add. If a registry item specifies a target path that fails validation, the entire operation is aborted.

7.5 Credential Hygiene

  • Registry authentication tokens belong in environment variables, not in components.json.
  • The CLI does not log or display registry headers (which may contain tokens).
  • The --dry-run flag on shadcn add allows previewing what will be written without executing any file operations or network requests beyond the initial fetch.

8. Developer Internals

8.1 Repository Structure

shadcn-ui/ui/
├── apps/
│   └── v4/                           # Next.js 16 documentation website
│       ├── app/                      # App Router pages and layouts
│       ├── registry/                 # Component registry source
│       │   ├── bases/
│       │   │   ├── base/             # Base UI components, blocks, examples
│       │   │   └── radix/            # Radix UI components, blocks, examples
│       │   ├── new-york-v4/          # New York v4 style variant
│       │   ├── icons/                # Icon library definitions
│       │   └── styles/               # Style definitions
│       ├── styles/                   # Pre-built CSS style variants
│       ├── content/docs/             # MDX documentation pages
│       ├── examples/                 # Live example components
│       ├── lib/                      # Shared website utilities (21 files)
│       ├── hooks/                    # Website React hooks
│       ├── components/               # Website-specific components
│       └── scripts/                  # Build scripts
│           ├── build-registry.ts     # Compiles registry JSON
│           ├── build-icons.ts        # Compiles icon definitions
│           └── ...
├── packages/
│   ├── shadcn/                       # CLI package (npm: "shadcn")
│   │   ├── src/
│   │   │   ├── index.ts              # CLI entry point (Commander setup)
│   │   │   ├── commands/             # 12 command implementations
│   │   │   ├── registry/             # Registry system (fetch, resolve, build)
│   │   │   ├── utils/                # Utilities (transforms, updaters, config)
│   │   │   ├── schema/               # Zod validation schemas
│   │   │   ├── preflights/           # Pre-flight check system
│   │   │   ├── preset/               # Preset system
│   │   │   ├── templates/            # Framework template handlers
│   │   │   ├── migrations/           # Migration system
│   │   │   ├── mcp/                  # MCP server implementation
│   │   │   ├── icons/                # Icon library definitions
│   │   │   └── styles/               # Style definitions
│   │   └── test/                     # CLI unit tests with fixtures
│   └── tests/                        # Integration tests
├── templates/                        # 10 project scaffolding templates
│   ├── next-app/
│   ├── next-monorepo/
│   ├── vite-app/
│   ├── vite-monorepo/
│   ├── react-router-app/
│   ├── react-router-monorepo/
│   ├── start-app/
│   ├── start-monorepo/
│   ├── astro-app/
│   └── astro-monorepo/
└── skills/
    └── shadcn/                       # AI agent skill definitions

8.2 CLI Entry Point — packages/shadcn/src/index.ts

The entry point configures Commander.js with all 12 commands. Each command is imported from packages/shadcn/src/commands/ and registered with its options. The file includes the shebang #!/usr/bin/env node for direct execution.

8.3 Command Modules — packages/shadcn/src/commands/

This directory contains 12 files (approximately 90K characters of TypeScript total), one per CLI command.

File Command Description
init.ts shadcn init Project initialization. Detects framework, runs preflights, applies template, creates components.json, installs base dependencies. Supports preset-based initialization.
add.ts shadcn add Component addition. Resolves registry tree, runs transforms, writes files. The most complex command — orchestrates the entire fetch-transform-write pipeline.
apply.ts shadcn apply Preset application. Decodes a preset (named or encoded) and applies it to an existing project's components.json and CSS.
diff.ts shadcn diff Component diffing. Fetches the latest registry version of installed components and shows a unified diff against local versions. Deprecated.
docs.ts shadcn docs Documentation display. Fetches and renders component documentation from the registry.
view.ts shadcn view Component viewing. Displays component source code from the registry without writing any files.
search.ts shadcn search Registry search. Queries the registry API for components matching a search term.
migrate.ts shadcn migrate Migration runner. Executes available migrations (icon library swap, radix-to-base, RTL enablement).
info.ts shadcn info Project info. Displays the current components.json configuration and detected project settings.
build.ts shadcn build Registry builder. Compiles a custom registry from local source into static JSON.
mcp.ts shadcn mcp MCP server. Starts a Model Context Protocol server for AI tool integration. Has init subcommand for client configuration.
registry.ts shadcn registry Registry management commands.

8.4 Registry System — packages/shadcn/src/registry/

The registry system (28 files, approximately 61K characters) handles all interaction with component registries — fetching, resolving dependencies, building custom registries, and validating data.

File Role
api.ts High-level registry API functions used by commands
builder.ts Builds custom registries from local source files; supports env var expansion in URLs (${VAR} syntax) and headers
config.ts Registry configuration management, URL resolution, namespace mapping
constants.ts Default registry URL (https://ui.shadcn.com/r), built-in registry definitions (@shadcn, @v0)
context.ts Registry context object carrying config + state through operations
errors.ts 15 error classes for granular error handling (see hierarchy below)
fetcher.ts HTTP fetch with https-proxy-agent proxy support, status-to-error mapping, response parsing
resolver.ts Recursive dependency tree resolution with deduplication; produces flat install order
search.ts Registry search implementation for shadcn search command
validator.ts Zod-based validation of registry responses against schema definitions
utils.ts Shared registry utilities (URL construction, name parsing, namespace extraction)

8.4.1 Error Hierarchy

The registry defines 15 error classes in errors.ts, all extending RegistryError:

RegistryError (base)
├── RegistryNotFoundError          (HTTP 404)
├── RegistryGoneError              (HTTP 410)
├── RegistryUnauthorizedError      (HTTP 401)
├── RegistryForbiddenError         (HTTP 403)
├── RegistryFetchError             (other HTTP errors)
├── RegistryNotConfiguredError     (registry not in components.json)
├── RegistryLocalFileError         (local file access issues)
├── RegistryParseError             (malformed JSON response)
├── RegistryMissingEnvironmentVariablesError
├── RegistryInvalidNamespaceError
├── ConfigMissingError             (no components.json found)
├── ConfigParseError               (invalid components.json)
├── RegistriesIndexParseError      (invalid registries index response)
└── InvalidConfigIconLibraryError  (unknown icon library in config)

8.4.2 Built-in Registries

Two registries are built in and do not need explicit configuration:

Namespace URL Purpose
@shadcn https://ui.shadcn.com/r Official shadcn/ui component registry
@v0 v0.dev integration URL Integration with Vercel's v0.dev AI design tool

Components are referenced by namespace/name syntax. Bare names (no namespace) default to @shadcn.

8.4.3 Registry Resolution Algorithm

When shadcn add button dropdown-menu is invoked, the resolver executes:

  1. Parse names: Split each argument into namespace + component name. Bare names default to @shadcn.
  2. Resolve registry URL: Map namespace to URL via constants.ts (built-in) or components.json#registries (custom).
  3. Fetch root items: HTTP GET each component JSON from {registryUrl}/styles/{style}/{name}.json.
  4. Extract registry dependencies: Each item may declare registryDependencies — other registry items it depends on.
  5. Recursive resolution: For each dependency not already seen, repeat steps 2-4. A Set of visited names prevents cycles and duplicates.
  6. Flatten: Return a deduplicated, topologically-ordered list of all items to install.
  7. Merge: Aggregate all npm dependencies and devDependencies across all resolved items.

8.5 Utilities — packages/shadcn/src/utils/

The utilities directory (35 files, approximately 161K characters) contains the heaviest logic in the CLI. Key modules:

File Role
add-components.ts Orchestrates the full component-addition pipeline (resolve, validate, deps, config, files, CSS)
create-project.ts Project scaffolding (clone template, configure, install)
get-config.ts Configuration loading via cosmiconfig with Zod validation
get-project-info.ts Framework detection (Next.js, Vite, Astro, React Router, TanStack Start)
get-package-manager.ts Package manager detection (npm, pnpm, yarn, bun) via npm_config_user_agent and lockfile heuristics
scaffold.ts Template-based project scaffolding
transformers.ts Entry point for the 9-stage transform pipeline

8.5.1 Transform Pipeline Modules

Located within packages/shadcn/src/utils/, these modules implement the AST-based transforms applied to every component file during shadcn add:

Transform Order Module Description
transformImport 1 transform-import.ts Rewrites import paths to match project aliases (e.g., @/components/ui to the user's configured alias). Uses ts-morph for AST manipulation.
transformRsc 2 transform-rsc.ts Adds or removes "use client" directives based on the rsc config flag. Components that use hooks or browser APIs get the directive; server-safe components do not.
transformCssVars 3 transform-css-vars.ts Converts between inline Tailwind utility classes and CSS custom property usage based on tailwind.cssVariables.
transformTwPrefixes 4 transform-tw-prefixes.ts Applies Tailwind class prefixes (e.g., bg-primary becomes tw-bg-primary) when tailwind.prefix is set.
transformIcons 5 transform-icons.ts Swaps icon imports between icon libraries (e.g., Lucide to custom icon set) based on iconLibrary configuration.
transformMenu 6 transform-menu.ts Applies menu color and accent transforms based on menuColor and menuAccent settings.
transformRtl 7 transform-rtl.ts Applies right-to-left layout transforms: swaps left/right classes, adjusts margin and padding directional utilities, mirrors layout-related properties.
transformFont 8 transform-font.ts Processes font marker comments in component source, replacing them with the configured font family.
transformCleanup 9 transform-cleanup.ts Final cleanup pass: removes internal markers, normalizes whitespace, ensures consistent formatting of the output file.

Note

Transforms use a combination of Babel (for JSX/TSX parsing), ts-morph (for TypeScript AST manipulation), and recast (for code printing that preserves formatting). This three-tool approach handles the full spectrum of code transformations while maintaining readable output.

8.5.2 Updater Modules

These modules handle the "write phase" after transforms complete:

File Role
updaters/update-dependencies.ts Runs npm install / pnpm add / yarn add / bun add for newly required npm dependencies
updaters/update-tailwind-config.ts Patches the Tailwind configuration file with new content paths or plugins required by added components
updaters/update-files.ts Writes transformed component files to disk with isSafeTarget validation
updaters/update-css.ts Updates the global CSS file with new CSS variable definitions or component styles (run last to trigger rebuild)
updaters/update-env-vars.ts Adds required environment variables to .env files when components declare envVars
updaters/update-fonts.ts Registers font imports required by components

8.5.3 Component Addition Orchestration

The add-components.ts module orchestrates the full write phase in a specific order designed for correctness:

  1. Resolve registry tree — fetch the component and all transitive dependencies.
  2. Validate file targets — ensure all target paths pass isSafeTarget.
  3. Update dependencies — install npm packages first so TypeScript/bundler can resolve imports.
  4. Update Tailwind config — add content paths or plugins.
  5. Update env vars — write .env entries.
  6. Update fonts — register font imports.
  7. Write files — write all transformed component files.
  8. Update CSS — write CSS changes last to trigger a hot reload that picks up all preceding changes.

Tip

The CSS-last ordering is intentional. In development with HMR, a CSS change triggers a rebuild. By making it the final step, the rebuild sees all new files and dependencies already in place, avoiding transient errors.

8.6 Pre-flight Checks — packages/shadcn/src/preflights/

Pre-flight checks run before command execution to validate the project environment:

Check Purpose
Framework detection Verify the project uses a supported framework
TypeScript detection Determine .ts/.tsx vs .js/.jsx output
Tailwind CSS detection Verify Tailwind is configured and locate its CSS entry point
Package manager detection Determine which package manager to use for dependency installation
RSC detection Determine if the project supports React Server Components
Config validation Validate components.json exists and is well-formed

8.7 Preset System — packages/shadcn/src/preset/

Presets encode a complete design configuration — style, base, font, icon library, colors, menu settings — into either a named preset or a compact encoded string.

Named presets include: lyra, nova, sera, and others. Each preset maps to a specific combination of configuration values that produce a cohesive visual design.

Preset codes are compact strings generated by the preset builder at ui.shadcn.com/create. They encode all configuration as a URL-safe string that the CLI can decode and apply. The encoding captures:

  • Style variant
  • Base UI library (Radix or Base)
  • Font family
  • Icon library
  • Color palette
  • Menu color and accent
  • RTL setting

The preset system is used by both shadcn init --preset and shadcn apply --preset.

8.8 Template System — packages/shadcn/src/templates/

Each of the 10 supported framework configurations has a template handler:

Template Framework Layout
next Next.js Single application
next-monorepo Next.js pnpm workspace monorepo
vite Vite (React) Single application
vite-monorepo Vite (React) pnpm workspace monorepo
react-router React Router Single application
react-router-monorepo React Router pnpm workspace monorepo
start TanStack Start Single application
start-monorepo TanStack Start pnpm workspace monorepo
astro Astro Single application
astro-monorepo Astro pnpm workspace monorepo

Each template handler implements:

  • init() — framework-specific initialization logic (copy boilerplate, configure package.json, set up routing)
  • postInit() — post-initialization steps (e.g., git init, install dependencies)

Template source files live in the top-level templates/ directory. Each template directory contains a complete, minimal project scaffold for its framework. The pnpm sync:templates command synchronizes shared files across all templates.

8.9 Migration System — packages/shadcn/src/migrations/

Migrations handle breaking changes between versions and enable bulk transformations across installed components. Available migrations:

Migration Purpose
Icons migration Swap between icon libraries across all installed components (e.g., migrating from Lucide to a custom icon set)
Radix migration Migrate components from Radix UI to Base UI primitives (or vice versa), rewriting imports and adapting API usage
RTL migration Enable or disable RTL support across all installed components, applying directional transforms

Migrations are run via shadcn migrate. The --list flag shows available migrations without executing them. The --yes flag skips confirmation prompts for CI usage.

8.10 MCP Server — packages/shadcn/src/mcp/

The MCP (Model Context Protocol) server exposes shadcn/ui's component-addition capabilities to AI coding tools. It implements the @modelcontextprotocol/sdk server interface, allowing AI agents to programmatically:

  • Search the component registry
  • Add components to the project
  • View component documentation
  • Inspect project configuration

Supported clients (configured via shadcn mcp init --client):

Client Config File Format
Claude Code .mcp.json JSON (project root)
Cursor .cursor/mcp.json JSON
VS Code .vscode/mcp.json JSON
Codex ~/.codex/config.toml TOML
OpenCode opencode.json JSON

The shadcn mcp init command generates the appropriate configuration file for the chosen client, pointing it at the shadcn mcp server command. After initialization, the AI tool can invoke shadcn operations as MCP tool calls.

8.11 Schema Definitions — packages/shadcn/src/schema/

Zod schemas define and validate all data structures at system boundaries:

Schema Validates
Config schema components.json structure (all fields, types, constraints)
Registry item schema Individual registry entries (files, dependencies, metadata)
Registry index schema Registry listing responses (array of available items)
Preset schema Preset definitions and encoded preset codes

Every external input — config files loaded from disk, JSON fetched from registries, user-provided options — passes through Zod validation before use. Parse failures produce structured ZodError instances with path information for debugging.

8.12 Icon Libraries — packages/shadcn/src/icons/

Icon library definitions map icon names to their import sources. The system supports multiple icon libraries, and the iconLibrary config key determines which one is active. The CLI uses these definitions when:

  • Initializing a project with a specific icon library via shadcn init
  • Migrating between icon libraries via shadcn migrate
  • Transforming icon imports during shadcn add (the transformIcons stage)

Each icon library definition maps logical icon names (e.g., "chevron-right", "search", "close") to their actual import paths and component names in the target library.

8.13 Style Definitions — packages/shadcn/src/styles/

Style definitions describe the available component style variants. These are referenced by the style field in components.json. The apps/v4/styles/ directory contains pre-built CSS for each style variant:

  • base-* variants — styles built for Base UI components
  • radix-* variants — styles built for Radix UI components

Each style variant determines the default appearance of components: colors, spacing, border radius, typography, and other visual properties.

8.14 Code Patterns and Conventions

ESM throughout: The entire codebase uses ECMAScript modules. The CLI bundle is ESM with a Node.js shebang. All imports use the import statement; there are no CommonJS require() calls.

Zod for validation: Every external data boundary (config files, registry responses, CLI arguments) is validated with Zod schemas. Parsing failures produce structured errors with clear messages and paths.

Commander.js for CLI: All command definitions, option parsing, and help text generation are handled by Commander.js. Each command module exports a function that receives the Commander program instance and registers its options.

cosmiconfig for config loading: The components.json file is loaded via cosmiconfig, which searches for configuration in multiple formats and locations. The search resolves the nearest config file up the directory tree.

Atomic write ordering: File writes during shadcn add follow a strict order (dependencies first, CSS last) to ensure consistent project state at every intermediate step.

Error-first design: The registry system uses a rich error hierarchy (15 classes) to provide specific, actionable error messages. HTTP errors are mapped to domain-specific error types that carry context about what went wrong and how to fix it.

ts-morph for AST transforms: TypeScript source files are manipulated programmatically using ts-morph (v26.0), which wraps the TypeScript compiler API in a developer-friendly interface. Babel handles JSX parsing, and recast preserves original formatting when printing modified ASTs.

8.15 Testing Strategy

Unit Tests — packages/shadcn/test/

The CLI unit tests use Vitest with an extensive fixture system:

Fixture Category Configurations Tested
Next.js App Router, Pages Router, /src layout, monorepo
Vite Standard, monorepo
Remix Standard layout
T3 Stack Standard layout
Custom aliases Various alias configurations

Tests cover:

  • Config loading and validation
  • Project detection across all supported frameworks
  • Package manager detection from multiple signals
  • Transform pipeline correctness (import rewriting, RSC directive handling, CSS variable conversion, Tailwind prefix application, icon swapping, menu transforms, RTL transforms, font processing, cleanup)
  • Registry fetching and parsing (mocked with MSW)
  • Preset encoding and decoding
  • Scaffold and template system

Integration Tests — packages/tests/

Integration tests exercise the full pipeline end-to-end:

  1. Build the component registry from source (pnpm registry:build).
  2. Start a local HTTP server to serve the built registry.
  3. Run CLI commands against the local registry.
  4. Verify output files match expectations (correct imports, correct file locations, correct dependencies).

Mock Service Worker (MSW)

Registry API calls in unit tests are intercepted by MSW handlers that return fixture data. This avoids network dependency in tests while exercising the full fetch-parse-validate pipeline. MSW handlers are configured to return realistic registry responses matching the production schema.

8.16 Potential Improvements

Based on source analysis, these areas present opportunities for enhancement:

Area Observation
Transactional writes When a multi-component add fails partway through, already-written files are not rolled back. A transactional write approach (write to temp, then atomically move) would improve reliability.
Local registry cache The CLI fetches registry data on every invocation. A local cache with TTL-based invalidation would reduce network calls, improve offline support, and speed up repeated operations.
Single-pass transforms The 9-stage transform pipeline parses and re-prints the AST multiple times. A single-pass approach with composed visitors would reduce parse overhead for large component files.
Broader workspace detection Monorepo detection relies on pnpm, npm, yarn, and bun workspace patterns. Nx and Lerna workspaces are not currently detected.
Config migration When the components.json schema changes between CLI versions, users must manually update their configuration. An automatic config migration system (similar to the component migration system) would smooth upgrades.
Offline mode No explicit offline mode exists. The CLI could fall back to a local cache or bundled registry subset when network is unavailable.

8.17 Contributing

Development Workflow

  1. Fork and clone the repository.
  2. Install dependencies: pnpm install
  3. Create a feature branch.
  4. Make changes.
  5. Run tests: pnpm test
  6. Run linting: pnpm lint
  7. Create a changeset: pnpm changeset (for changes that affect published packages).
  8. Submit a pull request.

Key Development Commands

Command Purpose
pnpm dev Start all dev servers in parallel
pnpm shadcn:dev Run CLI in dev mode (watches for changes)
pnpm shadcn:prod Run CLI against production registry
pnpm registry:build Rebuild component registry from source
pnpm registry:capture Capture component screenshots for docs
pnpm test Full test suite (build + serve + vitest)
pnpm lint ESLint checks across all packages
pnpm lint:fix Auto-fix lint issues
pnpm sync:templates Synchronize template scaffolds
pnpm v4:dev Run docs website in dev mode (port 4000)
pnpm v4:build Production build of docs website

Adding a New Component to the Registry

  1. Create the component source in apps/v4/registry/bases/{base}/ui/ (choose radix or base directory).
  2. Define the component's registry metadata (name, type, dependencies, registryDependencies).
  3. Create usage examples in apps/v4/registry/bases/{base}/examples/.
  4. Add MDX documentation in apps/v4/content/docs/.
  5. Run pnpm registry:build to compile the registry.
  6. Test locally with pnpm shadcn:dev add {component-name}.
  7. Add unit tests in packages/shadcn/test/ if the component introduces new patterns.

Adding a New CLI Command

  1. Create a new file in packages/shadcn/src/commands/ following the existing command pattern.
  2. Export the command registration function.
  3. Import and register the command in packages/shadcn/src/index.ts.
  4. Add tests in packages/shadcn/test/.
  5. Update the shadcn --help text if needed (Commander generates it automatically from option definitions).

9. Integration Points

9.1 MCP Protocol Integration

The MCP server (packages/shadcn/src/mcp/) implements the Model Context Protocol, enabling AI coding assistants to interact with shadcn/ui programmatically. The integration is bidirectional:

  • AI to shadcn: AI agents invoke CLI operations (add, search, docs) through MCP tool calls.
  • shadcn to AI: The shadcn mcp init command configures the appropriate MCP config file for each supported client.

Configuration files written by shadcn mcp init:

Client File Format
Claude Code .mcp.json JSON (project root)
Cursor .cursor/mcp.json JSON
VS Code .vscode/mcp.json JSON
Codex ~/.codex/config.toml TOML
OpenCode opencode.json JSON

Tip

When using Claude Code with shadcn/ui, run npx shadcn mcp init --client claude in your project root. This creates .mcp.json so Claude can add components directly during conversation.

9.2 Custom Registry API Contract

To host a custom component registry, serve static JSON files at a URL that matches this contract:

Base URL: https://your-registry.com/r

Endpoints (static JSON files):

Path Returns
/r/index.json Array of all available registry items with name, type, and description
/r/styles/{style}/{name}.json Individual component definition with files, dependencies, and metadata

Each component JSON follows the registry item schema:

{
  "name": "string",
  "type": "registry:ui | registry:hook | registry:lib | ...",
  "files": [
    {
      "path": "string (source path within registry)",
      "content": "string (full file content)",
      "type": "string (registry item type)",
      "target": "string (target path in user's project)"
    }
  ],
  "dependencies": ["string (npm package names)"],
  "devDependencies": ["string (npm dev package names)"],
  "registryDependencies": ["string (other registry item names)"],
  "cssVars": {
    "light": { "variable-name": "value" },
    "dark": { "variable-name": "value" }
  },
  "css": "string (additional CSS to inject)",
  "tailwind": {
    "config": {}
  },
  "fonts": ["string (font definitions)"],
  "envVars": ["string (required environment variables)"],
  "docs": "string (component documentation markdown)"
}

Tip

Use shadcn build to generate a compatible registry from your component source. This ensures the output matches the expected schema without manual JSON authoring.

9.3 v0.dev Integration

The @v0 built-in registry namespace connects to Vercel's v0.dev AI design tool. Components generated by v0 can be added to a shadcn/ui project using:

npx shadcn add @v0/component-id

This fetches the v0-generated component through the @v0 registry, applies the same transform pipeline as standard components (import rewriting, RSC handling, CSS variables, etc.), and writes the result to the project. The integration allows designers to generate components with v0's AI and immediately integrate them into a shadcn/ui project.

9.4 Framework Detection System

The framework detection system (packages/shadcn/src/utils/get-project-info.ts) examines multiple signals to identify the active framework:

Signal Frameworks Detected
package.json dependencies Next.js (next), Vite (vite), Astro (astro), React Router (react-router), TanStack Start (@tanstack/start)
Config files next.config.*, vite.config.*, astro.config.*, router config files
Directory structure app/ (App Router), pages/ (Pages Router), src/ layout variants
TypeScript config tsconfig.json paths, compiler options, JSX settings

The detection result drives:

  • Template selection during shadcn init (which scaffolding to use)
  • RSC support detection (Next.js App Router = RSC enabled; Vite = no RSC)
  • File target resolution (where components are written relative to project root)
  • Import alias detection (read from tsconfig.json paths)

9.5 Package Manager Detection

The package manager detection system (packages/shadcn/src/utils/get-package-manager.ts) uses a multi-signal approach:

Signal Priority Detection
npm_config_user_agent Highest Set automatically by npm/pnpm/yarn/bun when running scripts
Lock files High pnpm-lock.yaml, package-lock.json, yarn.lock, bun.lockb
packageManager field Medium package.json#packageManager (Corepack)

The detected package manager determines:

  • Which install command to run (npm install, pnpm add, yarn add, bun add)
  • Which workspace configuration format to read
  • How to detect monorepo workspace roots

Note

If the CLI detects the wrong package manager, ensure the correct lock file exists in your project root, or invoke the CLI through the correct package manager (e.g., pnpm dlx shadcn instead of npx shadcn).

9.6 Known Open Issues

The following issues are tracked in the shadcn-ui/ui GitHub repository:

Bugs: #10415, #10414, #4516, #5263, #10411, #9301, #7129, #10377, #6536, #10104, #7202, #7206

Feature Requests: #7187, #7199, #9407

Note

Issue numbers reference the shadcn-ui/ui GitHub repository. Check the repository's issue tracker for current status, as issues may have been resolved since this document was generated.