DEV Community

Atlas Whoff
Atlas Whoff

Posted on

Tailwind CSS v4: What Actually Changed and How to Migrate

Tailwind CSS v4: What Actually Changed and How to Migrate

Tailwind v4 is a complete rewrite. The utility classes you know still work, but the configuration system, build pipeline, and theming model are all different. Here's what changed and how to migrate without breaking your project.

The Biggest Change: No More tailwind.config.js

v4 moves configuration into CSS itself:

/* v4: app/globals.css */
@import "tailwindcss";

@theme {
  --color-primary: oklch(55% 0.2 250);
  --color-primary-foreground: white;
  --font-sans: "Inter", sans-serif;
  --radius: 0.5rem;
  --spacing-18: 4.5rem;
}
Enter fullscreen mode Exit fullscreen mode
// v3: tailwind.config.ts (gone in v4)
module.exports = {
  theme: {
    extend: {
      colors: { primary: '#3b82f6' },
    },
  },
}
Enter fullscreen mode Exit fullscreen mode

Your entire theme is now CSS custom properties. This means:

  • Live theme updates without rebuilding
  • Easy runtime theming (dark mode, user preferences)
  • Framework-agnostic (works in any CSS context)

New Build Engine: Oxide

v4 ships with a Rust-based engine (Oxide) that replaces PostCSS as the primary build tool:

# v3
npm install -D tailwindcss postcss autoprefixer

# v4
npm install -D @tailwindcss/vite   # or @tailwindcss/cli
# postcss config no longer needed for most setups
Enter fullscreen mode Exit fullscreen mode

Vite config:

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

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

Next.js (still uses PostCSS plugin):

// postcss.config.js
module.exports = { plugins: { '@tailwindcss/postcss': {} } };
Enter fullscreen mode Exit fullscreen mode

Build times drop 5-10x for large projects. Full rebuild on cold start is instant for most apps.

What's New in the Utility Layer

CSS Variables as First-Class Citizens

<!-- v4: use CSS variables directly in utilities -->
<div class="bg-[--color-primary] text-[--color-primary-foreground]">
  Uses your theme tokens
</div>
Enter fullscreen mode Exit fullscreen mode

Arbitrary Property Shorthand

<!-- v4 shorthand -->
<div class="[mask-type:luminance]">

<!-- v3 was the same syntax, but v4 resolves CSS variables inside -->
<div class="[color:--my-color]">
Enter fullscreen mode Exit fullscreen mode

New Variants

<!-- Nth-child selectors -->
<li class="odd:bg-gray-50 even:bg-white">

<!-- Field sizing (new CSS property) -->
<textarea class="field-sizing-content">

<!-- Starting style for enter animations -->
<div class="starting:opacity-0 transition-opacity">
  Fades in on mount without JavaScript
</div>
Enter fullscreen mode Exit fullscreen mode

not-* Variant

<!-- Style when NOT matching -->
<button class="not-disabled:hover:bg-blue-600 disabled:opacity-50">
Enter fullscreen mode Exit fullscreen mode

Migration Steps

Step 1: Update Dependencies

npm install -D tailwindcss@next @tailwindcss/vite
# or for Next.js:
npm install -D tailwindcss@next @tailwindcss/postcss
Enter fullscreen mode Exit fullscreen mode

Step 2: Update CSS Entry Point

/* Before (v3) */
@tailwind base;
@tailwind components;
@tailwind utilities;

/* After (v4) */
@import "tailwindcss";
Enter fullscreen mode Exit fullscreen mode

Step 3: Move Theme to CSS

/* Before: tailwind.config.ts */
/* After: globals.css */
@theme {
  --color-brand: #6366f1;
  --color-brand-dark: #4f46e5;

  /* Spacing tokens */
  --spacing-18: 4.5rem;
  --spacing-22: 5.5rem;
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Update Dark Mode

/* v4 default: uses @media (prefers-color-scheme: dark) */
/* For class-based dark mode: */
@import "tailwindcss";
@variant dark (&:where(.dark, .dark *));
Enter fullscreen mode Exit fullscreen mode

Step 5: Remove tailwind.config.js

Once theme is in CSS, the config file is no longer needed. The content paths are auto-detected in v4.

Breaking Changes to Watch

v3 v4
ring = 3px ring ring = 1px ring (use ring-3 for old behavior)
shadow = specific shadow shadow-sm is the new shadow default
blur applied to element No change, but compositing rules differ
bg-opacity-* Use bg-black/50 syntax (works in v3 too)
text-opacity-* Use text-black/50 syntax

shadcn/ui Migration

shadcn components use Tailwind extensively. Update the CSS variables in your globals.css:

/* shadcn v4 compatible theme */
@theme {
  --background: oklch(100% 0 0);
  --foreground: oklch(9% 0.01 285);
  --card: oklch(100% 0 0);
  --primary: oklch(55% 0.2 250);
  --primary-foreground: oklch(98% 0 0);
  --radius: 0.5rem;
}
Enter fullscreen mode Exit fullscreen mode

shadcn's CLI now generates v4-compatible components if you're on the latest version.


Build Faster With Pre-Configured Tooling

The AI SaaS Starter Kit ships with Tailwind v4, shadcn/ui, and Next.js App Router pre-wired — skip the migration and start with a working v4 setup.

$99 one-time → whoffagents.com


Migrated to v4 yet? What broke (or what got better)? Share below.

Top comments (0)