Engineering6 min read

shadcn/ui components explained: the CLI, registry, and theming

How the copy-into-your-repo model works in 2026, what components.json controls, which primitives sit underneath, and when to wrap it into a fuller system.

The shadcn/ui documentation opens with a line that surprises first-time readers: "This is not a component library." It means it. You never install shadcn/ui components as a dependency; you run a command, and the source code for a button or a dialog lands in your repository, yours to edit.

shadcn/ui is a set of accessible React components styled with Tailwind CSS, plus a CLI and registry format for distributing component source code. Instead of importing components from a package, you copy each one into your project with npx shadcn add, and from then on you own it. As of October 2026, new projects build those components on Base UI by default, with Radix and React Aria as supported alternatives.

Key takeaways

  • shadcn/ui distributes source code, not a package, so every component you add becomes your code to change, test, and maintain.
  • components.json records how your project is set up, and a few of its choices, including style, base color, and CSS variables, cannot be changed after init.
  • Since July 2026, shadcn init uses Base UI by default; Radix remains fully supported and can be chosen with -b radix.
  • Theming runs on semantic CSS variables in background and foreground pairs, exposed to Tailwind through @theme inline.
  • Teams outgrow plain shadcn/ui when several apps, a Figma library, or AI tools must share one set of components.

How does the copy-into-your-repo model work?

npx shadcn init sets up your project: it writes components.json, adds the theme variables to your global CSS, and installs the small helpers components rely on. Since September 2026 that includes the standalone cn package, a drop-in replacement for the old twMerge(clsx(...)) helper. Then npx shadcn add dialog writes the dialog's source into your components folder and installs its dependencies.

The docs frame this as open code: the top layer of every component is open for modification, so you change the source rather than overriding styles from outside. The trade is maintenance, because upstream fixes never arrive automatically. The CLI helps: add takes --dry-run and --diff to preview changes before overwriting, and migrate runs scripted upgrades such as migrate cn and migrate radix.

You are not upgrading a dependency; you are merging changes into code you own.

What does components.json control?

components.json tells the CLI where files go and how your project is styled:

FieldWhat it controlsChange after init?
styleThe component style the CLI installsNo
tailwind.cssThe CSS file that imports TailwindYes
tailwind.baseColorThe neutral palette for default tokens, such as neutral or zincNo
tailwind.cssVariablesSemantic CSS variables (true) or inline color utilities (false)No, without reinstalling components
rsc and tsxServer Component directives and TypeScript outputYes
aliasesImport paths for ui, components, lib, hooks, and utilsYes
registriesExtra registries by namespace, such as @acmeYes

For Tailwind CSS v4 projects, leave tailwind.config blank; configuration lives in CSS. shadcn/ui has supported Tailwind v4 since February 2025.

Which primitives does shadcn/ui build on today?

Behavior comes from a headless library underneath each component, and the answer changed in July 2026, when Base UI became the default for new projects and for the docs. The changelog cites Base UI's stability as the reason and states that Radix is not deprecated. Existing Radix projects keep working, new components ship for both libraries unless they exist only in Base UI, and shadcn init -b radix still starts a Radix project.

Earlier in 2026, the Radix version moved to the single radix-ui package, and shadcn migrate radix rewrites older imports. The CLI also accepts -b aria for a React Aria base. Migrating an existing Radix project to Base UI is optional, and the changelog describes a progressive, one-component-at-a-time path.

How does shadcn/ui theming work?

Components use semantic utilities such as bg-background, text-foreground, and bg-primary rather than raw palette colors. The values live in CSS variables under :root, with dark values overriding the same variables under .dark. Each surface token has a foreground partner: primary and primary-foreground, muted and muted-foreground, card and card-foreground. The defaults use OKLCH.

The set also covers destructive, the form trio of border, input, and ring, numbered chart colors, and a sidebar-* group. A single --radius drives the whole radius scale. An @theme inline block maps each variable to a Tailwind color, so --color-primary: var(--primary) produces bg-primary.

Adding a color takes three steps: define --warning and --warning-foreground in both modes, expose --color-warning in @theme inline, then use bg-warning in components.

Blocks, charts, and the registry

Blocks are larger compositions you add the same way as components: npx shadcn add dashboard-01 installs a dashboard with a sidebar, charts, and a data table. Sidebar, login, and signup blocks follow the same pattern, and blocks have been available for both Radix and Base UI since February 2026.

Charts are built on Recharts v3, and shadcn/ui deliberately does not wrap it. You compose Recharts components directly inside a ChartContainer, add ChartTooltip and ChartLegend where you want shadcn styling, and define labels and colors in a ChartConfig that points at your chart color variables. When Recharts ships a new version, you follow its upgrade path, not an abstraction's.

The registry is the distribution format behind all of this, and you can run your own. Describe items in a registry.json, run shadcn build to output JSON, and host it. Teams then install with a namespace, such as npx shadcn add @acme/button, configured in the registries field with optional headers and ${VAR} environment variables for private access. Since August 2026 the CLI can also install from private GitHub repositories. The CLI has also included an MCP server since version 3.0 in August 2025.

When should you wrap shadcn/ui into a fuller design system?

shadcn/ui gives you excellent components and a distribution format. It does not give you design files, usage guidance, or a token pipeline. Run the five-signal wrap test; two or more yeses mean it is time:

  1. More than one app consumes your components. Copies drift; publish a private registry under your own namespace.
  2. Designers work from a Figma library. Names, variants, and tokens need to match on both sides.
  3. Your brand goes beyond color and radius. You are adding variants and states the defaults don't have.
  4. You need proof, not assumption. Each component needs documented keyboard paths, focus states, and tests.
  5. AI tools generate UI in your codebase. They need a catalog of your components, or they will write new ones.

Wrapping means freezing your base and style, owning the token layer, documenting each component in Storybook, and distributing through your registry instead of copying between repositories.

Where Pegasus fits

Pegasus is one answer to the wrap test, built on shadcn/ui rather than around it. Its React components are written in TypeScript on shadcn/ui, Base UI primitives, recharts for charts, and Tailwind CSS. That is the Base UI base, the shadcn/ui default since July 2026, and shadcn/ui deserves the credit for that foundation.

What Pegasus adds is the system around it. A Figma library of 1,540 components uses the same component names, variant structure, and tokens as the code. One token source themes both the 470 Figma variables and the code's theme object. Components are built to WCAG 2.1 AA, with keyboard paths, focus states, and contrast documented per component, and Storybook carries stories and tests. A local MCP server exposes the catalog (components, props, variants, tokens) to clients such as Claude Code and Cursor, so AI tools compose from real components.

FAQ

Is shadcn/ui free to use?

Yes. The shadcn/ui site describes its blocks as open source and free, and the CLI copies component source into your project rather than selling access to a package. The cost is maintenance: once the code is in your repository, keeping it current is your job.

Does shadcn/ui use Radix or Base UI?

Both. As of October 2026, new projects use Base UI by default, a change made in July 2026. Radix is still fully supported: run shadcn init -b radix to start a Radix project, and existing Radix projects need no migration. The CLI also supports React Aria with -b aria.

Can I update shadcn/ui components after I've edited them?

Yes, but deliberately. Because the code is yours, updates are merges rather than version bumps. Use npx shadcn add with --dry-run or --diff to see what would change before overwriting, and run scripted migrations such as migrate cn when the project changes its conventions.

Sources

One system. In Figma and in code.

Pegasus ships 1,540 components, 470 tokens, matching React code, Storybook, and a local MCP server. $149, one-time.

Get the full system