Skip to content

shadcn/ui

Verified

shadcn/ui component library skill. Install, customize, and compose accessible React components with Radix UI and Tailwind.

By shadcn 18,100 stars v1.0 Updated 2026-03-15
$ Add to .claude/skills/

About This Skill

# shadcn/ui

A framework for building ui, components and design systems. Components are added as source code to the user's project via the CLI.

> IMPORTANT: Run all CLI commands using the project's package runner: `npx shadcn@latest`, `pnpm dlx shadcn@latest`, or `bunx --bun shadcn@latest` — based on the project's `packageManager`. Examples below use `npx shadcn@latest` but substitute the correct runner for the project.

Current Project Context

```json !`npx shadcn@latest info --json` ```

The JSON above contains the project config and installed components. Use `npx shadcn@latest docs <component>` to get documentation and example URLs for any component.

Principles

  1. Use existing components first. Use `npx shadcn@latest search` to check registries before writing custom UI. Check community registries too.
  2. Compose, don't reinvent. Settings page = Tabs + Card + form controls. Dashboard = Sidebar + Card + Chart + Table.
  3. Use built-in variants before custom styles. `variant="outline"`, `size="sm"`, etc.
  4. Use semantic colors. `bg-primary`, `text-muted-foreground` — never raw values like `bg-blue-500`.

Critical Rules

These rules are always enforced. Each links to a file with Incorrect/Correct code pairs.

Styling & Tailwind → [styling.md](./rules/styling.md)

  • `className` for layout, not styling. Never override component colors or typography.
  • No `space-x-*` or `space-y-*`. Use `flex` with `gap-*`. For vertical stacks, `flex flex-col gap-*`.
  • Use `size-*` when width and height are equal. `size-10` not `w-10 h-10`.
  • Use `truncate` shorthand. Not `overflow-hidden text-ellipsis whitespace-nowrap`.
  • No manual `dark:` color overrides. Use semantic tokens (`bg-background`, `text-muted-foreground`).
  • Use `cn()` for conditional classes. Don't write manual template literal ternaries.
  • No manual `z-index` on overlay components. Dialog, Sheet, Popover, etc. handle their own stacking.

Forms & Inputs → [forms.md](./rules/forms.md)

  • Forms use `FieldGroup` + `Field`. Never use raw `div` with `space-y-*` or `grid gap-*` for form layout.
  • `InputGroup` uses `InputGroupInput`/`InputGroupTextarea`. Never raw `Input`/`Textarea` inside `InputGroup`.
  • Buttons inside inputs use `InputGroup` + `InputGroupAddon`.
  • Option sets (2–7 choices) use `ToggleGroup`. Don't loop `Button` with manual active state.
  • `FieldSet` + `FieldLegend` for grouping related checkboxes/radios. Don't use a `div` with a heading.
  • Field validation uses `data-invalid` + `aria-invalid`. `data-invalid` on `Field`, `aria-invalid` on the control. For disabled: `data-disabled` on `Field`, `disabled` on the control.

Component Structure → [composition.md](./rules/composition.md)

  • Items always inside their Group. `SelectItem` → `SelectGroup`. `DropdownMenuItem` → `DropdownMenuGroup`. `CommandItem` → `CommandGroup`.
  • Use `asChild` (radix) or `render` (base) for custom triggers. Check `base` field from `npx shadcn@latest info`. → base-vs-radix.md
  • Dialog, Sheet, and Drawer always need a Title. `DialogTitle`, `SheetTitle`, `DrawerTitle` required for accessibility. Use `className="sr-only"` if visually hidden.
  • Use full Card composition. `CardHeader`/`CardTitle`/`CardDescription`/`CardContent`/`CardFooter`. Don't dump everything in `CardContent`.
  • Button has no `isPending`/`isLoading`. Compose with `Spinner` + `data-icon` + `disabled`.
  • `TabsTrigger` must be inside `TabsList`. Never render triggers directly in `Tabs`.
  • `Avatar` always needs `AvatarFallback`. For when the image fails to load.

