Tailwind CSS v4 Migration: What Changed and How to Upgrade
Step-by-step guide to migrating from Tailwind CSS v3 to v4, covering the new CSS-first config, removed utilities, and breaking changes to fix.
Tags
Tailwind CSS v4 Migration: What Changed and How to Upgrade
This is part of the AI Automation Engineer Roadmap series.
TL;DR
Tailwind v4 replaces tailwind.config.js with CSS-first configuration using @theme and @import, drops several deprecated utilities, and ships with a new high-performance Oxide engine.
Why This Matters
Your project uses Tailwind v3 with a JavaScript config file, custom theme values, and plugins. Upgrading to v4 breaks your build because the configuration format has fundamentally changed, and several utility classes you rely on have been renamed or removed.
Tailwind v4 is not just a minor version bump. It changes how configuration works, how the PostCSS pipeline is wired, and how teams think about theme tokens. That means migration quality affects:
- ›build stability
- ›team conventions
- ›design token organization
- ›plugin compatibility
- ›how much churn lands in one PR
If you approach it as a blind package upgrade, the migration is painful. If you treat it as a controlled refactor, it is much more manageable.
What Actually Changed
The biggest conceptual changes are:
- ›CSS-first configuration
- ›the new engine and build pipeline
- ›utility and syntax cleanup
- ›a stronger emphasis on CSS variables and tokens
The migration is easier once you stop thinking in terms of "Tailwind config as a JavaScript object" and start thinking in terms of "design tokens and utilities defined closer to CSS."
Step 1: Run the Automated Upgrade
npx @tailwindcss/upgradeThis codemod handles most renames and config migrations automatically. Review the changes before committing.
Do not treat the codemod as the end of the migration. It gets you through the obvious mechanical changes, but custom theme setup, plugins, and edge-case utilities still need review.
Step 2: Move Config to CSS
/* app.css - Before (v3 with JS config) */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* app.css - After (v4 CSS-first) */
@import "tailwindcss";
@theme {
--color-brand: #3b82f6;
--color-brand-dark: #1d4ed8;
--font-family-heading: "Inter", sans-serif;
--breakpoint-3xl: 1920px;
}Custom theme values are now CSS custom properties inside @theme, not JavaScript objects.
This is the change that usually feels the biggest. Teams used to centralizing everything in tailwind.config.js now need to rethink how tokens are organized and where configuration belongs.
Step 3: Update Removed Utilities
<!-- v3 (removed) -> v4 (replacement) -->
<!-- bg-opacity-50 -> bg-black/50 -->
<!-- text-opacity-75 -> text-white/75 -->
<!-- flex-grow-0 -> grow-0 -->
<!-- flex-shrink -> shrink -->
<!-- decoration-slice -> box-decoration-slice -->Utility cleanup is usually straightforward, but it can create a lot of noisy diffs. This is why the migration is better done as a focused branch instead of mixing it with unrelated UI work.
Step 4: Update PostCSS Config
// postcss.config.js (v4)
export default {
plugins: {
"@tailwindcss/postcss": {},
},
};The tailwindcss PostCSS plugin is replaced by @tailwindcss/postcss.
Step 5: Audit Custom Plugins and Theme Extensions
This is where many migrations slow down.
Look for:
- ›custom plugin functions
- ›theme extension helpers
- ›arbitrary utility generators
- ›assumptions tied to the old config structure
If your setup is heavily customized, the migration is not just package-level. It is a design-system migration.
Step 6: Verify the Real Surfaces
Do not stop at "the build passes." Check the places most likely to break:
- ›responsive layouts
- ›theme colors
- ›typography scales
- ›forms
- ›dark mode or theme switching
- ›components using custom utilities
Tailwind migrations often succeed technically while still breaking visual consistency in subtle ways.
Common Mistakes
Doing the Migration Inside a Large Feature Branch
This makes it much harder to isolate Tailwind-specific regressions.
Assuming the Codemod Solves Everything
It handles a lot of boilerplate, but it does not fully understand your design system or custom conventions.
Forgetting Design Token Semantics
Moving values into @theme is not enough if the token naming is still messy or inconsistent.
Skipping Visual Regression Checks
Tailwind changes can alter spacing, color, or utility output in ways that compile fine but still break the interface.
A Safer Migration Plan
If you are migrating a real product, the safest path is:
- ›upgrade Tailwind in isolation
- ›run the codemod
- ›migrate config into CSS intentionally
- ›clean up removed utilities
- ›audit plugins and custom tokens
- ›run a visual QA pass on core pages
This is slower than doing everything at once, but it reduces the risk of shipping subtle design regressions.
When Not to Upgrade Immediately
If your current v3 setup is stable and heavily customized, upgrading only makes sense when:
- ›you want the new CSS-first model
- ›you are already touching the design system
- ›build performance improvements matter
- ›plugin compatibility is confirmed
There is no prize for taking a migration early if the rest of the stack is not ready.
Why This Works
The CSS-first approach means your configuration lives in the same file as your styles, making it portable across frameworks without requiring JavaScript tooling. The @theme directive compiles down to standard CSS custom properties, which browsers understand natively. The Oxide engine, rewritten in Rust, delivers significantly faster builds while maintaining full backward compatibility with v3 utility class names that were not deprecated.
Final Takeaway
Tailwind v4 is a worthwhile migration when approached as a design-system and tooling upgrade, not just a dependency bump. Use the codemod to accelerate the mechanical work, but plan for token cleanup, plugin review, and visual QA before you call it done.
Collaboration
Need help with a project?
Let's Build It
I help startups and established companies design, build, and scale world-class digital products. From deep technical architecture to pixel-perfect UI — let's bring your vision to life.
Related Articles
TypeScript Utility Types You Should Know
Five essential built-in generic utility types in TypeScript that will save you hundreds of lines of code.
Generate Dynamic OG Images in Next.js
Generate dynamic Open Graph images in Next.js using the ImageResponse API with custom fonts, gradients, and data-driven content for social sharing.
GitHub Actions Reusable Workflows: Stop Repeating Yourself
Create reusable GitHub Actions workflows with inputs, secrets, and outputs to eliminate YAML duplication across repositories and teams efficiently.