Tailwind CSS v4 Setup
Install Tailwind CSS v4, configure with CSS-first approach, set up @theme, and use @import "tailwindcss".
Search across all documentation pages
Install Tailwind CSS v4, configure with CSS-first approach, set up @theme, and use @import "tailwindcss".
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Quick-reference recipe card - copy-paste ready.
# Install Tailwind CSS v4 with Next.js
npm install tailwindcss @tailwindcss/postcss postcss
# Or with Vite
npm install tailwindcss @tailwindcss/vite/* app/globals.css - the ONLY config you need */
@import "tailwindcss";
/* Custom theme values */
@theme {
--color-brand: #3b82f6;
--color-brand-dark: #1d4ed8;
--font-family-heading: "Inter", sans-serif;
--breakpoint-3xl: 1920px;
}// postcss.config.mjs (Next.js)
export default {
plugins: {
"@tailwindcss/postcss": {},
},
};// vite.config.ts (Vite)
import tailwindcss from "@tailwindcss/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [tailwindcss()],
});When to reach for this: At the start of every new project using Tailwind CSS v4 - the setup is simpler than v3, with no tailwind.config.js needed.
/* app/globals.css - full project setup */
@import "tailwindcss";
/* Source detection - tell Tailwind where your classes are */
@source "../components/**/*.tsx";
@source "../lib/**/*.ts";
/* Custom theme */
@theme {
/* Colors */
--color-primary: #2563eb;
--color-primary-foreground: #ffffff;
--color-secondary: #64748b;
--color-destructive: #ef4444;
--color-muted: #f1f5f9;
--color-border: #e2e8f0;
/* Spacing */
--spacing-18: 4.5rem;
--spacing-128: 32rem;
/* Typography */
--font-family-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
--font-family-mono: "JetBrains Mono", ui-monospace, monospace;
/* Border radius */
--radius-DEFAULT: 0.5rem;
--radius-lg: 0.75rem;
/* Shadows */
--shadow-soft: 0 2px 8px rgb(0 0 0 / 0.08);
/* Animations */
--animate-fade-in: fade-in 0.3s ease-out;
}
@keyframes fade-in {
from { opacity: 0; transform: translateY(-4px); }
to { opacity: 1; transform: translateY(0); }
}
/* Custom utilities */
@utility container-narrow {
max-width: 42rem;
margin-inline: auto;
padding-inline: 1rem;
}
/* Custom variant */
@variant hocus (&:hover, &:focus-visible);
/* Layer overrides */
@layer base {
html {
font-family: var(--font-family-sans);
color: var(--color-foreground, #0f172a);
}
h1, h2, h3 {
font-family: var(--font-family-heading, var(--font-family-sans));
}
}// Usage in components
export function Hero() {
return (
<section className="container-narrow animate-fade-in py-18">
<h1 className="text-4xl font-bold text-primary">Welcome</h1>
<p className="mt-4 text-secondary">Build something great.</p>
<button className="mt-6 rounded-lg bg-primary px-6 py-3 text-primary-foreground shadow-soft hocus:bg-primary-dark">
Get Started
</button>
</section>
);
}What this demonstrates:
@import "tailwindcss" replaces the old @tailwind directives@theme block for CSS-first configuration (no JS config file)@source for explicit content detection@utility@varianttext-primary, shadow-soft, etc.)@import "tailwindcss" includes all of Tailwind's base, components, and utilities layers@theme defines CSS custom properties that Tailwind converts into utility classes--color-* becomes text-*, bg-*, border-*; --spacing-* becomes p-*, m-*, gap-*@source tells Tailwind where to scan for class names (replaces content in v3 config)@source is only needed for non-standard paths@theme block compiles to CSS custom properties, enabling runtime themingMigrating from v3 config:
# Automatic migration tool
npx @tailwindcss/upgrade/* v3 tailwind.config.js colors → v4 @theme */
/* Before (JS): colors: { brand: { 500: '#3b82f6' } } */
/* After (CSS): */
@theme {
--color-brand-500: #3b82f6;
}Disabling default theme values:
@theme {
/* Remove all default colors */
--color-*: initial;
/* Define only your colors */
--color-primary: #2563eb;
--color-gray-50: #f9fafb;
--color-gray-900: #111827;
}Multiple CSS files:
/* styles/theme.css */
@theme {
--color-brand: #3b82f6;
}
/* app/globals.css */
@import "tailwindcss";
@import "./styles/theme.css";// No TypeScript impact - Tailwind is CSS-only
// But you can type your theme tokens for consistency:
const themeColors = {
primary: "text-primary",
secondary: "text-secondary",
destructive: "text-destructive",
} as const;
type ThemeColor = keyof typeof themeColors;No tailwind.config.js by default - v4 does not read a JS config file. Fix: All config goes in CSS via @theme. If you must use JS config (for plugins), use @config "./tailwind.config.js" in your CSS.
@tailwind base/components/utilities is removed - v4 uses @import "tailwindcss" instead. Fix: Replace the three @tailwind directives with a single @import.
PostCSS plugin changed - The plugin is now @tailwindcss/postcss, not tailwindcss. Fix: Update your PostCSS config.
Content auto-detection - v4 auto-scans your project. If classes are not being detected, add explicit @source paths. Fix: Use @source "../path/**/*.tsx" for non-standard file locations.
CSS custom property names - @theme values must follow the --category-name pattern to generate correct utilities. --color-brand becomes text-brand, but --brand-color does not.
| Alternative | Use When | Don't Use When |
|---|---|---|
| Tailwind v3 | Legacy project not ready to migrate | Starting a new project (v4 is simpler) |
| CSS Modules | You need locally-scoped CSS without utility classes | You want rapid prototyping with utilities |
| Vanilla Extract | You want type-safe CSS-in-TS with zero runtime | You prefer utility-first development |
| UnoCSS | You want an ultra-fast, configurable utility engine | You want Tailwind's ecosystem and community |
@import "tailwindcss" replaces @tailwind base, @tailwind components, and @tailwind utilities.
In your CSS file inside @theme { } blocks. There is no tailwind.config.js by default.
--color-brand becomes text-brand, bg-brand, border-brand--spacing-18 becomes p-18, m-18, gap-18--font-family-sans becomes font-sansOnly when Tailwind's auto-detection misses your files. Add @source "../path/**/*.tsx" for non-standard file locations.
Run npx @tailwindcss/upgrade. It converts your tailwind.config.js values into @theme CSS blocks.
@theme {
--color-*: initial;
--color-primary: #2563eb;
}@tailwindcss/postcss, not tailwindcss. Using the old name will fail silently.
Theme values must follow the --category-name pattern. --color-brand works, but --brand-color does not match any utility category.
They can. Use @config "./tailwind.config.js" in CSS only when you need JS-based plugins. Move all theme values to @theme to avoid conflicts.
const themeColors = {
primary: "text-primary",
secondary: "text-secondary",
} as const;
type ThemeColor = keyof typeof themeColors;Tailwind itself is CSS-only, but you can create a typed lookup map for consistency.
@utility container-narrow {
max-width: 42rem;
margin-inline: auto;
}
@variant hocus (&:hover, &:focus-visible);Yes. Import a separate theme file after @import "tailwindcss":
@import "tailwindcss";
@import "./styles/theme.css";Reviewed by Chris St. John·Last updated Jul 16, 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