Use Components, Not Custom Markup → [composition.md](./rules/composition.md)

  • Use existing components before custom markup. Check if a component exists before writing a styled `div`.
  • Callouts use `Alert`. Don't build custom styled divs.
  • Empty states use `Empty`. Don't build custom empty state markup.
  • Toast via `sonner`. Use `toast()` from `sonner`.
  • Use `Separator` instead of `<hr>` or `<div className="border-t">`.
  • Use `Skeleton` for loading placeholders. No custom `animate-pulse` divs.
  • Use `Badge` instead of custom styled spans.

Icons → [icons.md](./rules/icons.md)

  • Icons in `Button` use `data-icon`. `data-icon="inline-start"` or `data-icon="inline-end"` on the icon.
  • No sizing classes on icons inside components. Components handle icon sizing via CSS. No `size-4` or `w-4 h-4`.
  • Pass icons as objects, not string keys. `icon={CheckIcon}`, not a string lookup.

CLI

  • Never decode or fetch preset codes manually. Pass them directly to `npx shadcn@latest init --preset <code>`.

Key Patterns

These are the most common patterns that differentiate correct shadcn/ui code. For edge cases, see the linked rule files above.

```tsx // Form layout: FieldGroup + Field, not div + Label. <FieldGroup> <Field> <FieldLabel htmlFor="email">Email</FieldLabel> <Input id="email" /> </Field> </FieldGroup>

// Validation: data-invalid on Field, aria-invalid on the control. <Field data-invalid> <FieldLabel>Email</FieldLabel> <Input aria-invalid /> <FieldDescription>Invalid email.</FieldDescription> </Field>

// Icons in buttons: data-icon, no sizing classes. <Button> <SearchIcon data-icon="inline-start" /> Search </Button>

// Spacing: gap-*, not space-y-*. <div className="flex flex-col gap-4"> // correct <div className="space-y-4"> // wrong

// Equal dimensions: size-*, not w-* h-*. <Avatar className="size-10"> // correct <Avatar className="w-10 h-10"> // wrong

// Status colors: Badge variants or semantic tokens, not raw colors. <Badge variant="secondary">+20.1%</Badge> // correct <span className="text-emerald-600">+20.1%</span> // wrong ```

Component Selection

| Need | Use | | -------------------------- | --------------------------------------------------------------------------------------------------- | | Button/action | `Button` with appropriate variant | | Form inputs | `Input`, `Select`, `Combobox`, `Switch`, `Checkbox`, `RadioGroup`, `Textarea`, `InputOTP`, `Slider` | | Toggle between 2–5 options | `ToggleGroup` + `ToggleGroupItem` | | Data display | `Table`, `Card`, `Badge`, `Avatar` | | Navigation | `Sidebar`, `NavigationMenu`, `Breadcrumb`, `Tabs`, `Pagination` | | Overlays | `Dialog` (modal), `Sheet` (side panel), `Drawer` (bottom sheet), `AlertDialog` (confirmation) | | Feedback | `sonner` (toast), `Alert`, `Progress`, `Skeleton`, `Spinner` | | Command palette | `Command` inside `Dialog` | | Charts | `Chart` (wraps Recharts) | | Layout | `Card`, `Separator`, `Resizable`, `ScrollArea`, `Accordion`, `Collapsible` | | Empty states | `Empty` | | Menus | `DropdownMenu`, `ContextMenu`, `Menubar` | | Tooltips/info | `Tooltip`, `HoverCard`, `Popover` |

Key Fields

