Tailwind CSS v4 2026: Migration Best Practices
Tailwind CSS v4 replaces config files with CSS-native theme variables and drops 40% of legacy classes. Migration guide with breaking changes and automated codemods.
Faster Build Speed vs v3
Fewer Legacy Class Aliases
Full Rebuild Time in v4
Changes Automated by Codemod
Key Takeaways
Tailwind CSS v4 is the most significant release in the framework's history. After years of incremental improvements to the v3 architecture, the Tailwind Labs team rewrote the engine from the ground up — replacing the Node.js build pipeline with a high-performance Rust-based engine, eliminating the JavaScript configuration file in favour of CSS-native @theme directives, and canonicalising utility class names to match CSS specifications. The result is a framework that is fundamentally faster, simpler to configure, and better aligned with where the web platform is heading.
The trade-off is a migration path that requires attention. Legacy class aliases accumulated over years of Tailwind history — names like bg-gradient-to-r, flex-shrink-0, and overflow-ellipsis — are removed. Configuration that lived in tailwind.config.js must move into CSS. This guide walks through every breaking change, the automated migration tooling, and the new features that make the upgrade worthwhile.
npx @tailwindcss/upgrade in your project root. The codemod scans your HTML, JSX, TSX, and CSS files, automatically renames class aliases, and updates your configuration. It handles approximately 90% of the mechanical changes and shows a diff of everything it modified.What Changed in Tailwind CSS v4
Tailwind CSS v4 introduces three architectural changes that affect every project upgrading from v3: a new engine built on a high-performance Rust core, a CSS-native configuration system replacing the JavaScript config file, and a class name canonicalisation pass that removes legacy aliases. Understanding what changed at the architecture level clarifies why the migration steps are what they are.
Rust-based core with a unified bundler integration. Eliminates PostCSS plugin chain. Full rebuilds in under 100ms, incremental builds in single-digit milliseconds.
Theme configuration moves from tailwind.config.js into @theme directives in CSS. Design tokens become CSS custom properties available natively in the browser.
Legacy class aliases removed in favour of CSS-spec names. Gradient utilities, flex shorthand, and overflow utilities are the most commonly renamed classes across the ecosystem.
The Unified Toolchain Model
In Tailwind v3, the build pipeline required postcss, autoprefixer, and tailwindcss as separate packages wired together in a postcss.config.js. Tailwind v4 ships native integrations for Vite (@tailwindcss/vite) and PostCSS (@tailwindcss/postcss) that handle CSS processing, vendor prefixes, and Tailwind compilation in a single package. The PostCSS config simplifies to a single plugin entry.
postcss.config.js — v3 vs v4
// v3 — three separate packages
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
// v4 — single package handles everything
module.exports = {
plugins: {
"@tailwindcss/postcss": {},
},
};For projects using Vite, the PostCSS config is no longer needed at all. Import the Vite plugin in vite.config.ts and add a single @import "tailwindcss" line in your global CSS file — that is the entire setup. Next.js projects (which use PostCSS) switch to the @tailwindcss/postcss package for a similarly streamlined configuration. For a deeper look at how this toolchain fits into modern Next.js architecture, see our Next.js 16 performance and server components guide.
Breaking Changes Checklist
Tailwind v4 removes all class aliases that did not match their underlying CSS property names. The table below covers every renamed utility you are likely to encounter in a real codebase. Classes not listed here are unchanged and work identically in v4.
| Category | v3 Legacy Class (Remove) | v4 Canonical Class (Use) |
|---|---|---|
| Gradients | bg-gradient-to-t | bg-linear-to-t |
| Gradients | bg-gradient-to-tr | bg-linear-to-tr |
| Gradients | bg-gradient-to-r | bg-linear-to-r |
| Gradients | bg-gradient-to-br | bg-linear-to-br |
| Gradients | bg-gradient-to-b | bg-linear-to-b |
| Gradients | bg-gradient-to-bl | bg-linear-to-bl |
| Gradients | bg-gradient-to-l | bg-linear-to-l |
| Gradients | bg-gradient-to-tl | bg-linear-to-tl |
| Flexbox | flex-shrink | shrink |
| Flexbox | flex-shrink-0 | shrink-0 |
| Flexbox | flex-grow | grow |
| Flexbox | flex-grow-0 | grow-0 |
| Text Overflow | overflow-ellipsis | text-ellipsis |
| Text Overflow | overflow-clip | text-clip |
| Background Size | bg-[size:200%] | bg-size-[200%] |
| Mask Image | [mask-image:...] | mask-[...] |
| Aspect Ratio | aspect-[16/9] | aspect-video |
| Shadow | drop-shadow-sm | drop-shadow-xs |
| Shadow | shadow-sm | shadow-xs |
| Ring | ring (3px default) | ring-3 |
| Blur | blur-sm (4px) | blur-xs |
bg-gradient-to-* family is by far the most widespread breaking change. Search your codebase for bg-gradient-to- before running the upgrade tool to understand the scope. In a typical 100-component project, gradient classes appear in 20-40 component files. The codemod handles all of them automatically, but a post-migration visual review is recommended.Automated Migration with Codemods
The Tailwind team provides an official upgrade tool that automates the class renames, config file migration, and package updates. For most projects, running the codemod is the correct starting point — manual class hunting is error-prone and time-consuming when the tool handles it reliably.
Running the Upgrade Tool
Terminal — run the official upgrade codemod
# Run from your project root
npx @tailwindcss/upgrade
# For specific directories
npx @tailwindcss/upgrade --config ./tailwind.config.js
# Preview changes without writing files (dry run)
npx @tailwindcss/upgrade --dry-runThe upgrade tool performs the following operations automatically:
Package updates
Uninstalls tailwindcss v3, installs tailwindcss v4 and the appropriate integration package (@tailwindcss/postcss or @tailwindcss/vite).
Config file migration
Reads tailwind.config.js and converts theme extensions, custom colours, spacing, and font scales into @theme directives in your global CSS file.
Class renames across all files
Scans HTML, JSX, TSX, Vue, Svelte, and CSS files for legacy class aliases and replaces them with v4 canonical names.
PostCSS config update
Updates postcss.config.js to use @tailwindcss/postcss and removes the autoprefixer entry (now built into the package).
CSS import update
Converts @tailwind base; @tailwind components; @tailwind utilities; directives to a single @import "tailwindcss" statement.
What the Codemod Cannot Handle
The upgrade tool handles class renames and straightforward configuration migration, but several scenarios require manual intervention:
If your codebase builds class names programmatically (e.g., 'bg-gradient-to-' + direction), the codemod cannot detect these. Search for string patterns containing gradient-to-, flex-shrink, and flex-grow across your codebase and update any dynamic class construction manually.
Plugins using plugin(), addUtilities(), matchUtilities(), or the theme() callback require manual refactoring. Simple addUtilities plugins can be replaced with @utility definitions in CSS. Complex plugins need individual evaluation.
Component libraries like shadcn/ui, daisyUI, and Headless UI have their own v4 migration guides. Check each library's changelog and update to v4-compatible versions before or alongside the Tailwind upgrade. Running the codemod on node_modules is not effective — library migrations must come from updated package versions.
CSS-Native Theme Configuration
The most architecturally significant change in Tailwind v4 is the elimination of the JavaScript config file for theme configuration. All design tokens — colours, spacing, typography, border radius, shadows — now live in a @theme block in your global CSS file. This is not merely a syntax change: it fundamentally changes how theme tokens are consumed in the browser.
From tailwind.config.js to @theme
v3 — tailwind.config.js theme extension
// tailwind.config.js (v3)
module.exports = {
theme: {
extend: {
colors: {
brand: {
50: "#eff6ff",
500: "#3b82f6",
900: "#1e3a8a",
},
},
fontFamily: {
sans: ["Geist", "system-ui", "sans-serif"],
},
borderRadius: {
"4xl": "2rem",
},
},
},
};v4 — globals.css @theme directive
/* globals.css (v4) */
@import "tailwindcss";
@theme {
--color-brand-50: #eff6ff;
--color-brand-500: #3b82f6;
--color-brand-900: #1e3a8a;
--font-sans: "Geist", system-ui, sans-serif;
--radius-4xl: 2rem;
}The key insight is that @theme variables are real CSS custom properties accessible throughout your stylesheet with var(--color-brand-500). You get the Tailwind utility classes (text-brand-500, bg-brand-50) and access to the raw CSS custom property in the same token — no duplication needed.
Runtime Theme Switching
Because theme tokens are CSS custom properties, runtime theme switching no longer requires a rebuild. Override the @theme variables at a selector level and every Tailwind utility using those tokens updates automatically:
globals.css — multi-theme with CSS variable overrides
@import "tailwindcss";
@theme {
/* Default (light) theme */
--color-surface: #ffffff;
--color-surface-raised: #f4f4f5;
--color-text-primary: #18181b;
--color-accent: #3b82f6;
}
/* Dark theme — override tokens at selector level */
[data-theme="dark"] {
--color-surface: #09090b;
--color-surface-raised: #18181b;
--color-text-primary: #fafafa;
--color-accent: #60a5fa;
}
/* High contrast theme */
[data-theme="high-contrast"] {
--color-surface: #000000;
--color-text-primary: #ffffff;
--color-accent: #ffff00;
}Switching themes is a single attribute change on the <html> element: document.documentElement.dataset.theme = 'dark'. This replaces multi-config setups that previously required separate Tailwind builds or complex CSS-in-JS theming solutions. For projects building headless storefronts with complex theming requirements, see our headless CMS comparison for 2026.
New Utilities and Features
Beyond the migration work, Tailwind v4 ships a substantial set of new utilities that were not available in v3. These additions reduce reliance on arbitrary values and cover CSS features that have reached broad browser support in 2024-2026.
First-class container query support without the @tailwindcss/container-queries plugin. Use @container on a parent and @sm:, @md:, @lg: variants on children.
<div class="@container">
<p class="@sm:text-lg @lg:text-2xl">
Responsive to its container
</p>
</div>Tailwind v4 ships its default color palette in OKLCH, enabling wide-gamut colors on supported displays. All colour utilities are defined using oklch() by default, with sRGB fallbacks generated automatically for older browsers.
@theme {
/* Wide-gamut custom color */
--color-brand: oklch(0.6 0.2 250);
}Define custom utilities directly in CSS using the @utility directive. Tailwind handles variant generation (hover:, focus:, dark:, responsive) automatically for all custom utilities.
@utility tab-4 {
tab-size: 4;
}
/* Usage: class="tab-4 hover:tab-4" */Full 3D transform support with perspective-*, rotate-x-*, rotate-y-*, and translate-z-* utilities. No more arbitrary values or custom CSS for card flip animations and perspective effects.
<div class="perspective-800 rotate-y-12
hover:rotate-y-0 transition-transform">
Card with 3D effect
</div>Additional new utilities in v4 include field-sizing-content for auto-resizing textareas, text-wrap-balance and text-wrap-pretty for typographic wrapping control, inset-shadow-* for inner shadow utilities, and not-* variants for CSS :not() selector support.
Performance Improvements
The performance story for Tailwind v4 is the most compelling reason to upgrade for teams working on large codebases. The new engine, built on a Rust core called Lightning CSS, replaces the Node.js-based PostCSS pipeline that had been the bottleneck for large projects running full Tailwind rebuilds.
| Operation | Tailwind v3 | Tailwind v4 | Improvement |
|---|---|---|---|
| Full rebuild (large project) | ~3,500ms | ~100ms | 35x faster |
| Incremental rebuild | ~350ms | ~5ms | 70x faster |
| Dev server startup | ~800ms | ~50ms | 16x faster |
| CSS output size | Baseline | ~15% smaller | Reduced bundle |
The speed gains come from three sources. First, Lightning CSS processes CSS significantly faster than the Node.js PostCSS pipeline because Rust has dramatically lower per-operation overhead. Second, the v4 engine uses a smarter class scanning algorithm that only re-parses files that have changed since the last build rather than scanning the entire codebase on every hot-reload. Third, the unified toolchain eliminates the startup overhead of initialising multiple PostCSS plugins on each rebuild cycle.
Integration with Frameworks
Tailwind v4 ships first-party integrations for the most common JavaScript frameworks. The setup in each case is simpler than v3 because the unified toolchain handles what previously required manual PostCSS configuration.
Next.js (App Router)
Next.js uses PostCSS for CSS processing. Install @tailwindcss/postcss and update your postcss.config.mjs to use it as the sole plugin. In globals.css, replace the three @tailwind directives with a single import.
# Install
npm install tailwindcss @tailwindcss/postcss
# postcss.config.mjs
export default {
plugins: {
"@tailwindcss/postcss": {},
},
};
# globals.css
@import "tailwindcss";
@theme {
--font-sans: var(--font-geist-sans), system-ui, sans-serif;
--font-mono: var(--font-geist-mono), monospace;
}Vite
The Vite integration is the simplest setup. Install @tailwindcss/vite, add it to your Vite config plugins array, and delete the PostCSS config entirely.
# Install
npm install tailwindcss @tailwindcss/vite
// vite.config.ts
import tailwindcss from "@tailwindcss/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [tailwindcss()],
});
/* main.css — single import replaces @tailwind directives */
@import "tailwindcss";Remix / React Router v7
Remix and React Router v7 use Vite under the hood. The Vite plugin approach works identically. Add tailwindcss() to the plugins array in vite.config.ts before the Remix/React Router plugin, then update app/tailwind.css to use @import "tailwindcss".
Design System Migration
For teams maintaining a design system — a shared set of tokens, components, and theme definitions used across multiple projects — the Tailwind v4 migration requires a planned approach rather than a run-the-codemod-and-ship workflow. The good news is that the CSS-native token system in v4 is a significantly better foundation for design systems than the JavaScript config approach in v3.
Token Architecture in v4
In v3, design tokens lived in tailwind.config.js and were consumed exclusively through Tailwind utilities. Any component that needed a token value in a CSS property outside Tailwind's utility coverage had to duplicate the value or use a workaround. In v4, every @theme token is a CSS custom property, making it universally available across Tailwind utilities, CSS Modules, and inline styles alike.
design-tokens.css — shareable token layer
/* Shared across all apps in a monorepo */
@layer theme {
@theme {
/* Brand palette */
--color-primary-50: oklch(0.97 0.02 250);
--color-primary-500: oklch(0.60 0.20 250);
--color-primary-900: oklch(0.25 0.15 250);
/* Semantic tokens */
--color-surface: var(--color-zinc-50);
--color-surface-elevated: #ffffff;
--color-text-body: var(--color-zinc-700);
--color-text-heading: var(--color-zinc-900);
--color-border: var(--color-zinc-200);
/* Typography */
--font-sans: "Inter", system-ui, sans-serif;
--font-display: "Cal Sans", "Inter", sans-serif;
/* Spacing scale additions */
--spacing-18: 4.5rem;
--spacing-22: 5.5rem;
/* Border radius */
--radius-pill: 9999px;
--radius-card: 0.75rem;
}
}
/* Dark theme overrides */
[data-theme="dark"] {
--color-surface: var(--color-zinc-950);
--color-surface-elevated: var(--color-zinc-900);
--color-text-body: var(--color-zinc-300);
--color-text-heading: var(--color-zinc-50);
--color-border: var(--color-zinc-800);
}Migration Checklist for Design Systems
Audit all tailwind.config.js files across packages and consolidate token definitions
Convert theme.extend.colors, theme.extend.spacing, and theme.extend.fontFamily to @theme variables
Update the shared CSS package to export @theme definitions rather than a JS config
Replace plugin() calls with @utility definitions in CSS where possible
Update consuming apps to @import the shared token CSS before @import 'tailwindcss'
Test dark mode and any theme variants by verifying CSS custom property overrides
Update component documentation to reference CSS variable names alongside utility class names
Verify Storybook integration by updating the Storybook Vite or Webpack config to use @tailwindcss/vite or @tailwindcss/postcss
Tailwind v4's CSS-native token system pairs exceptionally well with component-driven development workflows. Our web development services help teams establish scalable design systems and migrate existing codebases to modern toolchains without disrupting ongoing product development.
Ready to Upgrade Your Frontend Stack?
The Digital Applied web development team specialises in framework migrations, design system architecture, and modern CSS toolchain setup. From running the codemod to refactoring custom plugins and validating visual output, we handle the full Tailwind v4 migration lifecycle.
Explore Web Development ServicesRelated Articles
Continue exploring with these related guides