Tailwind CSS v4: What's New and How to Migrate
Tailwind CSS v4 is not an incremental update. The entire engine was rewritten in Rust using Lightning CSS, the configuration file was eliminated in favor of CSS-first setup, and the PostCSS dependency became optional. For developers who've been using Tailwind since v2 or v3, the migration involves meaningful changes — but the upgrade tool handles most of it automatically, and the result is a significantly faster and simpler tool.
TL;DR
Tailwind CSS v4 is a complete rewrite with a new engine, CSS-first configuration, and zero config files. The Rust-based engine (Lightning CSS) makes full rebuilds 5x faster and incremental builds 100x faster. The big DX change: tailwind.config.js is gone — configuration now happens in your CSS file. Most utility class names stayed the same; the breaking changes are in configuration syntax and a handful of renamed utilities. For existing projects, the official upgrade tool handles ~80% of the migration automatically.
Quick Comparison
| Tailwind CSS v4 | Tailwind CSS v3 | |
|---|---|---|
| Weekly Downloads | Growing | ~15M |
| Engine | Rust (Lightning CSS) | Node.js (PostCSS) |
| Full Rebuild | ~150ms | ~800ms (5.3x faster) |
| Incremental Build | ~2ms | ~180ms (90x faster) |
| Config File | CSS @theme | tailwind.config.js |
| PostCSS Required | Optional | Required |
| Vite Plugin | ✅ @tailwindcss/vite | Via PostCSS |
| License | MIT | MIT |
Key Takeaways
- New engine: Rust-based (via Lightning CSS) — 5x faster full builds, 100x faster incremental
- No more tailwind.config.js — all configuration is now in CSS with
@theme - CSS-first config: define custom colors, fonts, spacing in your CSS file, not a JS config
- Zero PostCSS config needed — v4 is a standalone binary with the Vite plugin
- Breaking changes: some utility names changed; config format changed entirely
What's New: The Engine Rewrite
The biggest invisible change in v4 is the engine. Tailwind v3 ran through PostCSS, which processes CSS with a JavaScript plugin chain. Every time you save a file, PostCSS walks the plugin pipeline, parses your CSS, and outputs the result. This works, but it's not fast — especially for large projects with thousands of utility classes.
Tailwind v4 replaces this with Lightning CSS, a CSS parser and transformer written in Rust. Lightning CSS can parse and transform CSS roughly 100x faster than equivalent JavaScript tools. The difference is most noticeable in three scenarios: cold CI builds (where there's no warm cache), large projects with many utility classes, and development hot reload cycles where even 180ms feels like lag.
The Vite plugin (@tailwindcss/vite) is now the recommended installation for Vite-based projects. It integrates Tailwind's processing directly into Vite's build pipeline without going through PostCSS, eliminating one layer of indirection.
# Build speed comparison (same project, 450 utility classes):
Tailwind CSS v3 (PostCSS):
Full build: ~800ms
Incremental: ~180ms
Dev (first load): ~600ms
Tailwind CSS v4 (Lightning CSS):
Full build: ~150ms (5.3x faster)
Incremental: ~2ms (90x faster)
Dev (first load): ~80ms
# Real impact:
# → Cold CI builds: seconds saved per build
# → Dev hot reload: imperceptible (was already fast, now instant)
# → Large projects with 1000+ utility classes: most noticeable improvement
# Installation (v4 is a standalone package, no PostCSS required):
npm install tailwindcss@next @tailwindcss/vite@next
# vite.config.ts:
import tailwindcss from '@tailwindcss/vite';
export default {
plugins: [
tailwindcss(),
],
}
# That's it. No tailwind.config.js. No postcss.config.js.
# Add to CSS:
# @import "tailwindcss";
The 100ms incremental build speed deserves emphasis. In Tailwind v3, every save triggered a rebuild that typically completed in 100-200ms. That's fast enough to feel instant for small changes but noticeable for rapid iteration. At 2ms, Tailwind v4's incremental builds are genuinely imperceptible — you see the style change before you consciously register that a rebuild happened.
CSS-First Configuration: The Big Change
The most significant developer-facing change in v4 is the elimination of tailwind.config.js. Everything that used to live in the JavaScript config — custom colors, fonts, spacing scale, breakpoints, animations — now lives in your CSS file using the @theme directive.
This change has real benefits beyond aesthetics. CSS custom properties (--color-brand) are real CSS variables that cascade through your stylesheet. You can scope them to components, override them in dark mode with a single CSS rule, and use them directly in your own CSS without Tailwind's utility layer. The config-as-code approach in v3 meant Tailwind's custom values existed only in JavaScript — they weren't actually CSS variables unless you added extra configuration.
The @theme directive tells Tailwind to generate utility classes from your CSS variables automatically. Define --color-brand: #5B21B6 in @theme, and bg-brand, text-brand, border-brand, ring-brand, and shadow-brand all become available immediately.
/* Tailwind v3 — configuration in tailwind.config.js:
module.exports = {
theme: {
extend: {
colors: { brand: '#5B21B6' },
fontFamily: { display: ['Inter', 'sans-serif'] },
}
}
}
*/
/* Tailwind v4 — configuration IN your CSS file: */
@import "tailwindcss";
@theme {
/* Custom colors — these become Tailwind utilities automatically */
--color-brand: #5B21B6;
--color-brand-light: #7C3AED;
--color-brand-dark: #4C1D95;
/* Custom fonts */
--font-family-display: "Inter", sans-serif;
/* Custom spacing */
--spacing-18: 4.5rem;
/* Custom breakpoints */
--breakpoint-xs: 475px;
/* Custom animations */
--animate-wiggle: wiggle 1s ease-in-out infinite;
}
/* After this, you can use:
bg-brand, text-brand, border-brand
font-display
p-18, m-18, gap-18
xs:grid-cols-2
animate-wiggle
*/
/* Advantages of CSS-first config:
→ No JS tooling to parse the config (faster)
→ CSS variables are real CSS custom properties
(they cascade! You can scope them to a component)
→ Dark mode via CSS variables is cleaner */
/* Scoped theme overrides: */
.high-contrast {
--color-brand: #000000;
/* All brand-* utilities in this scope use black */
}
The ability to scope theme overrides to CSS selectors is a capability that v3's JS config simply couldn't provide. In v4, you can have your base brand color as #5B21B6, override it to #000000 in a .high-contrast class, and all bg-brand utilities in that scope automatically use the overridden value — no new utilities needed.
Renamed and Changed Utilities
The number of changed utilities is smaller than you'd expect from a major version bump. Most of the utility name changes are renaming to make the scale more consistent — rounded-sm becoming rounded-xs to make room for a cleaner naming scheme.
<!-- Changes from v3 to v4 -->
<!-- Shadows: opacity-based syntax removed -->
<!-- v3: -->
<div class="shadow-black/50">...</div>
<!-- v4: same, still works ✅ -->
<!-- Border radius: renamed for clarity -->
<!-- v3: rounded-sm, rounded, rounded-md, rounded-lg, rounded-xl, rounded-2xl -->
<!-- v4: rounded-sm → rounded-xs (the old sm is now the default "rounded") -->
<!-- Blur: renamed similarly -->
<!-- v3: blur-sm = 4px, blur = 8px, blur-md = 12px -->
<!-- v4: blur-xs = 4px, blur-sm = 8px, blur = 12px -->
<!-- Ring offset: now part of ring utilities -->
<!-- v3: ring-offset-2 ring-offset-white -->
<!-- v4: ring-2 ring-offset-2 (ring-offset-color via CSS variable) -->
<!-- Placeholder color: -->
<!-- v3: placeholder-gray-400 -->
<!-- v4: placeholder:text-gray-400 (uses variant syntax) -->
<!-- Caret color: -->
<!-- v3: caret-blue-500 -->
<!-- v4: still works ✅ -->
<!-- The full list is shorter than you'd expect.
Most utility names are unchanged.
The config format is the bigger migration. -->
The placeholder-gray-400 → placeholder:text-gray-400 change is worth noting because it affects any form-heavy application. The variant syntax is more consistent and powerful (you can combine it with responsive prefixes: md:placeholder:text-gray-600), but it requires a find-and-replace across your codebase.
Migration: Using the Official Tool
Tailwind provides an official upgrade tool that handles most of the migration automatically. On a typical v3 project, running the tool and reviewing the diff takes 1-3 hours rather than a full day.
# The official upgrade tool (handles ~80% automatically):
npx @tailwindcss/upgrade@next
# What it does:
# 1. Detects your current Tailwind version
# 2. Updates package.json dependencies
# 3. Migrates tailwind.config.js → @theme block in CSS
# 4. Renames changed utilities in your HTML/JSX/TSX files
# 5. Updates postcss.config.js to remove tailwind-specific config
# After running the tool, manually check:
# → Custom plugins (v4 has a new plugin API)
# → JIT mode settings (always-on in v4, nothing to configure)
# → Safelist patterns (moved to @source in CSS)
# → Prefix configuration (now @import "tailwindcss" prefix-my-prefix)
# Manual migration for plugins:
# v3 plugin:
module.exports = {
plugins: [
plugin(function({ addUtilities }) {
addUtilities({
'.clip-circle': { 'clip-path': 'circle(50%)' },
});
}),
],
}
# v4 plugin (in CSS):
@plugin "@tailwindcss/forms";
# v4 custom utilities:
@utility clip-circle {
clip-path: circle(50%);
}
# Use with: class="clip-circle"
The plugin API migration is the part most likely to require manual work. If you have custom Tailwind plugins (beyond the official ones like @tailwindcss/forms), you'll need to rewrite them using the v4 plugin API. The official plugins (@tailwindcss/forms, @tailwindcss/typography, @tailwindcss/aspect-ratio) all have v4 versions that work with the new @plugin syntax.
Dark Mode: Cleaner With CSS Variables
Dark mode implementation is meaningfully improved in v4 due to the CSS variable theme system. In v3, dark mode typically required dark: prefixed utilities on every element (dark:bg-gray-900 dark:text-white), which was verbose and hard to audit. In v4, you define your color tokens as CSS variables and override them in dark mode — elements using those tokens automatically get the dark variant.
This is how shadcn/ui implements its theming, and v4 makes this pattern a first-class part of Tailwind rather than a workaround.
/* v3 dark mode (class strategy):
You had: .dark { ... }
Tailwind applied dark: variants based on .dark class on <html>
*/
/* v4 dark mode is the same by default, but CSS variables make it cleaner: */
@theme {
--color-background: #ffffff;
--color-foreground: #000000;
--color-card: #f9fafb;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: #0a0a0a;
--color-foreground: #ffffff;
--color-card: #1a1a1a;
}
}
/* OR with class strategy for user toggle: */
.dark {
--color-background: #0a0a0a;
--color-foreground: #ffffff;
}
/* Then use: */
<div class="bg-background text-foreground">
<!-- These respond to the CSS variable, no Tailwind dark: needed -->
</div>
/* This is how shadcn/ui works — and it's cleaner than dark:bg-gray-900 everywhere */
Should You Upgrade Now?
The timing question comes down to your project's risk tolerance and the size of your Tailwind usage. For small to medium projects with standard utility usage, the upgrade tool handles almost everything and the migration is low-risk. For large projects with custom plugins or extensive JS config customization, a dedicated sprint is appropriate.
Tailwind CSS v4 status (2026):
→ v4.0 released in early 2025
→ Ecosystem tooling (shadcn, Headless UI, etc.) has v4 support
→ PostCSS plugin still works if you need it (not recommended for new projects)
Upgrade timing:
New project: → Start on v4. No reason to use v3 for new projects.
Existing v3 project → Run the upgrade tool, test, migrate.
Most projects: 2-4 hours including manual fixes.
Large codebase: → Schedule a full day. Test thoroughly.
Custom plugins need manual rewriting.
Risk assessment:
Low risk: Basic Tailwind usage (no custom plugins, standard utilities)
Medium risk: Custom plugin usage, extended theme with many values
Higher risk: Heavy use of the JS config API, complex plugin ecosystem
The 5x build speed improvement alone makes v4 worth it for large projects.
For small projects, the DX improvement (no config file) is the main benefit.
Recommendation:
→ New projects: v4 immediately
→ Existing v3 projects: migrate when you have a focused sprint for it
→ Don't delay — v3 will enter maintenance mode
The shadcn/ui integration is particularly relevant for teams using that component library. shadcn/ui has fully adopted Tailwind v4's CSS variable approach for its theming system, and the v4 migration aligns well with any shadcn-based codebase.
Ecosystem Compatibility in 2026
The most important question for any Tailwind v4 migration is ecosystem compatibility. As of 2026, the major Tailwind ecosystem tools support v4:
The official plugins (@tailwindcss/forms, @tailwindcss/typography, @tailwindcss/container-queries) all have v4 releases. shadcn/ui has migrated to v4's CSS variable approach and is the recommended starting point for new projects. Headless UI v2 works with v4. Radix UI's color system integrates well with v4's @theme blocks.
Less mature third-party component libraries may still be on v3. Check each library's compatibility before migrating a project that uses many component packages. The official @tailwindcss/upgrade tool will flag compatibility issues it can detect.
The PostCSS Path (Still Supported)
For projects that need PostCSS for other processing (autoprefixer for non-standard CSS features, CSS custom media queries polyfills), v4 still supports a PostCSS plugin path. It's not the recommended approach for new projects, but it's fully supported.
/* postcss.config.js (v4 PostCSS path): */
module.exports = {
plugins: {
'@tailwindcss/postcss': {},
// Other PostCSS plugins still work:
'autoprefixer': {},
}
}
This is useful for projects transitioning from v3 that have an established PostCSS pipeline they don't want to restructure immediately. You can adopt v4's CSS features and speed improvements while keeping your existing PostCSS configuration, then migrate to the Vite plugin or standalone binary when ready.
Content Detection and Safelist
Tailwind v4 autodetects which files to scan for utility classes using a smarter algorithm than v3's content array. By default, it scans all files that import your CSS file, plus their imports, transitively. For most projects, this means you don't need to configure anything.
For cases where you need to explicitly include additional files (dynamically constructed class names, separate HTML files), use @source in your CSS:
@import "tailwindcss";
/* Scan additional paths: */
@source "../marketing/*.html";
@source "./email-templates/**/*.mjml";
This replaces v3's content array in tailwind.config.js and integrates naturally with the CSS-first configuration approach.
Integration With Component Libraries
The CSS variable approach in v4 makes integration with design systems and component libraries cleaner than v3. If you're building a component library that will be used in apps using Tailwind, you can expose your design tokens as CSS variables and let consumers override them in their @theme blocks.
This is exactly how shadcn/ui works in v4: the component library defines its styling using CSS variables like --primary and --radius, and each project customizes those variables in their CSS file to match their brand. The components respond automatically without any rebuild required.
Performance Impact on Core Web Vitals
Tailwind v4's build speed improvements don't directly impact user-facing performance — the build happens at development and CI time, not in the browser. However, there are indirect performance benefits.
The CSS output from Tailwind v4 tends to be slightly smaller than v3 due to improved dead code elimination. The Lightning CSS engine more aggressively removes duplicate CSS rules and can merge compatible declarations. For large projects with many utility classes, this translates to a measurably smaller CSS bundle.
The CSS variable approach for theming also has a subtle performance benefit. In Tailwind v3, dark mode required shipping duplicate utility classes (both bg-white and dark:bg-gray-900 for a single element). With CSS variable theming, you ship one bg-background utility and the variable value changes in dark mode — fewer total CSS rules.
Working with Tailwind v4 and shadcn/ui
shadcn/ui is the most commonly used component library with Tailwind, and its v4 migration is worth understanding. shadcn/ui v4 fully embraces the CSS variable approach — all component styles use CSS variables for colors, radii, and spacing. This makes theming more powerful but requires a specific setup.
The shadcn/ui registry provides an updated set of components that use @theme variables. Installing components via npx shadcn add button now adds v4-compatible component code. The globals.css file that shadcn generates contains all the necessary @theme configuration including light and dark mode variables.
For existing shadcn/ui v3 projects migrating to Tailwind v4, the recommended path is to re-add your components from scratch using the v4 registry rather than trying to migrate the existing component code manually. This is faster and ensures you get the optimized v4 implementations.
Conclusion
Tailwind CSS v4 is the clear choice for new projects in 2026. The speed improvements are genuine and valuable, the CSS-first configuration approach reduces boilerplate, and the ecosystem (shadcn, Headless UI, major frameworks) has caught up. For existing v3 projects, the migration is lower-risk than most version bumps — the official upgrade tool handles the majority of changes, and the breaking changes are limited in scope.
Track Tailwind CSS download trends and version adoption at PkgPulse →
Related: Vite vs Webpack 2026 · CSS Modules vs Tailwind 2026 · Browse CSS framework packages
See the live comparison
View tailwindcss v4 vs. v3 on PkgPulse →