DEV Community

Ahmed Mahmoud
Ahmed Mahmoud

Posted on • Originally published at eng-ahmed.com

Tailwind CSS v4: What Actually Changed and How I Migrated Two Projects

Headline: Tailwind v4 is the most significant rewrite since the framework launched — CSS-first config, Lightning CSS under the hood, container queries built-in, and no more tailwind.config.js. I migrated two production projects and here's what actually broke and what the upgrade tool misses.

Tailwind CSS v4 arrived with a steeper upgrade curve than most version bumps in the JS ecosystem. The configuration story changed completely. The build engine changed. Several features that previously required plugins are now built-in.

The headline change: no more tailwind.config.js

In v3, configuration lived in a JavaScript file — theme extensions, plugins, content paths. In v4, it moves into your CSS:

@import "tailwindcss";

@theme {
  --color-brand: #6366f1;
  --spacing-18: 4.5rem;
}
Enter fullscreen mode Exit fullscreen mode

Theme tokens become CSS custom properties under @theme, and Tailwind generates utility classes automatically. The content array is gone — v4 detects source files automatically.

The new engine: Lightning CSS

Tailwind v4 ships with Lightning CSS replacing PostCSS as the default:

  • Build times drop significantly (cold rebuild went from ~8s to under 3s on the dashboard)
  • CSS nesting works natively without a plugin
  • Modern CSS features like color-mix(), @starting-style, oklch are transpiled automatically
  • autoprefixer is no longer needed

New features built-in

Container queries — native in v4, no plugin needed:

<div class="@container">
  <div class="grid grid-cols-1 @sm:grid-cols-2">...</div>
</div>
Enter fullscreen mode Exit fullscreen mode

3D transformsrotate-x-45, rotate-y-12, perspective-1000 for card flip effects without inline styles.

Dynamic spacingp-13, mt-22 work without explicit definition.

Migration: the upgrade tool and what it misses

npx @tailwindcss/upgrade@next
Enter fullscreen mode Exit fullscreen mode

The codemod handles the mechanical parts. What it missed:

  1. Custom plugins — the JS plugin API changed; non-trivial v3 plugins need a rewrite to the new @plugin / @utility API
  2. theme() calls in CSS — replace theme('colors.zinc.900') with var(--color-zinc-900); grep for theme( after the tool runs
  3. @apply in third-party components — test after migrating; the underlying CSS variable names changed
  4. JIT-era patterns — some peer/group modifier edge cases needed manual review

Build tool integration

// vite.config.ts
import tailwindcss from '@tailwindcss/vite';

export default { plugins: [tailwindcss()] };
Enter fullscreen mode Exit fullscreen mode

Remove PostCSS config after adding the Vite plugin. For Next.js + Turbopack: the integration is still maturing — test on a branch first.

The checklist

  1. Run the upgrade tool on a clean branch; commit it separately from manual fixes.
  2. Audit custom plugins first — they're the largest time sink.
  3. Grep CSS files for theme( and replace with var(--...).
  4. Install @tailwindcss/vite, remove PostCSS config.
  5. Visual regression pass on dark mode and @apply usage.
  6. Check third-party component library changelog for v4 notes.

Both migrations took a full afternoon. The result: leaner CSS, faster builds, and a config story that finally makes sense.


More writing on eng-ahmed.com. Built by Devya Solutions.

Top comments (0)