Tailwind CSS v4: What Changed and How to Migrate
Tailwind CSS v4 is not an incremental update. It's a ground-up rewrite with a new CSS engine (Lightning CSS, written in Rust), a new configuration model (CSS-first instead of JS config file), and significantly faster builds. It also drops several v3 APIs that accumulated technical debt.
If you're starting a new project, v4 is the right choice. If you're on v3, this guide explains what changed and how to migrate.
What's New in v4
1. Lightning CSS Engine
v4 uses Lightning CSS as its parser and transformer instead of PostCSS. Lightning CSS is written in Rust and is substantially faster. Build times drop from seconds to milliseconds for most projects, with the performance gap widening on large codebases.
Benchmark: a large Next.js project that took 3.2s with v3 PostCSS typically takes 90ms with v4.
2. No Configuration File Required
v3 required a tailwind.config.js (or .ts) file for any customization. v4 moves configuration into CSS itself using @theme:
/* app.css */
@import "tailwindcss";
@theme {
--color-primary: #3b82f6;
--color-primary-dark: #1d4ed8;
--font-display: "Inter", sans-serif;
--spacing-18: 4.5rem;
}
You can still use a JavaScript config file for advanced tooling integrations, but it's no longer required for typical customization.
3. CSS-First Theme Variables
In v4, your theme values are CSS custom properties. This means they're available everywhere — not just in Tailwind classes:
.my-component {
background: var(--color-primary);
padding: var(--spacing-4);
}
And in JavaScript:
const primaryColor = getComputedStyle(document.documentElement)
.getPropertyValue('--color-primary').trim();
This makes the theme genuinely shared between Tailwind classes and custom CSS, eliminating the previous pattern of importing values from the config file.
4. New Vite Plugin
The recommended setup for v4 with Vite uses a first-party plugin instead of PostCSS:
npm install tailwindcss @tailwindcss/vite
// vite.config.js
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [
tailwindcss(),
],
})
PostCSS is still supported but the Vite plugin is faster and simpler for Vite-based projects.
5. Simplified Directives
v3 had three directives: @tailwind base, @tailwind components, @tailwind utilities. v4 replaces all three with a single import:
/* v3 */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* v4 */
@import "tailwindcss";
6. Container Queries Built In
v4 includes native container query support without a plugin:
<div class="@container">
<div class="@sm:flex @lg:grid-cols-3">
<!-- Responds to container width, not viewport -->
</div>
</div>
In v3, this required the @tailwindcss/container-queries plugin.
7. New Variant Syntax
Some variant syntax changed in v4:
<!-- v3 -->
<div class="group-hover:opacity-50">
<!-- v4 (still works, plus new arbitrary variants) -->
<div class="group-hover:opacity-50 group-[.active]:opacity-100">
v4 also adds not-* variants:
<div class="not-hover:opacity-75 not-first:mt-4">
Installation for New Projects
With Vite (React, Vue, etc.):
npm install tailwindcss @tailwindcss/vite
// vite.config.js
import tailwindcss from '@tailwindcss/vite'
export default {
plugins: [tailwindcss()]
}
/* src/index.css */
@import "tailwindcss";
With Next.js:
npm install tailwindcss @tailwindcss/postcss postcss
// postcss.config.mjs
export default {
plugins: {
'@tailwindcss/postcss': {}
}
}
CLI (no bundler):
npm install tailwindcss @tailwindcss/cli
npx @tailwindcss/cli -i src/input.css -o dist/output.css --watch
Migrating from v3
The Tailwind team provides an automated migration codemod:
npx @tailwindcss/upgrade@next
This handles the most common changes automatically. What it does:
- Converts
tailwind.config.jsto@themeblocks in CSS - Updates deprecated class names
- Converts
@tailwinddirectives to@import "tailwindcss" - Updates PostCSS config if applicable
Manual Changes Required
Some things the codemod doesn't handle:
bg-opacity-*, text-opacity-*, border-opacity-* utilities removed:
<!-- v3 -->
<div class="bg-blue-500 bg-opacity-50">
<!-- v4 -->
<div class="bg-blue-500/50">
flex-shrink and flex-grow renamed:
<!-- v3 -->
<div class="flex-shrink-0 flex-grow">
<!-- v4 -->
<div class="shrink-0 grow">
overflow-ellipsis renamed:
<!-- v3: -->
<div class="overflow-ellipsis">
<!-- v4: -->
<div class="text-ellipsis">
JIT is now the only mode: v3's non-JIT mode (full CSS generation) is gone. v4 always generates only the classes you use.
theme() function syntax changed: In CSS, use CSS variables instead of theme():
/* v3 */
.foo { color: theme('colors.blue.500'); }
/* v4 */
.foo { color: var(--color-blue-500); }
Config File Migration
If you have a complex tailwind.config.js, the codemod converts it, but review the output. Complex configurations with plugins may need manual adjustment.
Custom colors:
// tailwind.config.js (v3)
module.exports = {
theme: {
extend: {
colors: {
brand: '#ff5500',
}
}
}
}
Becomes:
/* app.css (v4) */
@import "tailwindcss";
@theme {
--color-brand: #ff5500;
}
Custom breakpoints:
@theme {
--breakpoint-xs: 475px;
--breakpoint-3xl: 1920px;
}
Custom fonts:
@theme {
--font-display: "Inter Variable", sans-serif;
}
v3 Features Removed in v4
| Removed | Replacement |
|---|---|
bg-opacity-* utilities |
Opacity modifier syntax (bg-blue-500/50) |
text-opacity-* utilities |
Opacity modifier syntax (text-blue-500/75) |
flex-shrink |
shrink |
flex-grow |
grow |
decoration-slice |
box-decoration-slice |
decoration-clone |
box-decoration-clone |
overflow-ellipsis |
text-ellipsis |
transform (standalone class) |
Implicit — transform utilities auto-apply |
filter (standalone class) |
Implicit — filter utilities auto-apply |
ring-offset-* |
Changed implementation |
Run the codemod and check its output for these replacements.
Dark Mode in v4
Dark mode configuration moved to CSS:
/* v4 */
@import "tailwindcss";
@variant dark (&:where([data-theme=dark] *));
/* or: */
@variant dark (@media (prefers-color-scheme: dark));
The dark: variant works the same in HTML:
<div class="bg-white dark:bg-gray-900">
Should You Upgrade Now?
Upgrade if: Starting a new project, have good test coverage, or the codebase is small.
Wait if: Large production app with many custom plugins, complex configuration, or limited time for testing. v3 continues to receive security patches.
The migration path is well-defined for most projects, but edge cases with third-party plugins (forms, typography, aspect-ratio, etc.) may require plugin upgrades that aren't available yet. Check that the plugins you depend on have v4-compatible versions.
The official upgrade guide documents all breaking changes in detail.