The injected project context contains these key fields:

  • `aliases` → use the actual alias prefix for imports (e.g. `@/`, `~/`), never hardcode.
  • `isRSC` → when `true`, components using `useState`, `useEffect`, event handlers, or browser APIs need `"use client"` at the top of the file. Always reference this field when advising on the directive.
  • `tailwindVersion` → `"v4"` uses `@theme inline` blocks; `"v3"` uses `tailwind.config.js`.
  • `tailwindCssFile` → the global CSS file where custom CSS variables are defined. Always edit this file, never create a new one.
  • `style` → component visual treatment (e.g. `nova`, `vega`).
  • `base` → primitive library (`radix` or `base`). Affects component APIs and available props.
  • `iconLibrary` → determines icon imports. Use `lucide-react` for `lucide`, `@tabler/icons-react` for `tabler`, etc. Never assume `lucide-react`.
  • `resolvedPaths` → exact file-system destinations for components, utils, hooks, etc.
  • `framework` → routing and file conventions (e.g. Next.js App Router vs Vite SPA).
  • `packageManager` → use this for any non-shadcn dependency installs (e.g. `pnpm add date-fns` vs `npm install date-fns`).

See cli.md — `info` command for the full field reference.

Component Docs, Examples, and Usage

Run `npx shadcn@latest docs <component>` to get the URLs for a component's documentation, examples, and API reference. Fetch these URLs to get the actual content.

```bash npx shadcn@latest docs button dialog select ```

When creating, fixing, debugging, or using a component, always run `npx shadcn@latest docs` and fetch the URLs first. This ensures you're working with the correct API and usage patterns rather than guessing.

Workflow

  1. Get project context — already injected above. Run `npx shadcn@latest info` again if you need to refresh.
  2. Check installed components first — before running `add`, always check the `components` list from project context or list the `resolvedPaths.ui` directory. Don't import components that haven't been added, and don't re-add ones already installed.
  3. Find components — `npx shadcn@latest search`.
  4. Get docs and examples — run `npx shadcn@latest docs <component>` to get URLs, then fetch them. Use `npx shadcn@latest view` to browse registry items you haven't installed. To preview changes to installed components, use `npx shadcn@latest add --diff`.
  5. Install or update — `npx shadcn@latest add`. When updating existing components, use `--dry-run` and `--diff` to preview changes first (see Updating Components below).
  6. Fix imports in third-party components — After adding components from community registries (e.g. `@bundui`, `@magicui`), check the added non-UI files for hardcoded import paths like `@/components/ui/...`. These won't match the project's actual aliases. Use `npx shadcn@latest info` to get the correct `ui` alias (e.g. `@workspace/ui/components`) and rewrite the imports accordingly. The CLI rewrites imports for its own UI files, but third-party registry components may use default paths that don't match the project.
  7. Review added components — After adding a component or block from any registry, always read the added files and verify they are correct. Check for missing sub-components (e.g. `SelectItem` without `SelectGroup`), missing imports, incorrect composition, or violations of the Critical Rules. Also replace any icon imports with the project's `iconLibrary` from the project context (e.g. if the registry item uses `lucide-react` but the project uses `hugeicons`, swap the imports and icon names accordingly). Fix all issues before moving on.
  8. Registry must be explicit — When the user asks to add a block or component, do not guess the registry. If no registry is specified (e.g. user says "add a login block" without specifying `@shadcn`, `@tailark`, etc.), ask which registry to use. Never default to a registry on behalf of the user.
  9. Switching presets — Ask the user first: reinstall, merge, or skip?
  10. - Reinstall: `npx shadcn@latest init --preset <code> --force --reinstall`. Overwrites all components.
  11. - Merge: `npx shadcn@latest init --preset <code> --force --no-reinstall`, then run `npx shadcn@latest info` to list installed components, then for each installed component use `--dry-run` and `--diff` to smart merge it individually.
  12. - Skip: `npx shadcn@latest init --preset <code> --force --no-reinstall`. Only updates config and CSS, leaves components as-is.
  13. - Important: Always run preset commands inside the user's project directory. The CLI automatically preserves the current base (`base` vs `radix`) from `components.json`. If you must use a scratch/temp directory (e.g. for `--dry-run` comparisons), pass `--base <current-base>` explicitly — preset codes do not encode the base.

Updating Components

