Loading…
Loading…
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
This is part of the AI Automation Engineer Roadmap series.
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.
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:
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.
The biggest conceptual changes are:
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."
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.
/* 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.
<!-- 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.
// postcss.config.js (v4)
export default {
plugins: {
"@tailwindcss/postcss": {},
},
};The tailwindcss PostCSS plugin is replaced by @tailwindcss/postcss.
This is where many migrations slow down.
Look for:
If your setup is heavily customized, the migration is not just package-level. It is a design-system migration.
Do not stop at "the build passes." Check the places most likely to break:
Tailwind migrations often succeed technically while still breaking visual consistency in subtle ways.
This makes it much harder to isolate Tailwind-specific regressions.
It handles a lot of boilerplate, but it does not fully understand your design system or custom conventions.
Moving values into @theme is not enough if the token naming is still messy or inconsistent.
Tailwind changes can alter spacing, color, or utility output in ways that compile fine but still break the interface.
If you are migrating a real product, the safest path is:
This is slower than doing everything at once, but it reduces the risk of shipping subtle design regressions.
If your current v3 setup is stable and heavily customized, upgrading only makes sense when:
There is no prize for taking a migration early if the rest of the stack is not ready.
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.
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
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.
Five essential built-in generic utility types in TypeScript that will save you hundreds of lines of code.
Generate dynamic Open Graph images in Next.js using the ImageResponse API with custom fonts, gradients, and data-driven content for social sharing.
Create reusable GitHub Actions workflows with inputs, secrets, and outputs to eliminate YAML duplication across repositories and teams efficiently.