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
- Use existing components first. Search before writing custom UI.
- Compose, don't reinvent. Settings page = Tabs + Card + form controls. Dashboard = Sidebar + Card + Chart + Table.
- Use built-in variants before custom styles.
variant="outline", size="sm", etc.
- 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
1---2name: shadcn3description: 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, building forms or dashboards with Radix, working with component registries (@shadcn, @magicui, @tailark), or any task involving npx shadcn CLI commands. Also trigger when the user asks to fix styling issues, build a settings page, dashboard, or modal flow in a project that uses shadcn/ui — even if they don't say "shadcn" explicitly. This skill manages the full component lifecycle: search, add, view, diff, smart-merge upstream updates, and enforce critical rules for forms, composition, styling, and icons.4---56# shadcn/ui78A framework for building UI, components, and design systems.9Components are added as source code directly into the project via the CLI.1011> **IMPORTANT:** Always run CLI commands using the project's package runner.12> Check `packageManager` from project context and substitute accordingly:13> `npx shadcn@latest` · `pnpm dlx shadcn@latest` · `bunx --bun shadcn@latest`14> All examples below use `npx shadcn@latest`.1516---1718## Step 0 — Get Project Context First1920**Always do this before any other step.**2122```bash23npx shadcn@latest info --json24```2526This returns the project config and installed components. Key fields to read:2728| Field | What it controls |29|---|---|30| `aliases` | Import prefix — use exactly, never hardcode `@/` |31| `isRSC` | If `true`, any file using hooks/events/browser APIs needs `"use client"` |32| `tailwindVersion` | `v4` → `@theme inline` blocks; `v3` → `tailwind.config.js` |33| `tailwindCssFile` | The one global CSS file for variables — always edit this, never create a new one |34| `base` | `radix` or `base` — affects `asChild` vs `render` prop and available APIs |35| `iconLibrary` | Determines icon imports — `lucide-react`, `@tabler/icons-react`, etc. Never assume lucide |36| `style` | Visual treatment — `nova`, `vega`, `maia`, etc. |37| `resolvedPaths` | Exact file-system destinations for components, utils, hooks |38| `framework` | Routing and file conventions (Next.js App Router, Vite SPA, etc.) |39| `packageManager` | Use for any non-shadcn dependency installs |40| `components` | Already installed — check before adding or importing anything |4142---4344## Principles45461. **Use existing components first.** Search before writing custom UI.472. **Compose, don't reinvent.** Settings page = Tabs + Card + form controls. Dashboard = Sidebar + Card + Chart + Table.483. **Use built-in variants before custom styles.** `variant="outline"`, `size="sm"`, etc.494. **Use semantic colors only.** `bg-primary`, `text-muted-foreground` — never `bg-blue-500`.5051---5253## Critical Rules5455Violations of these rules are bugs, not preferences. Enforce them on every output.5657### Styling5859- **`className` for layout only.** Never override component colors or typography via className.60- **No `space-x-*` / `space-y-*`.** Use `flex` with `gap-*`. Vertical stacks: `flex flex-col gap-*`.61- **`size-*` when width = height.** `size-10` not `w-10 h-10`.62- **`truncate` shorthand.** Not `overflow-hidden text-ellipsis whitespace-nowrap`.63- **No manual `dark:` color overrides.** Use semantic tokens (`bg-background`, `text-muted-foreground`).64- **`cn()` for conditional classes.** Never write manual template literal ternaries.65- **No manual `z-index` on overlay components.** Dialog, Sheet, Popover manage their own stacking.6667### Forms & Inputs6869- **`FieldGroup` + `Field` for all form layout.** Never use raw `div` with `space-y-*` or `grid gap-*`.70- **`InputGroup` requires `InputGroupInput`/`InputGroupTextarea`.** Never raw `Input`/`Textarea` inside `InputGroup`.71- **Buttons inside inputs → `InputGroup` + `InputGroupAddon`.**72- **2–7 toggle options → `ToggleGroup`.** Don't loop `Button` with manual active state.73- **Grouped checkboxes/radios → `FieldSet` + `FieldLegend`.**74- **Validation → `data-invalid` on `Field`, `aria-invalid` on control.**75- **Disabled → `data-disabled` on `Field`, `disabled` on control.**7677### Composition7879- **Items always inside their Group.** `SelectItem` → `SelectGroup`. `DropdownMenuItem` → `DropdownMenuGroup`. `CommandItem` → `CommandGroup`.80- **Custom triggers → `asChild` (radix) or `render` (base).** Always check the `base` field.81- **Dialog, Sheet, Drawer always need a Title.** Use `className="sr-only"` if visually hidden.82- **Full Card composition.** Always use `CardHeader` / `CardTitle` / `CardDescription` / `CardContent` / `CardFooter`.83- **Button has no `isPending`/`isLoading`.** Compose with `Spinner` + `data-icon` + `disabled`.84- **`TabsTrigger` must be inside `TabsList`.**85- **`Avatar` always needs `AvatarFallback`.**8687### Use Components, Not Custom Markup8889- **Callouts → `Alert`.** Not custom styled divs.90- **Empty states → `Empty`.** Not custom markup.91- **Toast → `sonner`.** Use `toast()` from `sonner`.92- **Dividers → `Separator`.** Not `<hr>` or `<div className="border-t">`.93- **Loading placeholders → `Skeleton`.** No custom `animate-pulse` divs.94- **Status labels → `Badge`.** Not custom styled spans.9596### Icons9798- **Icons in `Button` use `data-icon`.** `data-icon="inline-start"` or `data-icon="inline-end"` on the icon element.99- **No sizing classes on icons inside components.** No `size-4` or `w-4 h-4` — components handle sizing via CSS.100- **Pass icons as objects, not strings.** `icon={CheckIcon}` not a string lookup.101102### CLI103104- **Never decode or fetch preset codes manually.** Pass them directly to `npx shadcn@latest apply --preset <code>`.105106---107108## Key Patterns109110```tsx111// ✅ Form layout: FieldGroup + Field112<FieldGroup>113 <Field>114 <FieldLabel htmlFor="email">Email</FieldLabel>115 <Input id="email" />116 </Field>117</FieldGroup>118119// ✅ Validation: data-invalid on Field, aria-invalid on control120<Field data-invalid>121 <FieldLabel>Email</FieldLabel>122 <Input aria-invalid />123 <FieldDescription>Invalid email address.</FieldDescription>124</Field>125126// ✅ Icons in buttons: data-icon, no sizing classes127<Button>128 <SearchIcon data-icon="inline-start" />129 Search130</Button>131132// ✅ Spacing: gap-*, not space-y-*133<div className="flex flex-col gap-4"> {/* correct */}134<div className="space-y-4"> {/* wrong */}135136// ✅ Equal dimensions: size-*, not w-* h-*137<Avatar className="size-10"> {/* correct */}138<Avatar className="w-10 h-10"> {/* wrong */}139140// ✅ Status colors: semantic tokens or Badge variants141<Badge variant="secondary">+20.1%</Badge> {/* correct */}142<span className="text-emerald-600">+20.1%</span> {/* wrong */}143144// ✅ Button loading state: Spinner + data-icon + disabled145<Button disabled>146 <Spinner data-icon="inline-start" />147 Saving...148</Button>149```150151---152153## Component Selection154155| Need | Component |156|---|---|157| Action / submit | `Button` with appropriate variant |158| Text input | `Input`, `Textarea`, `InputOTP` |159| Selection | `Select`, `Combobox`, `RadioGroup`, `Checkbox`, `Switch`, `Slider` |160| Toggle 2–5 options | `ToggleGroup` + `ToggleGroupItem` |161| Date | `DatePicker` (Calendar + Popover) |162| Data table | `Table` |163| Info display | `Card`, `Badge`, `Avatar` |164| Navigation | `Sidebar`, `NavigationMenu`, `Breadcrumb`, `Tabs`, `Pagination` |165| Modal | `Dialog` |166| Side panel | `Sheet` |167| Bottom sheet | `Drawer` |168| Confirmation | `AlertDialog` |169| Toast / notification | `sonner` → `toast()` |170| Inline feedback | `Alert` |171| Progress | `Progress`, `Spinner` |172| Loading placeholder | `Skeleton` |173| Command palette | `Command` inside `Dialog` |174| Charts | `Chart` (wraps Recharts) |175| Layout & structure | `Card`, `Separator`, `Resizable`, `ScrollArea`, `Accordion`, `Collapsible` |176| Empty states | `Empty` |177| Dropdown menus | `DropdownMenu`, `ContextMenu`, `Menubar` |178| Tooltips / info | `Tooltip`, `HoverCard`, `Popover` |179180---181182## Workflow183184Follow this sequence every time:185186**1. Get project context**187```bash188npx shadcn@latest info --json189```190Re-run any time config may have changed.191192**2. Check installed components**193Before running `add`, check the `components` list from project context or list `resolvedPaths.ui`.194Do not import components not yet added. Do not re-add already installed ones.195196**3. Find components**197```bash198npx shadcn@latest search @shadcn -q "sidebar"199npx shadcn@latest search @magicui -q "shimmer"200npx shadcn@latest search @tailark -q "stats"201```202203**4. Get docs and examples**204```bash205npx shadcn@latest docs button dialog select206```207Fetch the returned URLs. Always do this before creating, fixing, or composing a component.208209**5. Preview before installing**210```bash211npx shadcn@latest add button --dry-run212npx shadcn@latest add button --diff button.tsx213```214215**6. Install**216```bash217npx shadcn@latest add button card dialog218npx shadcn@latest add @magicui/shimmer-button219```220221**7. Fix imports in third-party components**222After adding from community registries, check added non-UI files for hardcoded `@/components/ui/...` paths.223Use `npx shadcn@latest info` to get the correct `ui` alias and rewrite accordingly.224225**8. Review added files**226After every `add`, read the added files and verify:227- No missing sub-components (e.g. `SelectItem` without `SelectGroup`)228- All imports correct229- Composition follows Critical Rules230- Icons match project's `iconLibrary` — swap if different231232**9. Never guess the registry**233If the user says "add a login block" without specifying `@shadcn`, `@tailark`, etc., ask which registry. Never default.234235---236237## Updating Components (Smart Merge)238239When updating a component while preserving local changes — **never fetch raw files from GitHub manually**.240241```bash242# 1. See all files that would change243npx shadcn@latest add <component> --dry-run244245# 2. Inspect diff for each file246npx shadcn@latest add <component> --diff <file>247248# 3. Decide per file:249# - No local changes → safe to overwrite250# - Has local changes → read local, apply upstream changes, preserve modifications251# - User says "update everything" → use --overwrite, but confirm first252253# ⚠️ Never use --overwrite without explicit user approval254```255256---257258## Switching Presets259260Always ask the user first: **overwrite**, **merge**, or **skip**?261262| Mode | Command | Effect |263|---|---|---|264| Overwrite | `npx shadcn@latest apply --preset <code>` | Overwrites components, fonts, CSS variables |265| Merge | `npx shadcn@latest init --preset <code> --force --no-reinstall` then diff each component | Updates config and CSS, smart-merges components |266| Skip | `npx shadcn@latest init --preset <code> --force --no-reinstall` | Updates config and CSS only, leaves components untouched |267268> Always run preset commands inside the project directory.269> `apply` requires an existing `components.json`.270> The CLI preserves the current `base` automatically.271> In temp directories, pass `--base <current-base>` explicitly.272273---274275## Quick Reference276277```bash278# New project279npx shadcn@latest init --name my-app --preset base-nova280npx shadcn@latest init --name my-app --preset a2r6bw --template vite281282# Monorepo283npx shadcn@latest init --name my-app --preset base-nova --monorepo284285# Existing project286npx shadcn@latest init --preset base-nova287npx shadcn@latest init --defaults288289# Apply preset to existing project290npx shadcn@latest apply --preset a2r6bw291292# Add components293npx shadcn@latest add button card dialog294npx shadcn@latest add --all295296# Search registries297npx shadcn@latest search @shadcn -q "sidebar"298299# Get docs300npx shadcn@latest docs button dialog301302# View without installing303npx shadcn@latest view @shadcn/button304```305306**Named presets:** `nova` · `vega` · `maia` · `lyra` · `mira` · `luma`307**Templates:** `next` · `vite` · `start` · `react-router` · `astro` · `laravel`308**Preset codes:** version-prefixed base62 strings from [ui.shadcn.com](https://ui.shadcn.com)309310---311312## Reference Files313314> Read these when the task involves the corresponding domain.315316- `rules/forms.md` — FieldGroup, Field, InputGroup, ToggleGroup, FieldSet, validation states317- `rules/composition.md` — Groups, overlays, Card, Tabs, Avatar, Alert, Empty, Toast, Separator, Skeleton, Badge, Button loading318- `rules/icons.md` — data-icon, icon sizing, passing icons as objects319- `rules/styling.md` — Semantic colors, variants, className, spacing, size, truncate, dark mode, cn(), z-index320- `rules/base-vs-radix.md` — asChild vs render, Select, ToggleGroup, Slider, Accordion321- `cli.md` — Full command reference, flags, presets, templates322- `customization.md` — Theming, CSS variables, extending components