shadcn/ui component library patterns. Covers CLI usage, installation, component categories, theming with CSS variables, dark mode setup, and integration with React Hook Form + Zod. Use when adding UI components, setting up design systems, or implementing forms with shadcn/ui.
shadcn/ui is a collection of beautifully-designed, accessible components and a code distribution platform. It is built with TypeScript, Tailwind CSS, and Radix UI primitives. It supports multiple frameworks including Next.js, Vite, Remix, Astro, and more. Open Source. Open Code. AI-Ready. It also comes with a command-line tool to install and manage components and a registry system to publish and distribute code.
# Initialize shadcn/ui in your project
npx shadcn@latest init
# Add a component
npx shadcn@latest add button
# Add multiple components
npx shadcn@latest add button card dialog
# Add all components
npx shadcn@latest add --all
# Update components
npx shadcn@latest diff
# List available components
npx shadcn@latest add
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/index.css",
"baseColor": "neutral",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
}
}
| Topic | URL | Description | |-------|-----|-------------| | Introduction | https://ui.shadcn.com/docs | Core principles and getting started | | CLI | https://ui.shadcn.com/docs/cli | Command-line tool reference | | components.json | https://ui.shadcn.com/docs/components-json | Configuration file documentation | | Theming | https://ui.shadcn.com/docs/theming | Colors, typography, design tokens | | Changelog | https://ui.shadcn.com/docs/changelog | Release notes and version history | | About | https://ui.shadcn.com/docs/about | Credits and project information |
| Framework | URL | Notes | |-----------|-----|-------| | Vite | https://ui.shadcn.com/docs/installation/vite | Recommended for this plugin | | TanStack Router | https://ui.shadcn.com/docs/installation/tanstack-router | Works with TanStack ecosystem | | TanStack Start | https://ui.shadcn.com/docs/installation/tanstack | Full-stack TanStack framework | | Next.js | https://ui.shadcn.com/docs/installation/next | App Router and Pages Router | | Remix | https://ui.shadcn.com/docs/installation/remix | Remix framework | | Astro | https://ui.shadcn.com/docs/installation/astro | Astro framework | | React Router | https://ui.shadcn.com/docs/installation/react-router | React Router v7+ | | Laravel | https://ui.shadcn.com/docs/installation/laravel | Laravel Inertia | | Gatsby | https://ui.shadcn.com/docs/installation/gatsby | Gatsby framework | | Manual | https://ui.shadcn.com/docs/installation/manual | Without CLI |
The fastest way to get started with shadcn/ui and TanStack Router:
# Create new project with shadcn/ui pre-configured
npx create-tsrouter-app@latest my-app --template file-router --tailwind --add-ons shadcn
Then add components:
npx shadcn@latest add button
Usage in routes:
// src/routes/index.tsx
import { createFileRoute } from "@tanstack/react-router"
import { Button } from "@/components/ui/button"
export const Route = createFileRoute("/")({
component: App,
})
function App() {
return (
<div>
<Button>Click me</Button>
</div>
)
}
Step 1: Create project
npm create vite@latest
# Select: React + TypeScript
Step 2: Add Tailwind CSS
npm install tailwindcss @tailwindcss/vite
Replace src/index.css:
@import "tailwindcss";
Step 3: Configure TypeScript paths
Edit tsconfig.json:
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
Edit tsconfig.app.json:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
Step 4: Configure Vite
npm install -D @types/node
Edit vite.config.ts:
import path from "path"
import tailwindcss from "@tailwindcss/vite"
import react from "@vitejs/plugin-react"
import { defineConfig } from "vite"
export default defineConfig({
plugins: [react(), tailwindcss()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
})
Step 5: Initialize shadcn/ui
npx shadcn@latest init
# Select: Neutral as base color
Step 6: Add components
npx shadcn@latest add button
Usage:
// src/App.tsx
import { Button } from "@/components/ui/button"
function App() {
return (
<div className="flex min-h-svh flex-col items-center justify-center">
<Button>Click me</Button>
</div>
)
}
export default App
Step 1: Add Tailwind CSS
Follow the Tailwind CSS installation guide.
Step 2: Add dependencies
npm install class-variance-authority clsx tailwind-merge lucide-react tw-animate-css
Step 3: Configure path aliases
In tsconfig.json:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./*"]
}
}
}
Step 4: Configure styles
Create src/styles/globals.css:
@import "tailwindcss";
@import "tw-animate-css";
@custom-variant dark (&:is(.dark *));
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--radius: 0.625rem;
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.145 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.145 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.985 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.396 0.141 25.723);
--destructive-foreground: oklch(0.637 0.237 25.331);
--border: oklch(0.269 0 0);
--input: oklch(0.269 0 0);
--ring: oklch(0.439 0 0);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(0.269 0 0);
--sidebar-ring: oklch(0.439 0 0);
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
}
Step 5: Add cn() helper
Create lib/utils.ts:
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
Step 6: Create components.json
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/styles/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}
Step 7: Add components
npx shadcn@latest add button
| Component | URL | Description | |-----------|-----|-------------| | Form | https://ui.shadcn.com/docs/components/form | React Hook Form + Zod validation | | Field | https://ui.shadcn.com/docs/components/field | Form field with label and error | | Button | https://ui.shadcn.com/docs/components/button | Multiple variants (default, destructive, outline, secondary, ghost, link) | | Button Group | https://ui.shadcn.com/docs/components/button-group | Group multiple buttons | | Input | https://ui.shadcn.com/docs/components/input | Text input | | Input Group | https://ui.shadcn.com/docs/components/input-group | Input with prefix/suffix | | Input OTP | https://ui.shadcn.com/docs/components/input-otp | One-time password input | | Textarea | https://ui.shadcn.com/docs/components/textarea | Multi-line text input | | Checkbox | https://ui.shadcn.com/docs/components/checkbox | Checkbox input | | Radio Group | https://ui.shadcn.com/docs/components/radio-group | Radio button group | | Select | https://ui.shadcn.com/docs/components/select | Select dropdown | | Switch | https://ui.shadcn.com/docs/components/switch | Toggle switch | | Slider | https://ui.shadcn.com/docs/components/slider | Slider input | | Calendar | https://ui.shadcn.com/docs/components/calendar | Date selection | | Date Picker | https://ui.shadcn.com/docs/components/date-picker | Input + calendar | | Combobox | https://ui.shadcn.com/docs/components/combobox | Searchable select with autocomplete | | Label | https://ui.shadcn.com/docs/components/label | Form label |
| Component | URL | Description | |-----------|-----|-------------| | Accordion | https://ui.shadcn.com/docs/components/accordion | Collapsible sections | | Breadcrumb | https://ui.shadcn.com/docs/components/breadcrumb | Navigation breadcrumbs | | Navigation Menu | https://ui.shadcn.com/docs/components/navigation-menu | Accessible nav with dropdowns | | Sidebar | https://ui.shadcn.com/docs/components/sidebar | Collapsible app sidebar | | Tabs | https://ui.shadcn.com/docs/components/tabs | Tabbed interface | | Separator | https://ui.shadcn.com/docs/components/separator | Visual divider | | Scroll Area | https://ui.shadcn.com/docs/components/scroll-area | Custom scrollable area | | Resizable | https://ui.shadcn.com/docs/components/resizable | Resizable panel layout |
| Component | URL | Description | |-----------|-----|-------------| | Dialog | https://ui.shadcn.com/docs/components/dialog | Modal dialog | | Alert Dialog | https://ui.shadcn.com/docs/components/alert-dialog | Confirmation prompts | | Sheet | https://ui.shadcn.com/docs/components/sheet | Slide-out panel (drawer) | | Drawer | https://ui.shadcn.com/docs/components/drawer | Mobile-friendly drawer (Vaul) | | Popover | https://ui.shadcn.com/docs/components/popover | Floating popover | | Tooltip | https://ui.shadcn.com/docs/components/tooltip | Additional context on hover | | Hover Card | https://ui.shadcn.com/docs/components/hover-card | Card on hover | | Context Menu | https://ui.shadcn.com/docs/components/context-menu | Right-click menu | | Dropdown Menu | https://ui.shadcn.com/docs/components/dropdown-menu | Dropdown menu | | Menubar | https://ui.shadcn.com/docs/components/menubar | Horizontal menubar | | Command | https://ui.shadcn.com/docs/components/command | Command palette (cmdk) |
| Component | URL | Description | |-----------|-----|-------------| | Alert | https://ui.shadcn.com/docs/components/alert | Messages and notifications | | Toast | https://ui.shadcn.com/docs/components/toast | Toast notifications (Sonner) | | Progress | https://ui.shadcn.com/docs/components/progress | Progress bar | | Spinner | https://ui.shadcn.com/docs/components/spinner | Loading spinner | | Skeleton | https://ui.shadcn.com/docs/components/skeleton | Loading placeholder | | Badge | https://ui.shadcn.com/docs/components/badge | Labels and status indicators | | Empty | https://ui.shadcn.com/docs/components/empty | Empty state |
| Component | URL | Description | |-----------|-----|-------------| | Avatar | https://ui.shadcn.com/docs/components/avatar | User profile images | | Card | https://ui.shadcn.com/docs/components/card | Card container | | Table | https://ui.shadcn.com/docs/components/table | Data display table | | Data Table | https://ui.shadcn.com/docs/components/data-table | Advanced table with TanStack Table | | Chart | https://ui.shadcn.com/docs/components/chart | Charts (Recharts) | | Carousel | https://ui.shadcn.com/docs/components/carousel | Carousel (Embla) | | Aspect Ratio | https://ui.shadcn.com/docs/components/aspect-ratio | Maintains aspect ratio | | Typography | https://ui.shadcn.com/docs/components/typography | Typography styles | | Item | https://ui.shadcn.com/docs/components/item | Generic list item | | Kbd | https://ui.shadcn.com/docs/components/kbd | Keyboard shortcut display |
| Component | URL | Description | |-----------|-----|-------------| | Collapsible | https://ui.shadcn.com/docs/components/collapsible | Collapsible container | | Toggle | https://ui.shadcn.com/docs/components/toggle | Toggle button | | Toggle Group | https://ui.shadcn.com/docs/components/toggle-group | Group of toggles | | Pagination | https://ui.shadcn.com/docs/components/pagination | Pagination controls |
| Framework | URL | |-----------|-----| | Overview | https://ui.shadcn.com/docs/dark-mode | | Vite | https://ui.shadcn.com/docs/dark-mode/vite | | Next.js | https://ui.shadcn.com/docs/dark-mode/next | | Astro | https://ui.shadcn.com/docs/dark-mode/astro | | Remix | https://ui.shadcn.com/docs/dark-mode/remix |
| Topic | URL | Description | |-------|-----|-------------| | Overview | https://ui.shadcn.com/docs/forms | Forms guide | | React Hook Form | https://ui.shadcn.com/docs/forms/react-hook-form | Recommended for this plugin | | TanStack Form | https://ui.shadcn.com/docs/forms/tanstack-form | Alternative form library | | Next.js Server Actions | https://ui.shadcn.com/docs/forms/next | Server-side forms |
| Topic | URL | Description | |-------|-----|-------------| | Monorepo | https://ui.shadcn.com/docs/monorepo | Monorepo setup | | React 19 | https://ui.shadcn.com/docs/react-19 | React 19 support | | Tailwind CSS v4 | https://ui.shadcn.com/docs/tailwind-v4 | Tailwind v4 setup | | JavaScript | https://ui.shadcn.com/docs/javascript | Non-TypeScript usage | | Figma | https://ui.shadcn.com/docs/figma | Design resources | | v0 | https://ui.shadcn.com/docs/v0 | AI UI generation |
shadcn/ui provides an MCP (Model Context Protocol) server for AI integrations:
URL: https://ui.shadcn.com/docs/mcp
Features:
Setup for Claude Code:
{
"mcpServers": {
"shadcn": {
"command": "npx",
"args": ["-y", "shadcn@canary", "mcp"]
}
}
}
| Topic | URL | Description | |-------|-----|-------------| | Overview | https://ui.shadcn.com/docs/registry | Creating registries | | Getting Started | https://ui.shadcn.com/docs/registry/getting-started | Setup guide | | Examples | https://ui.shadcn.com/docs/registry/examples | Example registries | | FAQ | https://ui.shadcn.com/docs/registry/faq | Common questions | | Authentication | https://ui.shadcn.com/docs/registry/authentication | Auth for registries | | Registry MCP | https://ui.shadcn.com/docs/registry/mcp | MCP integration |
| Schema | URL | |--------|-----| | Registry Index | https://ui.shadcn.com/schema/registry.json | | Registry Item | https://ui.shadcn.com/schema/registry-item.json |
# Authentication flow
npx shadcn@latest add button input form label card
# Dashboard layout
npx shadcn@latest add sidebar navigation-menu breadcrumb avatar dropdown-menu
# Data display
npx shadcn@latest add table data-table pagination skeleton
# Forms
npx shadcn@latest add form field input select checkbox radio-group switch calendar date-picker combobox
# Feedback
npx shadcn@latest add alert toast dialog alert-dialog
src/
├── components/
│ └── ui/
│ ├── button.tsx
│ ├── card.tsx
│ ├── dialog.tsx
│ └── ...
├── lib/
│ └── utils.ts # cn() utility function
└── index.css # Tailwind + CSS variables
cn() Utility// lib/utils.ts
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
// Usage
<Button className={cn("w-full", isLoading && "opacity-50")} />
<Button variant="default">Default</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="outline">Outline</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
<Button size="default">Default</Button>
<Button size="sm">Small</Button>
<Button size="lg">Large</Button>
<Button size="icon"><IconName /></Button>
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import * as z from "zod"
import { Button } from "@/components/ui/button"
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"
import { Input } from "@/components/ui/input"
const formSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
})
export function LoginForm() {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: { email: "", password: "" },
})
function onSubmit(values: z.infer<typeof formSchema>) {
console.log(values)
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input placeholder="email@example.com" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input type="password" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
)
}
import { DataTable } from "@/components/ui/data-table"
import { ColumnDef } from "@tanstack/react-table"
type User = {
id: string
name: string
email: string
}
const columns: ColumnDef<User>[] = [
{ accessorKey: "name", header: "Name" },
{ accessorKey: "email", header: "Email" },
]
export function UsersTable({ data }: { data: User[] }) {
return <DataTable columns={columns} data={data} />
}
/* index.css */
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--ring: 0 0% 3.9%;
--radius: 0.5rem;
}
.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
/* ... dark mode values */
}
}
// In components
<div className="bg-background text-foreground" />
<div cla
<!-- Content truncated for initial SEO render. Open the source file tab for the full file. -->
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer