Tutorials

Tailwind CSS v4 Migration Guide: Everything That Changed

Tailwind CSS v4 is a complete rewrite. CSS-first config, no more tailwind.config.js, new class names, 5-10x faster builds. Here's how to migrate without breaking your project.

April 11, 202610 min read
Share:
Tailwind CSS v4 Migration Guide: Everything That Changed

Tailwind CSS v4 is not an upgrade — it's a rewrite. The team rebuilt the engine in Rust, dropped the JavaScript config file entirely, changed how you import it, renamed classes, and made content detection automatic. If you open a v3 project with v4 installed, you will have errors.

The good news: there's an automated migration tool that handles ~90% of the work, and every breaking change has a direct fix. This guide covers all of them.

Why v4 Is a Big Deal

Three things changed fundamentally:

  1. The engine — rebuilt in Rust (via Lightning CSS). Cold builds are 5–10x faster. Incremental rebuilds happen in microseconds.
  2. The config — moved from tailwind.config.js to CSS-native @theme blocks inside your stylesheet.
  3. The PostCSS plugin — replaced. The old tailwindcss PostCSS plugin is gone. You now use @tailwindcss/postcss.

Browser support minimum: Safari 16.4+, Chrome 111+, Firefox 128+. If you need to support anything older, stay on v3.4 for now.


Run the Migration Tool First

Before touching anything manually, run the official upgrade tool:

npx @tailwindcss/upgrade

This handles approximately 90% of mechanical changes automatically — class renames, config migration, import updates. It shows a diff of everything it touched. Review it, then deal with whatever's left manually.

If you're on a large project, run it on a branch:

git checkout -b tailwind-v4-migration
npx @tailwindcss/upgrade
git diff

Breaking Change #1 — No More tailwind.config.js

What changed: The JavaScript config file is gone. Design tokens (colors, spacing, fonts, breakpoints) now live in your CSS file using @theme.

Before (v3):

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {
        brand: '#F97316',
        surface: '#0D1117',
      },
      fontFamily: {
        syne: ['Syne', 'sans-serif'],
      },
    },
  },
}

After (v4):

/* globals.css */
@import "tailwindcss";
 
@theme {
  --color-brand: #F97316;
  --color-surface: #0D1117;
  --font-syne: 'Syne', sans-serif;
}

Those CSS variables become Tailwind utilities automatically. bg-brand, text-brand, font-syne — all work without any extra config.

What to do: Run the migration tool. It converts your tailwind.config.js to @theme blocks automatically. Review the output and adjust any custom plugins or safelist entries manually.


Breaking Change #2 — @tailwind Directives Are Gone

What changed: The old three-liner at the top of your CSS file no longer works.

Before (v3):

@tailwind base;
@tailwind components;
@tailwind utilities;

After (v4):

@import "tailwindcss";

One line. That's it. The migration tool replaces this automatically.

If you use a bundler (Vite, Next.js, webpack), also update the PostCSS plugin:

npm install @tailwindcss/postcss
// postcss.config.js
export default {
  plugins: {
    '@tailwindcss/postcss': {},
  },
}

For Vite specifically, there's a dedicated plugin that's even faster:

npm install @tailwindcss/vite
// vite.config.js
import tailwindcss from '@tailwindcss/vite'
 
export default {
  plugins: [tailwindcss()],
}

Breaking Change #3 — Content Detection Is Automatic

What changed: You no longer need to specify the content array. Tailwind v4 scans your project automatically.

Before (v3):

module.exports = {
  content: [
    './app/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx,mdx}',
  ],
}

After (v4): Nothing. It just works.

Tailwind v4 detects all files in your project root that contain class names. The only exception is if you need to explicitly exclude paths — you can do that with @source in your CSS:

@import "tailwindcss";
@source "../node_modules/some-ui-library";

Breaking Change #4 — Class Renames

Several classes were renamed to match their actual CSS property names. The migration tool handles all of these, but you should know what changed:

Gradients

v3v4
bg-gradient-to-rbg-linear-to-r
bg-gradient-to-lbg-linear-to-l
bg-gradient-to-tbg-linear-to-t
bg-gradient-to-bbg-linear-to-b
bg-gradient-to-trbg-linear-to-tr
bg-gradient-to-tlbg-linear-to-tl
bg-gradient-to-brbg-linear-to-br
bg-gradient-to-blbg-linear-to-bl

Flex utilities

v3v4
flex-shrink-0shrink-0
flex-shrinkshrink
flex-grow-0grow-0
flex-growgrow

Other renames

v3v4
overflow-ellipsistext-ellipsis
decoration-clonebox-decoration-clone
decoration-slicebox-decoration-slice

If you have any of these in your codebase, run a global find-and-replace after the migration tool runs.


Breaking Change #5 — Default Border Color Changed

What changed: In v3, border without a color specified defaulted to gray-200. In v4 it defaults to currentColor.

What breaks: Any border or divide utilities you used without specifying a color will now render differently.

How to fix:

Option 1 — add explicit color everywhere it matters:

<!-- v3: border was gray-200 automatically -->
<div class="border">...</div>
 
<!-- v4: must be explicit -->
<div class="border border-gray-200">...</div>

Option 2 — override the default in @theme:

@theme {
  --default-border-color: var(--color-gray-200);
}

Breaking Change #6 — ring Default Width Changed

What changed: ring in v3 added a 3px ring. In v4 it's 1px.

How to fix:

If you want the v3 behavior:

<!-- v3 -->
<button class="ring">...</button>
 
<!-- v4 equivalent -->
<button class="ring-3">...</button>

New Features Worth Using

Arbitrary variants without plugins

In v4 you can write arbitrary CSS variants directly in HTML without needing a plugin:

<div class="[&>p]:text-sm [&>p]:text-white/60">...</div>

@starting-style support

Built-in support for CSS @starting-style for enter/exit animations:

@layer utilities {
  .animate-enter {
    @starting-style {
      opacity: 0;
      transform: translateY(-8px);
    }
  }
}

Container queries built-in

Container queries are now part of Tailwind core — no plugin required:

<div class="@container">
  <div class="@sm:grid-cols-2 @lg:grid-cols-4">...</div>
</div>

Dynamic values without arbitrary syntax

In v3, spacing values not in your scale required w-[37px]. In v4, the scale is continuous — w-37 just works.


Next.js Specific Setup

If you're on Next.js, the setup is slightly different:

npm install tailwindcss@latest @tailwindcss/postcss
// postcss.config.mjs
const config = {
  plugins: {
    '@tailwindcss/postcss': {},
  },
}
export default config
/* app/globals.css */
@import "tailwindcss";
 
@theme {
  /* your custom tokens here */
}

Remove tailwind.config.ts entirely after running the migration tool.


Full Migration Checklist

[ ] git checkout -b tailwind-v4-migration
[ ] npm install tailwindcss@latest @tailwindcss/postcss
[ ] npx @tailwindcss/upgrade (review the diff carefully)

Manual checks after the tool runs:
[ ] Replace @tailwind directives with @import "tailwindcss"
[ ] Update PostCSS config to use @tailwindcss/postcss
[ ] Remove tailwind.config.js (migrated to @theme in CSS)
[ ] Check border utilities — default color changed to currentColor
[ ] Check ring utilities — default width changed to 1px
[ ] Search for bg-gradient-to-* → bg-linear-to-*
[ ] Search for flex-shrink/flex-grow → shrink/grow
[ ] Test in Safari 16.4+, Chrome 111+, Firefox 128+
[ ] Verify build output looks correct
[ ] Merge and deploy

Should You Migrate Now?

Yes, if:

  • You're starting a new project — use v4 from day one
  • Your team is on modern browsers (Safari 16.4+)
  • Build time is a pain point — the 5–10x speedup is real
  • You want container queries and other new features

Wait, if:

  • You need IE or old Safari support
  • You have a large, complex tailwind.config.js with many plugins — test thoroughly before committing
  • You're mid-sprint and can't afford any visual regressions right now

The migration is well worth it long-term. The Rust engine is genuinely much faster, and the CSS-first config model is cleaner than a JavaScript file. Give yourself a quiet afternoon, run the upgrade tool, and fix what's left.


Building a Next.js project from scratch? Check out our Next.js Server vs Client Components guide for the full modern stack setup.

#tailwind#css#migration#frontend#webdev#react#nextjs
Share:

Enjoyed this article?

Join 2,400+ developers getting weekly insights on Claude Code, React, and AI tools.

No spam. Unsubscribe anytime. By subscribing you agree to our Privacy Policy.