shadcn/ui Setup
Sets up shadcn/ui with proper Tailwind CSS v4 configuration. This skill ensures you get the modern CSS-first setup, not the deprecated v3 approach.
Contract
Inputs:
- Project root directory (defaults to current working directory).
- Optional: theme name (zinc, slate, stone, etc.), component list, router type (app-router or pages-router).
Outputs:
globals.csswith@import "tailwindcss"and full@themetoken block.components.jsonwith CSS-first shadcn config.src/lib/utils.tswithcn()helper.- Installed packages:
class-variance-authority,clsx,tailwind-merge,lucide-react,tailwindcss,@tailwindcss/postcss.
Creates/Modifies:
src/app/globals.css(created or overwritten).components.json(created or overwritten).src/lib/utils.ts(created).package.json(dependencies updated viabun add).postcss.config.mjs(created if absent).
External Side Effects:
- Runs
bun addto install packages. - Runs
bunx shadcn@latest addfor any specified components.
Confirmation Required:
- None. Running this skill applies all changes immediately. Review the component list before invoking in an existing project.
Delegates To:
bunx shadcn@latest addfor individual component installation.
Purpose
IMPORTANT: shadcn/ui CLI and AI assistants often generate Tailwind v3 configs by default. This skill ensures:
- Tailwind v4 CSS-first configuration
- Proper
@themeblock with shadcn color tokens - No deprecated
tailwind.config.jsfiles - Correct dependency versions
When to Use
- Setting up a new Next.js project with shadcn/ui
- Adding shadcn/ui to an existing project
- Migrating from shadcn + Tailwind v3 to v4
- Resetting a broken shadcn configuration
Quick Start
# Install core dependencies
bun add class-variance-authority clsx tailwind-merge lucide-react
bun add -D tailwindcss @tailwindcss/postcss
# Initialize shadcn (CSS-first, no tailwind.config)
bunx shadcn@latest init
# Install specific components
bunx shadcn@latest add button card input dialog
# Or install an essential starter set
bunx shadcn@latest add button card input label dialog dropdown-menu toast
What Gets Installed
Dependencies
{
"dependencies": {
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"tailwind-merge": "^2.2.0",
"lucide-react": "^0.400.0"
},
"devDependencies": {
"tailwindcss": "^4.0.0",
"@tailwindcss/postcss": "^4.0.0"
}
}
File Structure
project/
├── src/
│ ├── app/
│ │ └── globals.css # Tailwind v4 + shadcn theme
│ ├── components/
│ │ └── ui/ # shadcn components
│ │ ├── button.tsx
│ │ ├── card.tsx
│ │ └── ...
│ └── lib/
│ └── utils.ts # cn() utility
├── components.json # shadcn config
└── postcss.config.mjs # PostCSS with @tailwindcss/postcss
Tailwind v4 + shadcn CSS Configuration
The skill generates a CSS-first configuration using @import "tailwindcss" and an @theme block with all shadcn color tokens and border-radius variables.
See ${CLAUDE_SKILL_DIR}/references/shadcn-theme-tokens.md for the full token block (load when constructing the globals.css file).
Available Themes
| Theme | Description |
|-------|-------------|
| default | shadcn default (neutral grays) |
| zinc | Zinc-based neutral |
| slate | Slate-based cool neutral |
| stone | Stone-based warm neutral |
| gray | Pure gray |
| neutral | True neutral |
| red | Red primary |
| rose | Rose primary |
| orange | Orange primary |
| green | Green primary |
| blue | Blue primary |
| yellow | Yellow primary |
| violet | Violet primary |
Common Components
Install commonly used components:
# Essential set
bunx shadcn@latest add button card input label dialog dropdown-menu toast
# Form-focused
bunx shadcn@latest add form input label select checkbox radio-group switch textarea
# Dashboard
bunx shadcn@latest add card table tabs badge avatar dropdown-menu sheet sidebar
components.json Configuration
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/app/globals.css",
"baseColor": "zinc",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}
Note: The tailwind.config is empty because we use CSS-first configuration in v4.
Utils File
// src/lib/utils.ts
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
Usage After Setup
Adding Components
# Using bunx (recommended with bun)
bunx shadcn@latest add button
# Multiple components
bunx shadcn@latest add card dialog dropdown-menu
Using Components
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
export function MyComponent() {
return (
<Card>
<CardHeader>
<CardTitle>Welcome</CardTitle>
</CardHeader>
<CardContent>
<Button>Click me</Button>
</CardContent>
</Card>
);
}
Dark Mode Support
The CSS uses prefers-color-scheme by default. For manual toggle:
// Add to layout.tsx or a theme provider
'use client';
import { useEffect, useState } from 'react';
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<'light' | 'dark'>('light');
useEffect(() => {
document.documentElement.classList.toggle('dark', theme === 'dark');
}, [theme]);
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
Update CSS to use class-based dark mode:
/* Replace @media (prefers-color-scheme: dark) with: */
.dark {
--color-background: hsl(222.2 84% 4.9%);
/* ... rest of dark theme variables */
}
Troubleshooting
"tailwind.config.js created by shadcn CLI"
Delete it. The CLI sometimes generates v3 configs. Run:
rm tailwind.config.js tailwind.config.ts
Components not styled correctly
- Check that
globals.cssis imported in your layout - Verify
@import "tailwindcss"is at the top - Ensure
@themeblock contains all required variables
Type errors with components
Run:
bun add -D @types/react @types/react-dom
cn() utility not found
Create src/lib/utils.ts:
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
Validation
After setup, verify no v3 patterns were introduced:
# Should return nothing (no v3 tailwind.config files)
find . -name "tailwind.config.*" -not -path "*/node_modules/*"
# Should return nothing (no @apply directives)
grep -r "@apply" src/ --include="*.css" 2>/dev/null
# Verify the @import directive is present
grep "@import \"tailwindcss\"" src/app/globals.css