shadcn
A command-line tool and component registry for copying editable React interface components into your own repository. You choose a base such as Base UI, Radix, or React Aria, a style preset, Tailwind configuration, aliases, and optional React Server Component behavior. The CLI then writes component source, dependencies, utilities, and theme tokens into your project. This is not a normal component package that remains behind an import; once added, the code is yours to maintain.
Excellent raw material for teams that truly want to own a design system. It is a poor fit if you expect package-style upgrades or want to avoid Tailwind, generated files, and permanent responsibility for every copied component.
Use it if
- You want accessible component primitives with polished defaults but need full ownership of markup, styles, and behavior
- Your team already uses React, Tailwind CSS, path aliases, and a component-source workflow
- You are building an internal design system and want registry items as a practical starting point rather than a permanent binary dependency
- You need the CLI to route shared UI code and imports across a supported monorepo structure
- You want upgrades to arrive through a package version: generated component files are copied into your repository, so upstream fixes do not automatically update code you have edited
- You do not use Tailwind CSS or do not want global design tokens: current setup configures Tailwind and generates CSS variables by default
- You need a stable theme decision that can be switched later without churn: the documentation says style, base color, and the CSS-variable choice cannot be changed after initialization without deleting and reinstalling components
- You need a small, neutral CLI footprint: version 4.16.2 requires Node 20.18.1 or newer and declares a long dependency list including Babel, TypeScript AST, PostCSS, registry, and MCP tooling
- You want a framework-agnostic hosted widget library: the generated code assumes React conventions, project aliases, local source ownership, and framework-specific client or server boundaries
Setup reality
The happy path is npx shadcn@latest init followed by npx shadcn@latest add button, but init is a project migration, not a harmless package install. It installs dependencies, writes a cn utility, modifies CSS, creates components.json, and asks you to commit to a base, preset, icon library, base color, CSS variables, TypeScript output, and React Server Component behavior. Version 4.16.2 requires Node 20.18.1 or newer. Existing Vite projects need Tailwind, @tailwindcss/vite, matching @ aliases in both tsconfig.json and tsconfig.app.json, a Vite resolver alias, and the correct global CSS path. Tailwind v4 requires an empty tailwind config entry. The CLI uses aliases to decide where to write files and how to rewrite imports, so a mismatch can scatter generated code or create imports the bundler cannot resolve. Use add --dry-run and --diff before accepting changes, especially because --overwrite replaces local component files. In a monorepo every workspace needs components.json, shared exports must match generated imports, and style, icon library, and base color must agree across workspaces. Generated components bring their own registry dependencies and client boundaries. Since the source is copied, you own testing, local changes, accessibility regressions, and future merges with upstream versions.
Patterns
Initialize an existing projectinitialize-existing-project
npx shadcn@4.16.2 initCommit or stash first because init installs dependencies and writes configuration, CSS tokens, and utility source.
Create a Vite project from the CLIscaffold-vite-project
npx shadcn@4.16.2 init --template vite --name dashboardThe CLI requires Node 20.18.1 or newer; the template command creates a project rather than modifying only the current package.
Preview generated files before writingpreview-component-changes
npx shadcn@4.16.2 add dialog --dry-run
npx shadcn@4.16.2 add dialog --diffUse this before adding to a customized codebase so dependency and file changes are visible.
Add several registry componentsadd-components
npx shadcn@4.16.2 add button card dialogThe command copies source and may install primitive dependencies; it does not create imports from a single shadcn UI runtime package.
Describe installation pathsconfigure-components-json
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "base-nova",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/index.css",
"baseColor": "neutral",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"ui": "@/components/ui",
"utils": "@/lib/utils",
"lib": "@/lib",
"hooks": "@/hooks"
}
}For Tailwind CSS v4, leave tailwind.config empty. Aliases must also resolve in TypeScript and the bundler.
Compose an installed cardrender-card-component
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
export function Summary() {
return (
<Card className="max-w-sm">
<CardHeader><CardTitle>Project status</CardTitle></CardHeader>
<CardContent>All checks passed.</CardContent>
</Card>
);
}The import points to local generated source; your alias and components.json must agree on that location.
Wrap a local button variantcustomize-button-source
import { Button } from '@/components/ui/button';
export function DangerButton(props) {
return <Button variant="destructive" {...props} />;
}Inspect the generated button source for the actual variants in your preset because local registry output can differ.
Expose a custom semantic coloradd-theme-token
:root {
--warning: oklch(0.84 0.16 84);
--warning-foreground: oklch(0.28 0.07 46);
}
.dark {
--warning: oklch(0.41 0.11 46);
--warning-foreground: oklch(0.99 0.02 95);
}
@theme inline {
--color-warning: var(--warning);
--color-warning-foreground: var(--warning-foreground);
}Define both light and dark values before using bg-warning and text-warning-foreground utilities.
Install from an app workspaceadd-to-monorepo
npx shadcn@4.16.2 add card --cwd apps/webEvery participating workspace needs a compatible components.json, and the shared UI package must export generated paths.
Import from a shared UI workspaceimport-monorepo-component
import { Button } from '@workspace/ui/components/button';
import { cn } from '@workspace/ui/lib/utils';
<Button className={cn('w-full', active && 'ring-2')}>Save</Button>The @workspace/ui package needs matching exports for components and lib paths or the generated import will fail.
Apply only a preset themeapply-theme-preset
npx shadcn@4.16.2 apply a2r6bw --only themeOmitting --only can reinstall UI components; review the preset and commit current local changes first.
View a registry item before addinginspect-registry-item
npx shadcn@4.16.2 view button card
npx shadcn@4.16.2 view @acme/authNamespaced items can come from third-party registries, so inspect their files and dependencies as you would any external code.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| @mui/material | npm | You want a conventional versioned component dependency with a large API and centralized upgrades |
| @chakra-ui/react | npm | You prefer a packaged accessible component system with runtime theming and composition props |
| daisyui | npm | You want Tailwind component class names with less generated React source to maintain |