Tailwind CSS v4: Everything Changed (Migration Guide 2026)
Complete guide to migrating from Tailwind CSS v3 to v4. CSS-first configuration, new utility classes, and step-by-step migration checklist.
Tailwind CSS v4: Everything Changed (Migration Guide 2026)
Tailwind CSS v4 is not a minor update. It is a ground-up architectural rewrite that replaces JavaScript configuration with CSS-first design tokens, drops PostCSS as a required dependency, and ships a new Rust-based engine that claims up to 5x faster builds. If you have a production project on v3, you are going to feel this migration β but the end result is worth it.
This guide covers every breaking change, shows real before/after code comparisons, and walks you through the migration step by step for both Astro and Next.js projects.
What Changed in Tailwind v4
The core philosophy shift: configuration moves from tailwind.config.js into your CSS file. No more JavaScript config object. No more theme.extend. No more separate purge/content arrays.
Here is what that means in practice:
Whatβs gone:
tailwind.config.js(or.ts) β no longer neededpostcss.config.jsin most setups β the new Vite plugin handles it natively@tailwind base; @tailwind components; @tailwind utilities;β replaced by a single import- The
theme()function in PostCSS β replaced by native CSS variables - JIT mode toggle β itβs always on, always full JIT
Whatβs new:
- CSS-first configuration via
@themeand@layer - Design tokens exposed as CSS custom properties automatically
- Native
@import "tailwindcss"syntax - New
field-sizing,starting-style,not-*,in-*, and container query variants - First-class
@propertysupport for type-safe CSS variables - Vite plugin (
@tailwindcss/vite) replaces the PostCSS setup for most projects
The New @import "tailwindcss" Syntax
v3 β three directives, PostCSS required:
/* globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
v4 β single import, Vite-native:
/* globals.css */
@import "tailwindcss";
That single line replaces the three @tailwind directives and triggers the full Tailwind pipeline. If you are using the Vite plugin, PostCSS is no longer involved at all. For projects still using PostCSS (like Create React App derivatives), you can use @tailwindcss/postcss as a drop-in.
You can also selectively import layers if you need fine-grained control:
@import "tailwindcss/preflight";
@import "tailwindcss/utilities";
CSS Variables for Design Tokens
This is the biggest conceptual change. In v3, design tokens lived in JavaScript and were inaccessible to vanilla CSS. In v4, every theme value is automatically exposed as a CSS custom property.
v3 β JavaScript-only config:
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
brand: {
DEFAULT: "#6366f1",
dark: "#4f46e5",
},
},
fontFamily: {
display: ["Inter", "sans-serif"],
},
},
},
};
v4 β CSS-first with @theme:
/* globals.css */
@import "tailwindcss";
@theme {
--color-brand: #6366f1;
--color-brand-dark: #4f46e5;
--font-display: "Inter", sans-serif;
--spacing-18: 4.5rem;
--radius-xl: 1rem;
}
After defining tokens in @theme, Tailwind generates utility classes for them automatically. bg-brand, text-brand-dark, font-display, p-18, and rounded-xl all just work. You can also reference these variables in arbitrary CSS:
.hero {
background: var(--color-brand);
font-family: var(--font-display);
}
This is a major win. Your design tokens are now accessible everywhere β in CSS animations, in calc() expressions, in color-mix() β without needing the Tailwind theme() function.
If you need to reference a spacing or color value in custom CSS, replace theme('colors.brand') with var(--color-brand).
Before (v3):
.custom-card {
border-color: theme('colors.brand.DEFAULT');
padding: theme('spacing.4');
}
After (v4):
.custom-card {
border-color: var(--color-brand);
padding: var(--spacing-4);
}
New Utility Classes and Variants
Container Queries (No Plugin Required)
Container queries were a plugin in v3. In v4 they are built in.
<!-- v3: required @tailwindcss/container-queries plugin -->
<div class="@container">
<div class="@md:grid @md:grid-cols-2">...</div>
</div>
<!-- v4: same syntax, no plugin needed -->
<div class="@container">
<div class="@md:grid @md:grid-cols-2">...</div>
</div>
not-* Variant
<!-- Apply styles to elements that do NOT match a condition -->
<button class="not-disabled:hover:bg-brand not-disabled:cursor-pointer disabled:opacity-50">
Submit
</button>
in-* Variant (Parent-Context Styling)
<!-- Style an element based on its ancestor's state -->
<div class="group">
<p class="in-[.group:hover]:text-brand">Highlights when parent hovered</p>
</div>
starting-style for Entry Animations
/* Animate elements when they first appear in the DOM */
.popover {
@starting-style {
opacity: 0;
transform: translateY(-4px);
}
opacity: 1;
transform: translateY(0);
transition: opacity 0.2s, transform 0.2s;
}
Or with utilities:
<div class="starting:opacity-0 starting:translate-y-1 transition-all duration-200">
Animated popover
</div>
field-sizing for Auto-Resizing Textareas
<textarea class="field-sizing-content">Auto-resizes to content</textarea>
When building custom CSS for these features, the CSS Box Shadow Generator and CSS Animation Builder can help you prototype values before committing them to your theme config.
Breaking Changes Checklist
Work through this list systematically before running the migration script.
Configuration:
-
tailwind.config.jsmust be converted to@themeblocks in CSS -
contentarray is no longer needed β v4 auto-detects files -
darkMode: 'class'becomes@variant dark (&:where(.dark, .dark *))or use the new@custom-variantAPI -
plugins: [require('@tailwindcss/typography')]becomes@plugin "@tailwindcss/typography"
Removed utilities:
-
bg-opacity-*β usebg-black/50opacity modifier syntax (this existed in v3 too, but the old utilities are removed) -
text-opacity-*β usetext-black/50 -
border-opacity-*β useborder-black/50 -
flex-shrink-0βshrink-0(the old alias is removed) -
flex-growβgrow(old alias removed) -
overflow-ellipsisβtext-ellipsis
Renamed utilities:
-
shadow-smdefault values changed β audit your shadows and use the CSS Box Shadow Generator to match visuals -
ringdefault width changed from 3px to 1px β updateringtoring-3to preserve v3 behavior -
blurdefault value increased β update explicitblur-smwhere needed
PostCSS / build:
- Replace
postcss.config.js+tailwindcssplugin with@tailwindcss/vite(Vite) or@tailwindcss/postcss(other) - Remove
autoprefixerβ v4 handles vendor prefixes internally -
@tailwind base/components/utilitiesβ@import "tailwindcss"
Step-by-Step Migration from v3 to v4
Step 1: Install v4 Packages
For Vite-based projects (Astro, SvelteKit, plain Vite):
npm install tailwindcss@^4.0 @tailwindcss/vite@^4.0
npm uninstall autoprefixer postcss
For Next.js (still uses PostCSS under the hood):
npm install tailwindcss@^4.0 @tailwindcss/postcss@^4.0
npm uninstall autoprefixer
Step 2: Run the Official Upgrade Tool
The Tailwind team ships an automated codemod that handles most of the migration:
npx @tailwindcss/upgrade@next
This tool will:
- Convert
tailwind.config.jsinto@themeblocks - Replace the three
@tailwinddirectives with@import "tailwindcss" - Update deprecated utility class names in your templates
- Generate a diff report of changes it could not handle automatically
Review the diff carefully. The codemod is thorough but not perfect β custom plugins and complex theme() usages often need manual review.
Step 3: Update Your CSS Entry Point
/* Before */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* After */
@import "tailwindcss";
If you have custom base styles, keep them after the import using @layer base:
@import "tailwindcss";
@layer base {
h1, h2, h3 {
font-family: var(--font-display);
}
body {
color: var(--color-gray-900);
}
}
Step 4: Convert Your Theme
Take each theme.extend block from your config and convert it to @theme:
// v3: tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
primary: { 50: "#eff6ff", 500: "#3b82f6", 900: "#1e3a5f" },
},
borderRadius: { "4xl": "2rem" },
maxWidth: { "8xl": "88rem" },
},
},
};
/* v4: globals.css */
@theme {
--color-primary-50: #eff6ff;
--color-primary-500: #3b82f6;
--color-primary-900: #1e3a5f;
--radius-4xl: 2rem;
--container-8xl: 88rem;
}
Step 5: Update Dark Mode Config
/* v3 equivalent: darkMode: 'class' */
@custom-variant dark (&:where(.dark, .dark *));
/* or use the media-query based dark mode (default in v4) */
/* no config needed β @media (prefers-color-scheme: dark) is automatic */
Step 6: Convert Plugins
/* v3: plugins: [require('@tailwindcss/typography'), require('@tailwindcss/forms')] */
/* v4: */
@plugin "@tailwindcss/typography";
@plugin "@tailwindcss/forms";
Third-party plugins that expose their own utility classes will need v4-compatible releases. Check each pluginβs changelog before migrating.
Step 7: Fix the Ring Width Regression
This trips up almost everyone. In v3, ring applies a 3px ring. In v4 it applies a 1px ring. If you use ring for focus indicators, update:
<!-- v3 behavior: 3px ring -->
<input class="focus:ring focus:ring-blue-500" />
<!-- v4: explicit width to preserve v3 behavior -->
<input class="focus:ring-3 focus:ring-blue-500" />
Step 8: Verify Your Build
npm run build
Check for:
- Any remaining
theme()calls in CSS (replace withvar()) - Classes using the removed
*-opacity-*pattern - Any
flex-shrink,flex-grow,overflow-ellipsisold names
Astro-Specific Migration
Astro projects using the official @astrojs/tailwind integration need to switch to the Vite plugin directly:
npm uninstall @astrojs/tailwind
npm install @tailwindcss/vite@^4.0
Update astro.config.mjs:
// Before
import tailwind from "@astrojs/tailwind";
export default defineConfig({
integrations: [tailwind()],
});
// After
import { defineConfig } from "astro/config";
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
vite: {
plugins: [tailwindcss()],
},
});
Move your CSS import to a global layout file if it isnβt already:
---
// src/layouts/BaseLayout.astro
---
<html>
<head>
<!-- other head content -->
</head>
<body>
<slot />
</body>
</html>
<style is:global>
@import "tailwindcss";
/* your @theme blocks here */
</style>
Or keep CSS in a separate file and import it in your layout:
---
import "../styles/globals.css";
---
Generating gradients for your Astro site? Use the CSS Gradient Generator to create gradient values you can drop directly into your
@themeblock as custom properties.
Next.js-Specific Migration
Next.js 15 still relies on PostCSS internally, so the migration path differs slightly.
npm install tailwindcss@^4.0 @tailwindcss/postcss@^4.0
Update postcss.config.mjs:
// Before
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
// After
export default {
plugins: {
"@tailwindcss/postcss": {},
},
};
Delete tailwind.config.ts after running the upgrade tool and verifying your @theme blocks are correct.
For App Router projects, update app/globals.css:
/* Before */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* After */
@import "tailwindcss";
@theme {
/* your tokens */
}
One gotcha in Next.js: if you use CSS Modules alongside Tailwind, the new variable-based tokens are available in .module.css files via var(--color-*) just like any other CSS custom property. No special handling needed.
Performance Improvements in v4
The headline number from the Tailwind team is a 5x faster full build and a 100x faster incremental rebuild compared to v3 in large projects.
This is achieved through:
Oxide engine (Rust-based): The scanner that detects Tailwind classes in your source files is rewritten in Rust. For a project with hundreds of components, the difference is measurable β cold builds that took 2+ seconds in v3 complete in under 500ms in v4.
No content scanning config needed: v4 automatically scans all files that Vite processes. This eliminates false negatives from misconfigured content arrays (a common source of missing classes in production builds) and removes a whole class of βworks in dev, broken in prodβ bugs.
Smaller CSS output: The new engine produces tighter CSS with better deduplication. Design tokens as CSS variables also means the browser can apply them natively without Tailwind generating separate property variants for every combination.
Lightning CSS integration: v4 uses Lightning CSS for transforms, vendor prefixing, and modern CSS syntax lowering. This replaces autoprefixer and adds support for nesting, :is(), color-mix(), and other modern features that get compiled down for older browsers automatically.
For teams using the CSS Flexbox or CSS Grid Generator tools to plan layouts, you will notice that v4βs native CSS nesting support means you can write nested flex/grid rules directly in @layer blocks without a preprocessor.
Summary
Tailwind CSS v4 is a significant migration, but the upgrade codemod handles the mechanical parts. The conceptual shift to CSS-first configuration actually makes the framework easier to reason about β your design tokens live in one place, they are real CSS variables accessible everywhere, and the build is dramatically faster.
Key points to remember:
- Run
npx @tailwindcss/upgrade@nextfirst β it does the heavy lifting - Move from
tailwind.config.jsto@theme {}blocks in CSS - Replace
@tailwind base/components/utilitieswith@import "tailwindcss" - Fix the
ringwidth regression (3px β 1px default) - Switch to
@tailwindcss/vitefor Vite/Astro,@tailwindcss/postcssfor Next.js - Use
var(--color-*)instead oftheme()in custom CSS
The build speed improvements alone are worth the migration for large projects. Start with a feature branch, run the codemod, fix the handful of manual issues, and you will be on v4 in an afternoon.
50 CLI Commands That Save Hours Every Week
The exact Git shortcuts, file search tricks, Docker commands, and productivity hacks that senior developers use daily. Compiled from 5+ years of shipping production code.
- Git mastery (10 cmds)
- File & search wizardry
- Docker & DevOps tricks
- Process & network tools
- Productivity boosters
- macOS & Linux install guide
No spam. 1 email/week max. Unsubscribe anytime.
Found this guide useful? Check out our free developer tools.
Affiliate disclosure: Some links below are affiliate links β we may earn a small commission at no extra cost to you. Learn more.
Recommended Tools & Resources
DigitalOcean
$200 credit for new users. Simple, affordable cloud hosting for developers.
GitHub Student Pack
Free access to 100+ developer tools. Perfect for students and new devs.
Vercel
Deploy frontend apps instantly. Free tier is generous for side projects.
DevPlaybook Products
Boilerplates, scripts & AI toolkits to 10x your dev workflow.