DEV Community

Anup Karanjkar
Anup Karanjkar

Posted on • Originally published at wowhow.cloud

Tailwind CSS v4 Migration Guide: Everything That Changed in 2026

Tailwind CSS v4 is not an incremental update — it is a rewrite. The tailwind.config.js file is gone. Configuration happens in CSS via the @theme directive. The color palette changed. The plugin API changed. The way you extend the framework changed.

This guide covers every breaking change and new feature, with before-and-after code for each. By the end you will have a working migration plan and understand why v4's CSS-first approach is actually easier to maintain than v3's JavaScript config.

For Tailwind-based templates and starter kits, see our UI template catalog. We also have free calculators built on Tailwind v4 if you want to see production code.

Installing Tailwind CSS v4

# v4 is installed differently — no @tailwindcss/postcss peer dependency needed
npm install tailwindcss@^4.0 @tailwindcss/vite

# For Next.js (no PostCSS config required in v4)
npm install tailwindcss@^4.0

# Verify
npx tailwindcss --version  # 4.x.x
Enter fullscreen mode Exit fullscreen mode

The Biggest Change: CSS-First Configuration

In v3, you configured Tailwind in tailwind.config.js. In v4, you configure it directly in your CSS using the @theme directive. This means your design tokens live alongside your other CSS — no context switching.

/* v3 approach: tailwind.config.js */
/* module.exports = {
  theme: {
    extend: {
      colors: {
        brand: { 50: '#f0f9ff', 500: '#0ea5e9', 900: '#0c4a6e' },
      },
      fontFamily: {
        display: ['Inter', 'sans-serif'],
      },
      borderRadius: {
        '4xl': '2rem',
      },
    },
  },
} */

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

@theme {
  --color-brand-50: #f0f9ff;
  --color-brand-100: #e0f2fe;
  --color-brand-500: #0ea5e9;
  --color-brand-900: #0c4a6e;

  --font-display: "Inter", sans-serif;

  --radius-4xl: 2rem;

  --spacing-18: 4.5rem;
  --spacing-112: 28rem;
}
Enter fullscreen mode Exit fullscreen mode

The @import Directive Replaces Directives

/* v3 globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

/* v4 globals.css — single import */
@import "tailwindcss";

/* Source detection is automatic in v4 — no content[] array needed */
/* But you can still customize: */
@source "./src/**/*.{ts,tsx,mdx}";
@source not "./src/**/*.test.{ts,tsx}";
Enter fullscreen mode Exit fullscreen mode

New Color System — OKLCH by Default

Tailwind v4 switched to OKLCH (perceptually uniform color space) for all default colors. Colors look better across screens, and the scale is more consistent. But if you were relying on exact v3 hex values, your colors will shift slightly.

/* v4 uses OKLCH natively */
@theme {
  /* Define colors in oklch for perceptual consistency */
  --color-primary: oklch(55% 0.2 250);
  --color-primary-light: oklch(75% 0.15 250);
  --color-primary-dark: oklch(35% 0.25 250);

  /* You can still use hex if migrating from v3 */
  --color-legacy-brand: #0ea5e9;
}

/* Usage is identical to v3 */
/* bg-primary, text-primary-light, border-primary-dark */
Enter fullscreen mode Exit fullscreen mode
/* Migrating v3 custom colors to v4 */

/* v3 */
/* colors: { primary: { DEFAULT: '#0ea5e9', dark: '#0284c7' } } */

/* v4 equivalent in @theme */
@theme {
  --color-primary: #0ea5e9;      /* becomes bg-primary, text-primary */
  --color-primary-dark: #0284c7; /* becomes bg-primary-dark */
}
Enter fullscreen mode Exit fullscreen mode

Container Queries — Built In, No Plugin

Container queries were a plugin in v3. In v4 they are first-class utilities.














Enter fullscreen mode Exit fullscreen mode
/* Custom container breakpoints in @theme */
@theme {
  --container-3xs: 16rem;
  --container-2xs: 18rem;
  --container-xs: 20rem;
  /* sm, md, lg, xl, 2xl are built-in */
}
Enter fullscreen mode Exit fullscreen mode

@starting-style — Animate Elements on Enter

One of the most requested CSS features: animate an element when it first appears in the DOM. v4 exposes this via the starting variant.


  Modal content that fades/slides in

  Popover with enter animation

Enter fullscreen mode Exit fullscreen mode

The not-* Variant


  List item with divider except last



Enter fullscreen mode Exit fullscreen mode

New Gradient Utilities


  Vivid gradient without muddy center

  Color wheel

  Spotlight effect

  Off-center radial