When the user asks to update a component from upstream while keeping their local changes, use `--dry-run` and `--diff` to intelligently merge. NEVER fetch raw files from GitHub manually — always use the CLI.

  1. Run `npx shadcn@latest add <component> --dry-run` to see all files that would be affected.
  2. For each file, run `npx shadcn@latest add <component> --diff <file>` to see what changed upstream vs local.
  3. Decide per file based on the diff:
  4. - No local changes → safe to overwrite.
  5. - Has local changes → read the local file, analyze the diff, and apply upstream updates while preserving local modifications.
  6. - User says "just update everything" → use `--overwrite`, but confirm first.
  7. Never use `--overwrite` without the user's explicit approval.

Quick Reference

```bash # Create a new project. npx shadcn@latest init --name my-app --preset base-nova npx shadcn@latest init --name my-app --preset a2r6bw --template vite

# Create a monorepo project. npx shadcn@latest init --name my-app --preset base-nova --monorepo npx shadcn@latest init --name my-app --preset base-nova --template next --monorepo

# Initialize existing project. npx shadcn@latest init --preset base-nova npx shadcn@latest init --defaults # shortcut: --template=next --preset=base-nova

# Add components. npx shadcn@latest add button card dialog npx shadcn@latest add @magicui/shimmer-button npx shadcn@latest add --all

# Preview changes before adding/updating. npx shadcn@latest add button --dry-run npx shadcn@latest add button --diff button.tsx npx shadcn@latest add @acme/form --view button.tsx

# Search registries. npx shadcn@latest search @shadcn -q "sidebar" npx shadcn@latest search @tailark -q "stats"

# Get component docs and example URLs. npx shadcn@latest docs button dialog select

# View registry item details (for items not yet installed). npx shadcn@latest view @shadcn/button ```

Named presets: `base-nova`, `radix-nova` Templates: `next`, `vite`, `start`, `react-router`, `astro` (all support `--monorepo`) and `laravel` (not supported for monorepo) Preset codes: Base62 strings starting with `a` (e.g. `a2r6bw`), from ui.shadcn.com.

Detailed References

  • rules/forms.md — FieldGroup, Field, InputGroup, ToggleGroup, FieldSet, validation states
  • rules/composition.md — Groups, overlays, Card, Tabs, Avatar, Alert, Empty, Toast, Separator, Skeleton, Badge, Button loading
  • rules/icons.md — data-icon, icon sizing, passing icons as objects
  • rules/styling.md — Semantic colors, variants, className, spacing, size, truncate, dark mode, cn(), z-index
  • rules/base-vs-radix.md — asChild vs render, Select, ToggleGroup, Slider, Accordion
  • cli.md — Commands, flags, presets, templates
  • customization.md — Theming, CSS variables, extending components

Use Cases

  • Build modern UI components using shadcn/ui with Tailwind CSS and Radix primitives
  • Add and customize shadcn/ui components in Next.js and React projects
  • Create consistent design systems using shadcn/ui's copy-paste component model
  • Implement accessible, production-ready UI components without heavy dependencies
  • Customize component styling and behavior by editing source code directly

Pros & Cons

Pros

  • +Copy-paste model gives full ownership of component code — no library lock-in
  • +Built on Radix UI primitives ensuring accessibility compliance out of the box
  • +Tailwind CSS integration provides consistent and customizable styling

Cons

  • -React/Next.js specific — not usable with Vue, Svelte, or other frameworks
  • -Requires Tailwind CSS setup — adds complexity for projects not already using Tailwind

FAQ

What does shadcn/ui do?
shadcn/ui component library skill. Install, customize, and compose accessible React components with Radix UI and Tailwind.
What platforms support shadcn/ui?
shadcn/ui is available on Claude Code, Cursor.
What are the use cases for shadcn/ui?
Build modern UI components using shadcn/ui with Tailwind CSS and Radix primitives. Add and customize shadcn/ui components in Next.js and React projects. Create consistent design systems using shadcn/ui's copy-paste component model.

100+ free AI tools

Writing, PDF, image, and developer tools — all in your browser.

Next Step

Use the skill detail page to evaluate fit and install steps. For a direct browser workflow, move into a focused tool route instead of staying in broader support surfaces.