Tailwind CSS v4: Migration Guide and New Features

Complete Tailwind CSS v4 migration guide: new CSS-first config, P3 color gamut, container queries, CSS variables, performance improvements, and breaking changes.

E
ECOSIRE Research and Development Team
|March 19, 20268 min read1.8k Words|

Tailwind CSS v4: Migration Guide and New Features

Tailwind CSS v4 is the most significant rewrite since the framework launched. The JavaScript configuration file is gone — replaced by CSS-first configuration using custom properties. The class API is largely unchanged, but the internals are completely new: a Rust-based engine (oxide), first-class support for the P3 color gamut, native container queries, and a build pipeline that integrates directly with CSS rather than through PostCSS plugins.

This guide walks you through what changed, why it changed, and how to migrate a real production application from Tailwind v3 to v4. We'll cover the breaking changes that will trip you up and the new features worth adopting immediately.

Key Takeaways

  • tailwind.config.js is deprecated — configuration moves to CSS using @theme directive
  • Import changed from @tailwind base/components/utilities to a single @import "tailwindcss"
  • Custom colors now use CSS custom properties under --color-* namespace
  • The content configuration is automatic — Tailwind v4 scans files automatically via glob
  • Container queries are now built-in with @container and @sm:, @lg: variants
  • P3 color gamut support via oklch() color space for all built-in colors
  • @apply still works but is less necessary with CSS custom properties
  • The JIT engine is replaced by Oxide (Rust) — dramatically faster compilation

What Changed and Why

Tailwind v3 used PostCSS as a processing pipeline, a JavaScript config file, and generated utility classes via a JIT engine written in JavaScript. This worked well but had limitations:

  • Configuration required JavaScript, making it inaccessible from CSS-only tools
  • Custom properties had to be set separately from Tailwind config
  • Container queries required a plugin
  • Color system didn't support P3 gamut

Tailwind v4 addresses all of these by moving configuration into CSS itself, using the browser's native CSS custom property system, and rewriting the engine in Rust for 5-10x faster builds.


Installation

# Tailwind v4
pnpm add tailwindcss@^4.0.0 @tailwindcss/vite

# For Next.js
pnpm add tailwindcss@^4.0.0 @tailwindcss/postcss

The @tailwindcss/vite plugin integrates directly with Vite. For Next.js (which uses PostCSS internally), use @tailwindcss/postcss.


CSS-First Configuration

This is the biggest conceptual change. Your globals.css becomes the source of truth for Tailwind configuration:

/* Before: globals.css with v3 */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* After: globals.css with v4 */
@import "tailwindcss";

/* Theme configuration replaces tailwind.config.js */
@theme {
  /* Custom colors as CSS custom properties */
  --color-brand-50: oklch(97% 0.02 250);
  --color-brand-100: oklch(94% 0.04 250);
  --color-brand-500: oklch(62% 0.18 250);
  --color-brand-900: oklch(28% 0.09 250);

  /* Custom font families */
  --font-sans: 'Inter', system-ui, sans-serif;
  --font-mono: 'JetBrains Mono', monospace;

  /* Custom spacing */
  --spacing-18: 4.5rem;
  --spacing-88: 22rem;

  /* Custom breakpoints */
  --breakpoint-3xl: 1920px;

  /* Custom shadows */
  --shadow-card: 0 1px 3px oklch(0% 0 0 / 12%), 0 1px 2px oklch(0% 0 0 / 8%);
}

Your tailwind.config.js can be deleted (or kept for now — v4 supports both during migration).


Content Detection

Tailwind v4 automatically detects template files. The content configuration you meticulously maintained in v3 is gone:

// v3 tailwind.config.js — you needed this
module.exports = {
  content: [
    './src/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx}',
  ],
  // ...
}

In v4, Tailwind scans your project automatically using sensible defaults. If you have files in unusual locations, you can explicitly include them:

@import "tailwindcss";
@source "../scripts/*.ts"; /* Scan additional paths */
@source "../../packages/ui/src/**/*.tsx"; /* Monorepo workspace packages */

PostCSS Configuration Update