Enter fullscreen mode Exit fullscreen mode

Migrating Plugins and Custom Utilities

/* v3 plugin (JavaScript): */
/* plugin(({ addUtilities }) => {
  addUtilities({
    '.text-balance': { textWrap: 'balance' },
    '.text-pretty': { textWrap: 'pretty' },
  })
}) */

/* v4 equivalent — just write CSS: */
@layer utilities {
  .text-balance {
    text-wrap: balance;
  }
  .text-pretty {
    text-wrap: pretty;
  }
}

/* Custom components */
@layer components {
  .btn-primary {
    @apply inline-flex items-center justify-center px-4 py-2
           rounded-lg bg-brand-500 text-white font-medium
           hover:bg-brand-600 focus-visible:outline-2
           focus-visible:outline-offset-2 focus-visible:outline-brand-500
           disabled:opacity-50 disabled:cursor-not-allowed
           transition-colors duration-150;
  }
}
Enter fullscreen mode Exit fullscreen mode

v3 to v4 Class Name Changes

# Run the official codemod first — handles ~80% of renames
npx @tailwindcss/upgrade@next

# Manual fixes after codemod:
# shadow-sm      → shadow-xs
# shadow         → shadow-sm
# rounded        → rounded-sm
# outline-none   → outline-hidden (preserves a11y ring)
# blur           → blur-sm
# ring           → ring-3 (ring width changed from 3px to 1px default)
# decoration-*   → underline-* (text-decoration shorthand)
Enter fullscreen mode Exit fullscreen mode
/* Before: v3 arbitrary values */
/* class="p-[14px] text-[15px] leading-[1.6]" */

/* After: still valid in v4, but theme values preferred */
@theme {
  --spacing-3-5: 0.875rem;  /* 14px */
  --text-body: 0.9375rem;   /* 15px */
  --leading-reading: 1.6;
}
/* class="p-3-5 text-body leading-reading" */
Enter fullscreen mode Exit fullscreen mode

Dark Mode: class Strategy Still Works

/* v4 dark mode configuration */
@import "tailwindcss";

/* Class-based dark mode (recommended) */
@variant dark (&:is(.dark *));

/* Or media-query based */
/* @variant dark (@media (prefers-color-scheme: dark)); */

/* Custom variant: system with manual override */
@variant dark {
  &:is([data-theme="dark"] *),
  @media (prefers-color-scheme: dark) {
    &:not([data-theme="light"] *)
  }
}
Enter fullscreen mode Exit fullscreen mode

Full Migration Checklist

# 1. Update package
npm install tailwindcss@^4.0

# 2. Run official codemod
npx @tailwindcss/upgrade@next

# 3. Remove tailwind.config.js (after migrating theme to @theme)

# 4. Update globals.css:
#    - Replace @tailwind directives with @import "tailwindcss"
#    - Move theme.extend into @theme block
#    - Move plugins into @layer utilities/@layer components

# 5. Fix renamed classes (shadow, rounded, ring, outline-none)

# 6. Test dark mode — verify @variant dark config

# 7. Remove @tailwindcss/container-queries if installed
#    (it conflicts with v4 built-in)

# 8. Check PostCSS config — v4 uses its own bundler;
#    PostCSS is optional and only needed for other plugins

# 9. Run production build and check for purge issues
#    (content scanning changed — @source if needed)
Enter fullscreen mode Exit fullscreen mode

People Also Ask

Is Tailwind CSS v4 backward compatible with v3?

No, v4 has multiple breaking changes. The official upgrade tool (npx @tailwindcss/upgrade@next) handles about 80% of the migration automatically, but you will need to manually migrate your tailwind.config.js theme extensions to the @theme directive and fix renamed utility classes (shadow, rounded, ring). Budget 1-2 hours for a medium-sized project. The new CSS-first approach is significantly cleaner once you have migrated.

Do I still need PostCSS with Tailwind v4?

Not for Tailwind itself. v4 ships its own Rust-based engine and Vite plugin that do not require PostCSS. You only need PostCSS if you are using other PostCSS plugins (Autoprefixer, cssnano, etc.). If you are using only Tailwind, remove your postcss.config.js and use @tailwindcss/vite (for Vite/Next.js) or the standalone CLI instead.

What happened to the JIT engine in Tailwind v4?

The JIT engine from v3 is the default and only mode in v4 — there is no "classic" mode. v4 takes this further by using an even faster Rust-based scanner and bundler. Content detection is now automatic (no content: [] array required) — Tailwind scans your project files automatically. Use the @source directive only when you need to add non-standard file paths or explicitly exclude directories like test files.

Originally published at wowhow.cloud

Top comments (0)