shadcn

Complete shadcn/ui component management for adding, searching, fixing, styling, and composing UI in any React project. Use this skill whenever the user mentions shadcn, shadcn/ui, adding UI components

SKILL.md

GitHub

shadcn/ui

A framework for building UI, components, and design systems. Components are added as source code directly into the project via the CLI.

IMPORTANT: Always run CLI commands using the project's package runner. Check packageManager from project context and substitute accordingly: npx shadcn@latest · pnpm dlx shadcn@latest · bunx --bun shadcn@latest All examples below use npx shadcn@latest.


Step 0 — Get Project Context First

Always do this before any other step.

npx shadcn@latest info --json

This returns the project config and installed components. Key fields to read:

Field What it controls
aliases Import prefix — use exactly, never hardcode @/
isRSC If true, any file using hooks/events/browser APIs needs "use client"
tailwindVersion v4 → @theme inline blocks; v3 → tailwind.config.js
tailwindCssFile The one global CSS file for variables — always edit this, never create a new one
base radix or base — affects asChild vs render prop and available APIs
iconLibrary Determines icon imports — lucide-react, @tabler/icons-react, etc. Never assume lucide
style Visual treatment — nova, vega, maia, etc.
resolvedPaths Exact file-system destinations for components, utils, hooks
framework Routing and file conventions (Next.js App Router, Vite SPA, etc.)
packageManager Use for any non-shadcn dependency installs
components Already installed — check before adding or importing anything

Principles

  1. Use existing components first. Search before writing custom UI.
  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 only. bg-primary, text-muted-foreground — never bg-blue-500.

Critical Rules

Violations of these rules are bugs, not preferences. Enforce them on every output.

Styling

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

Forms & Inputs

  • FieldGroup + Field for all form layout. Never use raw div with space-y-* or grid gap-*.
  • InputGroup requires InputGroupInput/InputGroupTextarea. Never raw Input/Textarea inside InputGroup.
  • Buttons inside inputs → InputGroup + InputGroupAddon.
  • 2–7 toggle options → ToggleGroup. Don't loop Button with manual active state.
  • Grouped checkboxes/radios → FieldSet + FieldLegend.
  • Validation → data-invalid on Field, aria-invalid on control.
  • Disabled → data-disabled on Field, disabled on control.

Composition

  • Items always inside their Group. SelectItem → SelectGroup. DropdownMenuItem → DropdownMenuGroup. CommandItem → CommandGroup.
  • Custom triggers → asChild (radix) or render (base). Always check the base field.
  • Dialog, Sheet, Drawer always need a Title. Use className="sr-only" if visually hidden.
  • Full Card composition. Always use CardHeader / CardTitle / CardDescription / CardContent / CardFooter.
  • Button has no isPending/isLoading. Compose with Spinner + data-icon + disabled.
  • TabsTrigger must be inside TabsList.
  • Avatar always needs AvatarFallback.

Use Components, Not Custom Markup

  • Callouts → Alert. Not custom styled divs.
  • Empty states → Empty. Not custom markup.
  • Toast → sonner. Use toast() from sonner.
  • Dividers → Separator. Not <hr> or <div className="border-t">.
  • Loading placeholders → Skeleton. No custom animate-pulse divs.
  • Status labels → Badge. Not custom styled spans.

Icons

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

CLI

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

Key Patterns

// ✅ Form layout: FieldGroup + Field
<FieldGroup>
  <Field>
    <FieldLabel htmlFor="email">Email</FieldLabel>
    <Input id="email" />
  </Field>
</FieldGroup>

// ✅ Validation: data-invalid on Field, aria-invalid on control
<Field data-invalid>
  <FieldLabel>Email</FieldLabel>
  <Input aria-invalid />
  <FieldDescription>Invalid email address.</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: semantic tokens or Badge variants
<Badge variant="secondary">+20.1%</Badge>          {/* correct */}
<span className="text-emerald-600">+20.1%</span>   {/* wrong   */}

// ✅ Button loading state: Spinner + data-icon + disabled
<Button disabled>
  <Spinner data-icon="inline-start" />
  Saving...
</Button>

Component Selection

