How the shadcn Build Process Works: Configuration, Output, and Registry Generation
The shadcn build process compiles the CLI and core utilities into a minified ES-module bundle using tsup, while a separate registry build command generates static JSON files that power the component installation workflow.
The shadcn-ui/ui repository is a monorepo that packages the shadcn CLI and its registry system. Understanding the shadcn build process is essential for contributors who need to modify the CLI or maintain a custom component registry. This article breaks down the bundler configuration, monorepo orchestration, and registry generation pipeline.
shadcn Build Output Configuration
The core build configuration lives in packages/shadcn/tsup.config.ts and uses tsup, a fast TypeScript bundler built on esbuild.
tsup Bundler Settings
The configuration defines a strict ES-module output with aggressive optimizations:
// packages/shadcn/tsup.config.ts
import { defineConfig } from "tsup"
export default defineConfig({
clean: true, // Removes dist/ before building
dts: true, // Generates .d.ts type declarations
entry: [
"src/index.ts", // CLI entry point
"src/registry/**/*.ts",
"src/utils/**/*.ts",
// ... other public APIs
],
format: ["esm"], // Only ES modules
sourcemap: true, // Debug symbols
minify: true, // Minified output
target: "esnext", // Latest JavaScript features
outDir: "dist", // Output directory
treeshake: true, // Remove unused code
onSuccess: "cp src/tailwind.css dist/tailwind.css"
})
The onSuccess hook copies src/tailwind.css into the dist folder, ensuring the CLI can access the base Tailwind styles when initializing new projects.
Output Directory Structure
After running the build, packages/shadcn/dist/ contains:
index.js– The CLI entry point (shebang included fornpxexecution)registry/,schema/,utils/,icons/– Sub-paths for modular importstailwind.css– Static asset for project initialization*.d.ts– Bundled type definitions for TypeScript consumers
Monorepo Build Orchestration with Turbo
The shadcn-ui/ui repository uses Turborepo to coordinate builds across packages.
Root-Level Build Scripts
The root package.json defines a Turbo pipeline:
{
"scripts": {
"build": "turbo run build"
}
}
Running pnpm run build (or npm run build) triggers turbo run build, which walks the dependency graph and executes package builds in parallel where possible.
Package-Specific Build Commands
For the shadcn package specifically, the build is invoked via:
pnpm shadcn:build # alias for pnpm --filter=shadcn build
This executes the build script defined in packages/shadcn/package.json:
{
"scripts": {
"build": "tsup"
}
}
Other packages in the monorepo (such as the v4 app) have their own build steps, but the core CLI output always lands in packages/shadcn/dist.
shadcn Registry Build Process
Beyond the CLI bundle, shadcn provides a registry build command that generates static JSON files powering the component installation workflow.
CLI Command Structure
The registry build logic lives in packages/shadcn/src/commands/registry/build.ts. The command is exposed through the CLI as:
pnpm shadcn registry:build
Or using the production binary:
pnpm shadcn:prod registry:build
The command accepts several options:
-o, --output– Output directory (default:./public/r)-c, --cwd– Working directory--verbose– Detailed logging
Registry Resolution and Validation
The build process follows a strict pipeline:
- Pre-flight validation – Ensures
registry.json(orcomponents.json) exists and loads project configuration. - Schema validation – Loads and validates
registry.jsonagainst a Zod schema. - Item resolution – Recursively walks each
registryItem.files, resolvingimportstatements and pulling referenced files and dependencies. - Deduplication – Removes duplicate file paths and dependencies per item.
- Content embedding – Reads the source code of every resolved file from disk and stores it under
file.content.
JSON Generation and Output
After processing, the command:
- Attaches a
$schemaURL for editor validation. - Validates each enriched item against
registryItemSchema. - Writes one JSON file per registry item to the output directory (default
./public/r). - Copies the master
registry.jsoninto the output folder. - Optionally prints a summary of generated items when
--verboseis enabled.
The resulting ./public/r folder contains:
registry.json– The master index of all available components.- Individual JSON files (e.g.,
button.json,card.json) – Self-contained component definitions with embedded source code.
Practical Build Examples
Building the Component Library
To compile the shadcn CLI and utilities from the repository root:
# Build all packages using Turbo
pnpm run build
# Or build only the shadcn package
pnpm shadcn:build
The output lands in packages/shadcn/dist/ and includes the CLI entry point, type definitions, and the tailwind.css asset.
Building the Registry JSON
To generate the static registry files for component distribution:
# Default output to ./public/r
pnpm shadcn registry:build
# Custom working directory and output
pnpm shadcn registry:build -c ./my-app -o ./my-app/public/registry
# With detailed logging
pnpm shadcn registry:build --verbose
The production CLI runs with REGISTRY_URL=https://ui.shadcn.com/r, serving the generated JSON files as static assets.
Key Configuration Files
| File | Role |
|---|---|
packages/shadcn/tsup.config.ts |
Defines how TypeScript source compiles into the dist folder using tsup. |
packages/shadcn/package.json |
Contains the build script (tsup) and publish configuration. |
packages/shadcn/src/commands/registry/build.ts |
Implements the CLI command that creates JSON registry output. |
package.json (root) |
Turbo monorepo orchestration; top-level build script runs all package builds. |
turbo.json |
Turbo configuration defining the task graph and build caching. |
Summary
- tsup configuration in
packages/shadcn/tsup.config.tscontrols the bundler settings, producing a minified ES-module output with type declarations indist/. - Turborepo orchestration coordinates builds across the monorepo via
turbo run build, with the shadcn package building independently throughpnpm shadcn:build. - Registry build command (
shadcn registry:build) generates static JSON files by resolving imports, deduplicating dependencies, and embedding source code intopublic/r/. - Output artifacts include the CLI bundle (
index.js), sub-modules for registry utilities, TypeScript declarations, and thetailwind.cssasset required for project initialization.
Frequently Asked Questions
What bundler does shadcn use for its build process?
The shadcn CLI and core utilities are bundled using tsup, a fast TypeScript bundler built on esbuild. The configuration in packages/shadcn/tsup.config.ts specifies ES-module output, type declaration generation, minification, and tree-shaking to produce the final dist/ artifacts.
How does the shadcn registry build command work?
The shadcn registry:build command, implemented in packages/shadcn/src/commands/registry/build.ts, reads the project's registry.json, validates it against a Zod schema, recursively resolves all file imports for each registry item, deduplicates dependencies, embeds the source code content, and writes individual JSON files to the output directory (default ./public/r).
Where does the shadcn build output go?
The main CLI build outputs to packages/shadcn/dist/ and includes index.js (the CLI entry point), modular sub-directories for registry and utility functions, bundled .d.ts type declarations, and a copied tailwind.css file. The registry build outputs to ./public/r/ (configurable via -o flag) and contains the master registry.json and individual component JSON files.
Can I customize the output directory for the registry build?
Yes, the shadcn registry:build command accepts an -o, --output flag to specify a custom directory. For example, pnpm shadcn registry:build -o ./my-app/public/registry directs the generated JSON files to that location instead of the default ./public/r.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →