Tailwind v4 is not an upgrade. It is a rewrite. The class names mostly stay the same, but everything underneath is new: a new engine (oxide), CSS-first config, no more PostCSS pipeline by default, and several v3 patterns stop working silently.
I migrated a client app on day one and hit every wall. Here is the guide I wish I had before starting.
What changed
1. The engine
v3 used a JIT engine layered on top of PostCSS. v4 ships a Rust-based engine (oxide) that is much faster. On a project with ~500 templates, full builds went from 1.2s to 240ms. Incremental builds are nearly instant.
You will not notice the speed until you go back to a v3 project. Then you will.
2. The config
tailwind.config.js is gone. Theme lives in CSS now, through the @theme directive:
/* app.css */
@import "tailwindcss";
@theme {
--color-brand: #0066ff;
--color-brand-dark: #0047b3;
--font-display: "Inter", sans-serif;
--radius-card: 0.75rem;
}These become utilities automatically:
<button className="bg-brand text-white rounded-card font-display">
Click
</button>The first time I saw this I was skeptical. Two days in, I prefer it. Configuration lives next to the CSS it affects, and you do not need to keep a JS file in sync.
3. The entry point
Old:
@tailwind base;
@tailwind components;
@tailwind utilities;New:
@import "tailwindcss";One line. The layers are still there under the hood, but you do not reference them directly most of the time.
4. The PostCSS plugin
v3 needed postcss, autoprefixer, and tailwindcss in your PostCSS config. v4 ships its own plugin that handles autoprefixing:
// postcss.config.js
export default {
plugins: {
"@tailwindcss/postcss": {},
},
}Drop autoprefixer. Drop postcss-import unless you have a specific reason. The Tailwind plugin handles it.
5. Browser support
v4 dropped support for some older browsers. The target is now modern evergreen browsers by default. If you need to support legacy Safari or old Android browsers, configure @supports queries manually.
What breaks
Custom plugin APIs
If you wrote v3 plugins using the JS API, they will not work in v4 without changes. The plugin API is different. Most published plugins have either shipped v4-compatible versions or have replacements.
Check your dependencies:
@tailwindcss/forms— updated, works@tailwindcss/typography— updated, works@tailwindcss/aspect-ratio— replaced by built-inaspect-*utilities@tailwindcss/line-clamp— replaced by built-inline-clamp-*utilities
@apply in regular CSS
@apply still works but with caveats. It can only reference utilities, not arbitrary CSS. If you used @apply with custom class names defined elsewhere in your CSS, those break.
Use @apply for utility composition. For everything else, write regular CSS.
Class name changes
A handful of utilities renamed:
shadow-sm→shadow-xsshadow→shadow-smrounded-sm→rounded-xsrounded→rounded-smoutline-none→outline-hidden(for the old behavior)
This is the most annoying part of the migration. Most teams miss a few and ship subtle visual regressions. Audit carefully.
theme() function
The theme() function in arbitrary values still works, but references theme keys defined in @theme, not your old JS config. If you had theme('colors.brand.500'), change it to use the new theme key.
How I migrate a real project
Step by step:
-
Update dependencies
npm install tailwindcss@latest @tailwindcss/postcss@latest -
Update PostCSS config — remove
autoprefixerandtailwindcss, add@tailwindcss/postcss. -
Update CSS entry point — replace the three
@tailwindlines with@import "tailwindcss";. -
Convert config — port your
tailwind.config.jstheme into@themeblocks. For complex configs, use the official upgrade tool:npx @tailwindcss/upgrade@latestThis handles most of the mechanical changes automatically.
-
Audit renamed classes — grep your codebase for
shadow-sm,rounded-sm,outline-none, and update to the new names. -
Update third-party plugins — check that your plugins are v4-compatible. Find replacements for ones that are not.
-
Test visually — class renames silently change your UI. Walk through every screen.
What got way better
- Speed. Builds are nearly instant.
- CSS-first config. Theme lives where it is used. No more context-switching to a JS file.
- No more purge. JIT is the only mode. Class detection just works.
- Smaller output. Generated CSS is smaller due to better deduplication.
- Better error messages. v4 tells you which line of which template caused a problem.
Should you migrate?
For new projects: yes, immediately.
For existing projects: depends.
- Small / new codebase: migrate now.
- Large codebase with simple Tailwind usage: migrate now, run the upgrade tool.
- Heavy use of v3-only plugins: wait for the ecosystem to catch up, then migrate.
- Marketing sites with frequent redesigns: migrate now, the speed alone is worth it.
Tailwind v4 is faster, the config is cleaner, and the migration is mostly mechanical. Audit your plugins first, run the upgrade tool, and test visually. The hard parts are small and well-documented.
Want help migrating to Tailwind v4?
I migrate Next.js and React apps to Tailwind v4 — upgrades, audits, and design system refactors. Let's talk.
Frequently Asked Questions
Is Tailwind CSS v4 a breaking change?
Yes. v4 is a rewrite of the engine and the configuration system. The class names mostly stay the same, but tailwind.config.js is replaced by CSS-based @theme configuration, and some v3 patterns stop working.
Do I need tailwind.config.js in Tailwind v4?
No. v4 moves theme configuration into CSS using the @theme directive. You can still use a JS config for compatibility, but the recommended approach is CSS-first configuration.
Is Tailwind v4 faster than v3?
Yes, significantly. The new oxide engine is up to 10x faster on incremental builds and 5x faster on full builds. JIT is now the only mode, so there is no separate purge step.
Should I migrate my project to Tailwind v4?
Migrate new projects immediately. For existing projects, audit dependencies first — many v3 plugins and UI libraries do not yet support v4. Migrate when your stack is ready, not before.