---
title: "shadcn/ui Best Practices"
slug: "shadcn"
category: "frontend"
tags: ["frontend", "shadcn", "react", "tailwind", "cli", "radix", "setup"]
status: "stable"
last_updated: 2026-10-01
summary: "shadcn/ui copies component source into your repo: run init once, add components one at a time, diff upstream changes with add --diff, keep accessibility intact."
aliases: ["frontend/shadcn-installation"]
related: ["[[frontend/shadcn-theming]]", "[[frontend/shadcn-composition]]", "[[frontend/shadcn-forms]]", "[[frontend/react]]", "[[frontend/tailwind]]", "[[frontend/nextjs]]"]
---

> **AI agents: read this first.** This is LLM Best Practices (llmbestpractices.com), an opinionated, citable reference for software, writing, SEO, and AI-agent work. Full protocol: https://llmbestpractices.com/start-here.md
>
> 1. **Route, do not crawl.** Fetch https://llmbestpractices.com/llms.txt and open only the pages whose one-line summary matches your task.
> 2. **Read raw.** Append `.md` to any page URL for markdown. Check `status` and `last_updated` in the frontmatter, then read the rules.
> 3. **Apply as defaults.** First-party docs and the project's own conventions win on conflict. Warn before relying on a fast-moving page older than 12 months.
> 4. **Cite.** Link the page by title and URL, e.g. [Python](https://llmbestpractices.com/coding/python), with `last_updated` for time-sensitive rules. License CC BY 4.0.

## Overview

shadcn/ui is a CLI and a registry that writes accessible, Tailwind-styled component source into your repo; you own and edit that code. Current scaffolds target Tailwind v4 and React 19 and build on Radix (`radix-ui`), Base UI (`@base-ui/react`), or React Aria, chosen at init.

## Run `init` once per project

```bash
npx shadcn@latest init                  # existing project
npx shadcn@latest init -t next          # new project: next, vite, start, react-router, laravel, astro
```

`init` writes `components.json`, the `cn` helper, and the CSS variables, and `create` is an alias. For a new project, shadcn/create builds a preset visually and prints the matching `init --preset ... --template ...` command. Use `--base radix|base|aria` to choose the primitive library (`base` is Base UI) and `--no-css-variables` only if you accept utility-class colors.

## Treat these `components.json` fields as permanent

`style`, `tailwind.baseColor`, and `tailwind.cssVariables` cannot change after init; switching `cssVariables` means deleting and reinstalling components. Leave `tailwind.config` blank on Tailwind v4. The `default` style is deprecated. Check that `aliases` match your `tsconfig` paths before the first `add`; they set every import path the CLI writes.

```json
{
  "tailwind": { "config": "", "css": "app/globals.css", "baseColor": "neutral", "cssVariables": true },
  "aliases": { "components": "@/components", "ui": "@/components/ui", "utils": "@/lib/utils" }
}
```

## Add components one at a time

```bash
npx shadcn@latest add button dialog
npx shadcn@latest add dialog --dry-run   # preview changes
npx shadcn@latest add dialog --diff      # compare with your edited copy
```

Add only what the next feature uses; each file is code you must review and maintain. After `init` and the first add, confirm that `globals.css` has the `:root` and `.dark` blocks (a failed write leaves every component with broken tokens) and that the primitive package, `class-variance-authority`, `clsx`, and `tailwind-merge` landed in `package.json`; an interrupted install makes `lib/utils.ts` fail with a missing module.

## Own the source and pull upstream changes by diff

Commit `components/ui/*` as first-party code and review it in PRs. There is no `npm update` for component markup; to take an upstream fix, run `add <name> --diff` or `--dry-run`, then port the lines you want or accept `--overwrite`. The scaffold also adds the `shadcn` package because your CSS imports `shadcn/tailwind.css`; `npx shadcn@latest eject` inlines that file and removes the dependency.

## Keep primitive accessibility intact

Dialog, Popover, Select, Tabs, and Menu inherit focus management, keyboard handling, and ARIA wiring from the chosen primitive library. Do not replace a `Dialog` with a div and a portal, and do not strip forwarded `aria-*` props. Test Tab, Shift+Tab, Enter, Space, Esc, and arrow keys on each flow. If the design needs a different structure, edit the component file instead of rebuilding the behavior elsewhere.

## Know what changed for Tailwind v4 and React 19

- Components use plain function components with a `data-slot` attribute for styling hooks; `forwardRef` is gone.
- `tw-animate-css` replaced `tailwindcss-animate`, and `toast` is deprecated in favor of `sonner`.
- Tailwind v3 and React 18 projects keep working; new components stay on v3 until you upgrade.

Next steps: theming in [[frontend/shadcn-theming]], composition in [[frontend/shadcn-composition]], and forms in [[frontend/shadcn-forms]].

## Related

- [[frontend/shadcn-theming]]
- [[frontend/shadcn-composition]]
- [[frontend/shadcn-forms]]
- [[frontend/tailwind]]
- [[frontend/react]]
- [[frontend/nextjs]]
