How shadcn/ui Works
shadcn/ui is NOT a component library - it's a collection of re-usable components you copy into your project and own. This philosophy changes everything about how you work with it.
1. You Own the Code
Components are copied directly into your components/ui/ directory. You can and should modify them. Don't wrap components - edit them directly. This is intentional.
# Install a component
npx shadcn@latest add button
# Components land in your project
# src/components/ui/button.tsx
2. Composition Over Monoliths
Build complex UIs by composing primitives. Every component follows consistent patterns:
- Radix UI primitives for accessibility
- CVA (class-variance-authority) for variants
- cn() utility for conditional classes
- data-slot attributes for styling hooks
// Composition pattern - build from primitives
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Open</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Title</DialogTitle>
<DialogDescription>Description</DialogDescription>
</DialogHeader>
{/* Content */}
<DialogFooter>
<Button>Save</Button>
</DialogFooter>
</DialogContent>
</Dialog>
3. The asChild Pattern
Use asChild prop to merge behavior onto a different element. Critical for navigation, forms, and custom triggers.
// asChild merges Button behavior onto Link
<Button asChild>
<Link href="/dashboard">Dashboard</Link>
</Button>
// asChild on DialogTrigger
<DialogTrigger asChild>
<Button>Open Dialog</Button>
</DialogTrigger>
4. Variant-Driven Design
Use CVA for consistent variant APIs. Every button, badge, and alert follows this pattern:
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md...", // Base
{
variants: {
variant: {
default: "bg-primary text-primary-foreground",
destructive: "bg-destructive text-white",
outline: "border bg-background",
ghost: "hover:bg-accent",
},
size: {
default: "h-9 px-4",
sm: "h-8 px-3",
lg: "h-10 px-6",
icon: "size-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
5. CSS Variables for Theming
All colors use CSS variables. Theme by changing variables, not component code:
/* Light mode */
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
}
/* Dark mode */
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
}
6. React 19 Patterns
shadcn/ui uses modern React patterns - no forwardRef (React 19), data-slot attributes, and function components:
// Modern pattern (no forwardRef in React 19)
function Button({ className, variant, size, asChild = false, ...props }) {
const Comp = asChild ? Slot : "button"
return (
<Comp
data-slot="button"
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
)
}
- Add a new shadcn component to the project
- Build a form with validation
- Build a data table with sorting/filtering
- Customize an existing component
- Create a compound component
- Add animations/transitions
- Set up or modify theming
- Debug a component issue
- Something else
Then read the matching workflow from workflows/ and follow it.
After reading the workflow, follow it exactly.
- TypeScript compiles?
bunx tsc --noEmit
Component renders?
Check the dev server - no console errors
Accessibility check:
- Keyboard navigation works
- Focus states visible
- ARIA attributes present
- Visual check:
- Matches design intent
- Works in light/dark mode
- Responsive on mobile
Report: "TypeScript: ✓ | Renders: ✓ | A11y: ✓ | Visual: ✓"
All in references/:
Core:
- core-components.md - Button, Input, Label, Card, Badge, etc.
- composition-patterns.md - asChild, Slot, compound components
Forms:
- form-components.md - Form, Field, Input, Select, Combobox, etc.
- form-validation.md - Zod, React Hook Form, TanStack Form
Data Display:
- data-table.md - TanStack Table integration
- data-components.md - Kanban, Gantt, List, Calendar
Navigation:
- navigation-components.md - Sidebar, Tabs, Breadcrumb, Command
Overlays:
- overlay-components.md - Dialog, Sheet, Drawer, Popover, Tooltip
Feedback:
- feedback-components.md - Toast/Sonner, Alert, Progress, Skeleton
Hooks:
- hooks.md - useLocalStorage, useMediaQuery, useDebounce, etc.
Animation:
- animation-patterns.md - Framer Motion, micro-interactions
Theming:
- theming.md - CSS variables, dark mode, color system
Advanced:
- cli-registry.md - CLI commands, custom registries
- accessibility.md - WCAG compliance, keyboard navigation
All in workflows/:
| File |
Purpose |
| add-component.md |
Install and configure a shadcn component |
| build-form.md |
Build forms with validation |
| build-data-table.md |
Build tables with TanStack Table |
| customize-component.md |
Modify existing components |
| build-compound-component.md |
Create new compound components |
| add-animations.md |
Add Framer Motion animations |
| setup-theming.md |
Configure theming and dark mode |
| debug-component.md |
Troubleshoot component issues |
|
|
1---2name: shadcn-ui3description: Build production-ready React/Next.js UIs with shadcn/ui components. Full lifecycle - install, customize, compose, debug, optimize. Covers components, forms, tables, theming, animations, and hooks.4---56<essential_principles>78## How shadcn/ui Works910shadcn/ui is NOT a component library - it's a collection of re-usable components you copy into your project and own. This philosophy changes everything about how you work with it.1112### 1. You Own the Code1314Components are copied directly into your `components/ui/` directory. You can and should modify them. Don't wrap components - edit them directly. This is intentional.1516```bash17# Install a component18npx shadcn@latest add button1920# Components land in your project21# src/components/ui/button.tsx22```2324### 2. Composition Over Monoliths2526Build complex UIs by composing primitives. Every component follows consistent patterns:27- **Radix UI primitives** for accessibility28- **CVA (class-variance-authority)** for variants29- **cn() utility** for conditional classes30- **data-slot attributes** for styling hooks3132```tsx33// Composition pattern - build from primitives34<Dialog>35 <DialogTrigger asChild>36 <Button variant="outline">Open</Button>37 </DialogTrigger>38 <DialogContent>39 <DialogHeader>40 <DialogTitle>Title</DialogTitle>41 <DialogDescription>Description</DialogDescription>42 </DialogHeader>43 {/* Content */}44 <DialogFooter>45 <Button>Save</Button>46 </DialogFooter>47 </DialogContent>48</Dialog>49```5051### 3. The asChild Pattern5253Use `asChild` prop to merge behavior onto a different element. Critical for navigation, forms, and custom triggers.5455```tsx56// asChild merges Button behavior onto Link57<Button asChild>58 <Link href="/dashboard">Dashboard</Link>59</Button>6061// asChild on DialogTrigger62<DialogTrigger asChild>63 <Button>Open Dialog</Button>64</DialogTrigger>65```6667### 4. Variant-Driven Design6869Use CVA for consistent variant APIs. Every button, badge, and alert follows this pattern:7071```tsx72const buttonVariants = cva(73 "inline-flex items-center justify-center rounded-md...", // Base74 {75 variants: {76 variant: {77 default: "bg-primary text-primary-foreground",78 destructive: "bg-destructive text-white",79 outline: "border bg-background",80 ghost: "hover:bg-accent",81 },82 size: {83 default: "h-9 px-4",84 sm: "h-8 px-3",85 lg: "h-10 px-6",86 icon: "size-9",87 },88 },89 defaultVariants: {90 variant: "default",91 size: "default",92 },93 }94)95```9697### 5. CSS Variables for Theming9899All colors use CSS variables. Theme by changing variables, not component code:100101```css102/* Light mode */103:root {104 --background: 0 0% 100%;105 --foreground: 222.2 84% 4.9%;106 --primary: 222.2 47.4% 11.2%;107 --primary-foreground: 210 40% 98%;108}109110/* Dark mode */111.dark {112 --background: 222.2 84% 4.9%;113 --foreground: 210 40% 98%;114}115```116117### 6. React 19 Patterns118119shadcn/ui uses modern React patterns - no forwardRef (React 19), data-slot attributes, and function components:120121```tsx122// Modern pattern (no forwardRef in React 19)123function Button({ className, variant, size, asChild = false, ...props }) {124 const Comp = asChild ? Slot : "button"125 return (126 <Comp127 data-slot="button"128 className={cn(buttonVariants({ variant, size, className }))}129 {...props}130 />131 )132}133```134135</essential_principles>136137<intake>138**What would you like to do?**1391401. Add a new shadcn component to the project1412. Build a form with validation1423. Build a data table with sorting/filtering1434. Customize an existing component1445. Create a compound component1456. Add animations/transitions1467. Set up or modify theming1478. Debug a component issue1489. Something else149150**Then read the matching workflow from `workflows/` and follow it.**151</intake>152153<routing>154| Response | Workflow |155|----------|----------|156| 1, "add", "install", "component" | `workflows/add-component.md` |157| 2, "form", "validation", "input" | `workflows/build-form.md` |158| 3, "table", "data table", "sorting", "filtering" | `workflows/build-data-table.md` |159| 4, "customize", "modify", "change", "edit" | `workflows/customize-component.md` |160| 5, "compound", "create", "new component", "build component" | `workflows/build-compound-component.md` |161| 6, "animation", "motion", "transition", "animate" | `workflows/add-animations.md` |162| 7, "theme", "dark mode", "colors", "styling" | `workflows/setup-theming.md` |163| 8, "debug", "fix", "broken", "not working", "issue" | `workflows/debug-component.md` |164| 9, other | Clarify, then select workflow or references |165166**After reading the workflow, follow it exactly.**167</routing>168169<verification_loop>170## After Every Change1711721. **TypeScript compiles?**173```bash174bunx tsc --noEmit175```1761772. **Component renders?**178Check the dev server - no console errors1791803. **Accessibility check:**181- Keyboard navigation works182- Focus states visible183- ARIA attributes present1841854. **Visual check:**186- Matches design intent187- Works in light/dark mode188- Responsive on mobile189190Report: "TypeScript: ✓ | Renders: ✓ | A11y: ✓ | Visual: ✓"191</verification_loop>192193<reference_index>194## Domain Knowledge195196All in `references/`:197198**Core:**199- core-components.md - Button, Input, Label, Card, Badge, etc.200- composition-patterns.md - asChild, Slot, compound components201202**Forms:**203- form-components.md - Form, Field, Input, Select, Combobox, etc.204- form-validation.md - Zod, React Hook Form, TanStack Form205206**Data Display:**207- data-table.md - TanStack Table integration208- data-components.md - Kanban, Gantt, List, Calendar209210**Navigation:**211- navigation-components.md - Sidebar, Tabs, Breadcrumb, Command212213**Overlays:**214- overlay-components.md - Dialog, Sheet, Drawer, Popover, Tooltip215216**Feedback:**217- feedback-components.md - Toast/Sonner, Alert, Progress, Skeleton218219**Hooks:**220- hooks.md - useLocalStorage, useMediaQuery, useDebounce, etc.221222**Animation:**223- animation-patterns.md - Framer Motion, micro-interactions224225**Theming:**226- theming.md - CSS variables, dark mode, color system227228**Advanced:**229- cli-registry.md - CLI commands, custom registries230- accessibility.md - WCAG compliance, keyboard navigation231</reference_index>232233<workflows_index>234## Workflows235236All in `workflows/`:237238| File | Purpose |239|------|---------|240| add-component.md | Install and configure a shadcn component |241| build-form.md | Build forms with validation |242| build-data-table.md | Build tables with TanStack Table |243| customize-component.md | Modify existing components |244| build-compound-component.md | Create new compound components |245| add-animations.md | Add Framer Motion animations |246| setup-theming.md | Configure theming and dark mode |247| debug-component.md | Troubleshoot component issues |248</workflows_index>