For Next.js projects (and any PostCSS-based setup), update your PostCSS config:

// Before: postcss.config.js with v3
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};
// After: postcss.config.mjs with v4
export default {
  plugins: {
    '@tailwindcss/postcss': {},
  },
};

autoprefixer is included in @tailwindcss/postcss by default. Remove the separate autoprefixer dependency.


Color System: OKLCH

Tailwind v4 uses OKLCH for its built-in color palette. OKLCH is a perceptually uniform color space — adjusting lightness actually looks like equal steps to the human eye, unlike HSL.

/* v4 colors use OKLCH internally */
/* bg-blue-500 generates: */
background-color: oklch(62.8% 0.258 264.1);

/* Custom colors with OKLCH */
@theme {
  --color-ecosire-amber: oklch(78% 0.18 80);
  --color-ecosire-navy: oklch(22% 0.04 250);
}

For wider gamut displays (most modern phones and Macs), OKLCH enables colors that sRGB simply cannot represent. The oklch() values are automatically converted to rgb() for displays that don't support P3.


Container Queries — Built-in

Container queries are now first-class in v4. No plugin needed:

/* Define a container */
.card-wrapper {
  container-type: inline-size;
  container-name: card;
}
<!-- Responsive based on parent container, not viewport -->
<div class="card-wrapper">
  <div class="@sm:flex-row @lg:grid-cols-3 flex flex-col">
    <!-- Layout changes based on container width, not screen width -->
  </div>
</div>

This enables component-level responsiveness — a sidebar card and a full-width card can have different layouts without separate CSS.

Named containers let you target specific ancestors:

<div class="card-wrapper [container-name:card]">
  <div class="@card/sm:text-lg text-base">
    Responsive to the 'card' container
  </div>
</div>

CSS Custom Properties Integration

Custom properties defined in @theme are accessible everywhere — CSS, JavaScript, and design tools that read CSS variables:

@theme {
  --color-primary: oklch(62% 0.18 250);
  --spacing-section: 5rem;
}
// Access in JavaScript/TypeScript
const style = getComputedStyle(document.documentElement);
const primary = style.getPropertyValue('--color-primary');
<!-- Use in inline styles -->
<div style={{ color: 'var(--color-primary)' }}>
  Themed content
</div>

This replaces the pattern of maintaining parallel values in tailwind.config.js and a tokens.ts file — there's now a single source of truth in CSS.


Breaking Changes

1. Plugin API changes

v3 plugins using addBase, addComponents, addUtilities are incompatible with v4. Rewrite them as CSS using the new layer system:

/* v3 plugin equivalent in v4 CSS */
@layer base {
  h1 {
    @apply text-3xl font-bold;
  }
}

@layer components {
  .btn {
    @apply px-4 py-2 rounded-lg font-medium;
  }
}

2. @apply with custom classes

@apply with non-Tailwind classes is more restricted in v4. Use @apply only with actual utility classes:

/* This works in v4 */
.btn-primary {
  @apply bg-blue-500 text-white px-4 py-2 rounded-lg;
}

/* This is deprecated — use regular CSS instead */
.btn-primary {
  @apply custom-shadow; /* custom-shadow is not a Tailwind utility */
}

3. tailwind.config.js theme.extend is gone

Configuration in JS is no longer the primary path. Use @theme in CSS. If you have both, the CSS configuration takes precedence.

4. Default color palette naming

A few color names changed in v4. Run the official codemod to catch them:

npx @tailwindcss/upgrade@next --config tailwind.config.js

Migration Strategy

For a large production application, migrate incrementally:

Phase 1: Update installation

pnpm remove tailwindcss autoprefixer postcss
pnpm add tailwindcss@^4.0.0 @tailwindcss/postcss

Update postcss.config.mjs:

export default {
  plugins: { '@tailwindcss/postcss': {} },
};

Update globals.css:

/* Replace the three @tailwind directives */
@import "tailwindcss";

Phase 2: Run the codemod

npx @tailwindcss/upgrade@next

This automates most of the migration: updates color names, converts config to CSS, and flags issues that need manual attention.

Phase 3: Move config to CSS

Take your tailwind.config.js theme values and move them to @theme in your CSS. Delete the config file after verification.

Phase 4: Adopt new features

Replace viewport-based responsive breakpoints with container queries where appropriate. Adopt OKLCH for custom colors to gain P3 gamut support.


Performance Improvements

Tailwind v4's Oxide engine (Rust) delivers measurably faster builds:

v3 (JavaScript JIT)v4 (Oxide, Rust)
Cold build2.1s0.3s
Incremental build120ms12ms
100k class scan4.2s0.4s

For large applications, this translates to noticeably faster HMR in development and significantly shorter CI build times.


Real-World Migration Example

Here's a practical before/after for a design system's base configuration:

// Before: tailwind.config.js (v3)
module.exports = {
  theme: {
    extend: {
      colors: {
        brand: {
          50: '#fef3c7',
          500: '#f59e0b',
          900: '#78350f',
        },
      },
      fontFamily: {
        sans: ['Inter', 'system-ui'],
      },
      boxShadow: {
        card: '0 1px 3px rgba(0,0,0,0.12)',
      },
    },
  },
  plugins: [
    require('@tailwindcss/typography'),
    require('@tailwindcss/forms'),
  ],
};
/* After: globals.css (v4) */
@import "tailwindcss";
@plugin "@tailwindcss/typography";
@plugin "@tailwindcss/forms";

@theme {
  --color-brand-50: oklch(97% 0.04 85);
  --color-brand-500: oklch(78% 0.18 85);
  --color-brand-900: oklch(33% 0.08 85);

  --font-sans: 'Inter', system-ui, sans-serif;

  --shadow-card: 0 1px 3px oklch(0% 0 0 / 12%);
}

The CSS-native configuration is more concise and eliminates the node_modules dependency for reading the config.


Frequently Asked Questions

Do I need to delete tailwind.config.js immediately?

No — Tailwind v4 supports both JS config and CSS config simultaneously during migration. The JS config still works, but features like @theme in CSS are only available without it. Migrate incrementally: first get the app building with v4, then gradually move configuration to CSS, then delete the JS config when you're confident.

Will my existing Tailwind v3 classes still work in v4?

The vast majority of utility classes are unchanged. Some color names were updated (gray → neutral in some contexts), and a few deprecated utilities were removed. Run npx @tailwindcss/upgrade@next to catch most issues automatically. The main incompatibilities are in plugins and custom theme configuration, not in how you use classes in HTML/JSX.

Is OKLCH supported in all browsers?

OKLCH is supported in all modern browsers (Chrome 111+, Firefox 113+, Safari 15.4+). Tailwind v4 generates fallbacks for older browsers where needed. For the P3 color gamut (the expanded colors), support requires both a wide-gamut display and a supporting browser — Tailwind generates rgb() fallbacks for narrower gamut displays.

How do I use Tailwind v4 with shadcn/ui?

shadcn/ui 2.0+ supports Tailwind v4. Update your components.json to use the v4 CSS variables format. The component library uses CSS custom properties for theming, which maps naturally to Tailwind v4's @theme system. Run npx shadcn@latest init in an existing project to get the v4-compatible configuration.

Are there TypeScript types for the new configuration?

Tailwind v4's configuration is CSS, so there are no TypeScript types for the config itself. IDE support comes from CSS language services that understand custom properties. For accessing theme values in TypeScript (for Charts, animations, etc.), use getComputedStyle to read CSS custom properties at runtime, or generate a tokens file from your CSS as part of your build.


Next Steps

Tailwind CSS v4 is a significant upgrade worth adopting for new projects and planning for existing ones. ECOSIRE's frontend stack runs Tailwind CSS v4.1 in production across a 249-page Next.js 16 application with a comprehensive design system.

Need help with a Tailwind v4 migration or want a design system built from the ground up? Explore our frontend engineering services to see how we can help.

E

Written by

ECOSIRE Research and Development Team

Building enterprise-grade digital products at ECOSIRE. Sharing insights on Odoo integrations, e-commerce automation, and AI-powered business solutions.

Chat on WhatsApp