Need Component
Action / submit Button with appropriate variant
Text input Input, Textarea, InputOTP
Selection Select, Combobox, RadioGroup, Checkbox, Switch, Slider
Toggle 2–5 options ToggleGroup + ToggleGroupItem
Date DatePicker (Calendar + Popover)
Data table Table
Info display Card, Badge, Avatar
Navigation Sidebar, NavigationMenu, Breadcrumb, Tabs, Pagination
Modal Dialog
Side panel Sheet
Bottom sheet Drawer
Confirmation AlertDialog
Toast / notification sonner → toast()
Inline feedback Alert
Progress Progress, Spinner
Loading placeholder Skeleton
Command palette Command inside Dialog
Charts Chart (wraps Recharts)
Layout & structure Card, Separator, Resizable, ScrollArea, Accordion, Collapsible
Empty states Empty
Dropdown menus DropdownMenu, ContextMenu, Menubar
Tooltips / info Tooltip, HoverCard, Popover

Workflow

Follow this sequence every time:

1. Get project context

npx shadcn@latest info --json

Re-run any time config may have changed.

2. Check installed components Before running add, check the components list from project context or list resolvedPaths.ui. Do not import components not yet added. Do not re-add already installed ones.

3. Find components

npx shadcn@latest search @shadcn -q "sidebar"
npx shadcn@latest search @magicui -q "shimmer"
npx shadcn@latest search @tailark -q "stats"

4. Get docs and examples

npx shadcn@latest docs button dialog select

Fetch the returned URLs. Always do this before creating, fixing, or composing a component.

5. Preview before installing

npx shadcn@latest add button --dry-run
npx shadcn@latest add button --diff button.tsx

6. Install

npx shadcn@latest add button card dialog
npx shadcn@latest add @magicui/shimmer-button

7. Fix imports in third-party components After adding from community registries, check added non-UI files for hardcoded @/components/ui/... paths. Use npx shadcn@latest info to get the correct ui alias and rewrite accordingly.

8. Review added files After every add, read the added files and verify:

  • No missing sub-components (e.g. SelectItem without SelectGroup)
  • All imports correct
  • Composition follows Critical Rules
  • Icons match project's iconLibrary — swap if different

9. Never guess the registry If the user says "add a login block" without specifying @shadcn, @tailark, etc., ask which registry. Never default.


Updating Components (Smart Merge)

When updating a component while preserving local changes — never fetch raw files from GitHub manually.

# 1. See all files that would change
npx shadcn@latest add <component> --dry-run

# 2. Inspect diff for each file
npx shadcn@latest add <component> --diff <file>

# 3. Decide per file:
#    - No local changes → safe to overwrite
#    - Has local changes → read local, apply upstream changes, preserve modifications
#    - User says "update everything" → use --overwrite, but confirm first

# ⚠️ Never use --overwrite without explicit user approval

Switching Presets

Always ask the user first: overwrite, merge, or skip?

Mode Command Effect
Overwrite npx shadcn@latest apply --preset <code> Overwrites components, fonts, CSS variables
Merge npx shadcn@latest init --preset <code> --force --no-reinstall then diff each component Updates config and CSS, smart-merges components
Skip npx shadcn@latest init --preset <code> --force --no-reinstall Updates config and CSS only, leaves components untouched

Always run preset commands inside the project directory. apply requires an existing components.json. The CLI preserves the current base automatically. In temp directories, pass --base <current-base> explicitly.


Quick Reference

# New project
npx shadcn@latest init --name my-app --preset base-nova
npx shadcn@latest init --name my-app --preset a2r6bw --template vite

# Monorepo
npx shadcn@latest init --name my-app --preset base-nova --monorepo

# Existing project
npx shadcn@latest init --preset base-nova
npx shadcn@latest init --defaults

# Apply preset to existing project
npx shadcn@latest apply --preset a2r6bw

# Add components
npx shadcn@latest add button card dialog
npx shadcn@latest add --all

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

# Get docs
npx shadcn@latest docs button dialog

# View without installing
npx shadcn@latest view @shadcn/button

Named presets: nova · vega · maia · lyra · mira · luma Templates: next · vite · start · react-router · astro · laravel Preset codes: version-prefixed base62 strings from ui.shadcn.com


Reference Files

Read these when the task involves the corresponding domain.

  • 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 — Full command reference, flags, presets, templates
  • customization.md — Theming, CSS variables, extending components