Tailwind CSS 4 Migration: What Changed & How to Upgrade
DesignRevision Editorial
· SaaS, frontend & developer tooling
Tailwind CSS 4.0 is the biggest update in the framework's history. It replaces the JavaScript-based build system with a Rust-powered engine, moves configuration from JavaScript files to native CSS, and delivers build times up to 5x faster than v3.
If you have been writing Tailwind for years, the upgrade changes how you think about configuration. If you are starting fresh, tailwind 4 is simply a better starting point. This guide covers every major change, the complete migration path from v3, and the framework-specific setup for Next.js and Vite projects.
Key Takeaways
If you remember nothing else:
- Tailwind 4 replaces
tailwind.config.jswith CSS-first configuration using the@themedirective and CSS custom properties- The new Oxide engine (built in Rust) delivers 2-5x faster builds: large projects drop from 600ms to ~120ms
- The entry point changes from
@tailwind base/components/utilitiesto@import "tailwindcss"- Content detection is automatic in tailwind 4. No more manual
content: [...]arrays in config- New features include native container queries, 3D transforms,
color-mix()support, and CSS cascade layers- The official upgrade tool (
npx @tailwindcss/upgrade) automates most migration steps- PostCSS setup changes: replace
tailwindcssplugin with@tailwindcss/postcss, or use the new@tailwindcss/viteplugin
Table of Contents
- Why Tailwind 4 Is a Major Rewrite
- The Oxide Engine: Performance Gains
- CSS-First Configuration: Goodbye tailwind.config.js
- New Features in Tailwind 4
- Breaking Changes: What Moved, Changed, or Disappeared
- Step-by-Step Migration Guide
- Framework-Specific Setup
- Plugin Compatibility
- Before and After: Code Comparisons
- Common Migration Errors and Fixes
- Should You Upgrade Now?
- Conclusion
Why Tailwind 4 Is a Major Rewrite
Tailwind CSS v3 was built on a JavaScript engine that processed your configuration file, scanned your templates, and generated CSS. It worked, but the architecture had limits. Build times scaled poorly on large projects. Configuration lived in JavaScript, separate from the CSS it produced. And the content scanning system required manual file path configuration that broke in monorepos.
Tailwind 4 rewrites the core from scratch. The JavaScript engine is replaced by Oxide, a Rust-based compiler that processes CSS natively. Configuration moves from tailwind.config.js into your CSS files using standard CSS syntax. Content detection becomes automatic, scanning your project without manual path configuration.
The result is a framework that is faster, simpler to configure, and more aligned with how CSS actually works. Tailwind css 4 is not a minor version bump with new utilities. It is a foundational change in how the tool operates.
The Oxide Engine: Performance Gains
The headline improvement in tailwind 4 is raw speed. The Oxide engine replaces the JavaScript-based build pipeline with a Rust compiler that processes CSS directly.
Build Time Benchmarks
| Metric | Tailwind v3 | Tailwind v4 | Improvement |
|---|---|---|---|
| Full build (large project) | ~600ms | ~120ms | ~5x faster |
| Incremental rebuild | ~100ms | ~15ms | ~6x faster |
| HMR update | Noticeable delay | Near-instant | Significant |
| Cold start | Moderate | Fast | ~3x faster |
For small projects with a few hundred utility classes, the difference is marginal. For production applications with thousands of classes across hundreds of files, the improvement is transformative. HMR updates that previously caused a visible flash now feel instant.
Why Rust Matters
The performance gain comes from three architectural changes:
- Native CSS parsing. Oxide reads and generates CSS without converting to and from JavaScript ASTs. This eliminates the serialization overhead that slowed v3
- Parallel processing. Rust enables multi-threaded scanning of source files. Large codebases benefit from parallel content detection
- Zero-runtime JavaScript. The build process produces pure CSS without any runtime JavaScript. No more PostCSS plugin chain for Tailwind processing
For teams using v4 alongside other build tools, the reduced build footprint means faster CI/CD pipelines and quicker development server startup.
CSS-First Configuration: Goodbye tailwind.config.js
The most significant workflow change in tailwind v4 is the move from JavaScript configuration to CSS-first configuration. Instead of defining your design system in tailwind.config.js, you define it directly in your CSS using the @theme directive.
The @theme Directive
The @theme directive replaces the theme and extend sections of your JavaScript config. Every value you define becomes a CSS custom property, making your design tokens available throughout your stylesheets.
Tailwind v3 configuration:
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
primary: '#3B82F6',
secondary: '#10B981',
},
fontFamily: {
sans: ['Inter', 'sans-serif'],
mono: ['JetBrains Mono', 'monospace'],
},
spacing: {
'18': '4.5rem',
'112': '28rem',
},
},
},
}
Tailwind 4 configuration:
@import "tailwindcss";
@theme {
--color-primary: #3B82F6;
--color-secondary: #10B981;
--font-family-sans: "Inter", sans-serif;
--font-family-mono: "JetBrains Mono", monospace;
--spacing-18: 4.5rem;
--spacing-112: 28rem;
}
The CSS approach has concrete advantages beyond simplicity. Your design tokens are native CSS custom properties, accessible from any stylesheet without Tailwind-specific tooling. IDE autocompletion works out of the box. And there is no JavaScript build step between your configuration and your CSS output.
The @import Entry Point
The entry point for tailwind css 4 changes from three directives to a single import:
v3:
@tailwind base;
@tailwind components;
@tailwind utilities;
v4:
@import "tailwindcss";
This single import handles base styles, component layer, and utilities. The framework uses native CSS cascade layers internally to maintain the correct specificity order.
Automatic Content Detection
One of the most frustrating aspects of Tailwind v3 was the content array. You had to manually specify every file path that contained Tailwind classes. Miss a path and your classes got purged. Monorepos required complex glob patterns.
Version 4 eliminates the content array entirely. The Oxide engine automatically detects which files contain Tailwind classes by scanning your project. No configuration needed. No broken builds because you forgot to add a new directory.
If you need to explicitly include or exclude paths, you can use the @source directive:
@import "tailwindcss";
@source "../shared-components/**/*.tsx";
But for most projects, automatic detection handles everything. If you have been using our Tailwind cheat sheet as a reference, the utility class names remain the same. The change is only in how the build tool finds them.
New Features in Tailwind 4
Beyond the architectural rewrite, tailwind 4.0 introduces utilities and capabilities that were previously impossible or required plugins.
Container Queries
Native container query support lets you style elements based on their parent container's size rather than the viewport. This has been the most requested feature for responsive component design.
<div class="@container">
<div class="grid @sm:grid-cols-1 @lg:grid-cols-2 @xl:grid-cols-3">
<div class="p-4">Responsive to container, not viewport</div>
</div>
</div>
Container queries make components truly portable. A card component adapts its layout based on where it is placed, whether that is a narrow sidebar or a wide main content area.
3D Transforms
New transform utilities support 3D space without custom CSS:
<div class="perspective-[1000px]">
<div class="rotate-x-12 rotate-y-6 translate-z-8 transform-3d">
3D transformed element
</div>
</div>
Utilities include rotate-x-*, rotate-y-*, translate-z-*, perspective-*, and transform-style-3d for building card flips, parallax effects, and interactive 3D interfaces.
color-mix() Support
The color-mix() function enables dynamic color blending directly in utility classes:
<div class="bg-[color-mix(in_srgb,var(--color-primary)_70%,white)]">
70% primary, 30% white
</div>
This is particularly useful for generating hover states, disabled states, and color variations without defining every shade manually.
CSS Cascade Layers
The framework uses native CSS @layer for organizing styles in v4. The framework creates three layers: base, components, and utilities. Your custom styles integrate into this system naturally:
@import "tailwindcss";
@layer components {
.btn {
@apply px-6 py-3 rounded-lg font-medium;
}
.card {
@apply bg-white rounded-xl shadow-sm border border-gray-200;
}
}
Cascade layers guarantee that utilities always override component styles regardless of source order, eliminating a class of specificity bugs that plagued v3 projects.
Native CSS Variables Everywhere
Every theme value in v4 is a CSS custom property. This means you can access your design tokens from any CSS context, not just through utility classes:
.custom-element {
background: var(--color-primary);
font-family: var(--font-family-sans);
padding: var(--spacing-4);
}
This bridges the gap between utility-first and traditional CSS approaches. You get the design system consistency of Tailwind with the flexibility of custom CSS when you need it.
Improved Gradient APIs
Gradient utilities are more flexible with support for gradient angles, multiple color stops with positions, and interpolation methods:
<div class="bg-linear-to-r from-blue-500 from-20% via-purple-500 via-60% to-pink-500">
Gradient with explicit stop positions
</div>
Breaking Changes: What Moved, Changed, or Disappeared
Every major version introduces breaking changes. This release changes more than most because of the architectural shift. Here is what you need to know before migrating.
Configuration Changes
| What Changed | v3 | v4 |
|---|---|---|
| Config file | tailwind.config.js (required) |
@theme in CSS (recommended) |
| Entry point | @tailwind base/components/utilities |
@import "tailwindcss" |
| Content paths | content: ['./src/**/*.tsx'] |
Automatic detection |
| PostCSS plugin | tailwindcss |
@tailwindcss/postcss |
| Vite integration | PostCSS plugin | @tailwindcss/vite plugin |
| CLI | npx tailwindcss |
npx @tailwindcss/cli |
Renamed and Updated Utilities
| v3 Utility | v4 Equivalent | Notes |
|---|---|---|
bg-gradient-to-r |
bg-linear-to-r |
Aligns with CSS linear-gradient naming |
bg-opacity-* |
bg-{color}/{opacity} |
Uses slash syntax: bg-blue-500/75 |
text-opacity-* |
text-{color}/{opacity} |
Same pattern as background |
decoration-slice |
box-decoration-slice |
Full CSS property name |
decoration-clone |
box-decoration-clone |
Full CSS property name |
flex-shrink-* |
shrink-* |
Simplified (already in late v3) |
flex-grow-* |
grow-* |
Simplified (already in late v3) |
overflow-ellipsis |
text-ellipsis |
Renamed for clarity |
Default Changes
- Default border color changed from
gray-200tocurrentColor, matching the CSS specification default - Default ring width changed from
3pxto1px - Color scale uses updated values. If your design depends on exact hex values from v3 defaults, verify them after migration
- Dark mode defaults to
mediastrategy. Switch toclassstrategy via CSS if you use manual dark mode toggling
What Was Removed
- The standalone
tailwindcssPostCSS plugin. Use@tailwindcss/postcssinstead - The
tailwindcss initcommand. Configuration lives in CSS now - The
contentconfiguration array. Replaced by automatic detection and@source - The
safelistconfiguration. Use@sourcefor explicit inclusion - JavaScript-based plugin registration via
plugins: [require('...')]. Plugins use CSS imports or the@plugindirective
Step-by-Step Migration Guide
Here is the exact process to migrate to tailwind 4 from v3. The official upgrade tool handles most of this automatically, but understanding each step helps when you need to debug issues.
Step 1: Run the Upgrade Tool
The fastest path is the automated upgrade:
npx @tailwindcss/upgrade@latest
This tool scans your project and automatically:
- Converts
@tailwinddirectives to@import "tailwindcss" - Migrates
tailwind.config.jsto@themein your CSS - Updates PostCSS configuration
- Renames deprecated utilities in your templates
- Adjusts import paths
For most projects, the upgrade tool handles 80% of the migration. Run it first, then fix remaining issues manually.
Step 2: Update Dependencies
# Remove old packages
npm uninstall tailwindcss postcss-import
# Install Tailwind 4
npm install tailwindcss@latest
# Install the integration for your build tool
npm install -D @tailwindcss/postcss # For PostCSS users
# OR
npm install -D @tailwindcss/vite # For Vite users
# OR
npm install -D @tailwindcss/cli # For CLI-only builds
Step 3: Update Your CSS Entry Point
Replace the old directives with the new import:
/* Before (v3) */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* After (v4) */
@import "tailwindcss";
Add your theme configuration below the import:
@import "tailwindcss";
@theme {
--color-primary: #3B82F6;
--color-secondary: #10B981;
--font-family-sans: "Inter", sans-serif;
}
Step 4: Update Build Configuration
For PostCSS users, update postcss.config.js:
// Before (v3)
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
// After (v4)
module.exports = {
plugins: {
'@tailwindcss/postcss': {},
},
}
Note: autoprefixer is no longer needed. Tailwind css 4 handles vendor prefixing automatically.
For Vite users, update vite.config.js:
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})
No PostCSS configuration is needed when using the Vite plugin.
Step 5: Migrate Configuration
Convert your tailwind.config.js theme values to @theme:
@import "tailwindcss";
@theme {
/* Colors */
--color-brand: #6366F1;
--color-brand-light: #818CF8;
--color-brand-dark: #4F46E5;
/* Typography */
--font-family-heading: "Cal Sans", sans-serif;
--font-family-body: "Inter", sans-serif;
/* Custom spacing */
--spacing-128: 32rem;
--spacing-144: 36rem;
/* Border radius */
--radius-lg: 0.75rem;
--radius-xl: 1rem;
/* Breakpoints */
--breakpoint-3xl: 1920px;
}
If you still need JavaScript configuration (for complex plugins or dynamic values), use the @config directive:
@import "tailwindcss";
@config "../tailwind.config.js";
This backward compatibility bridge lets you migrate incrementally.
Step 6: Delete Old Configuration Files
Once everything works with CSS-first configuration:
rm tailwind.config.js
Also remove any postcss-import references and old Tailwind-specific PostCSS plugins from your project.
Step 7: Test and Fix
Run your build and check for:
- Missing styles (classes that got purged incorrectly)
- Color differences (default palette values changed)
- Dark mode behavior (verify your strategy is applied correctly)
- Plugin output (ensure third-party plugins render correctly)
The migration is complete when your build passes and your UI matches its pre-migration appearance. For projects using Tailwind templates, verify that template-specific customizations survive the migration.
Framework-Specific Setup
Next.js Setup
For Next.js projects with Tailwind, the setup depends on whether you use the Pages Router or App Router.
App Router (recommended):
Install the PostCSS plugin:
npm install -D @tailwindcss/postcss
Update postcss.config.mjs:
const config = {
plugins: {
"@tailwindcss/postcss": {},
},
};
export default config;
Update your global CSS file (app/globals.css):
@import "tailwindcss";
@theme {
--color-primary: #3B82F6;
--font-family-sans: "Inter", sans-serif;
}
Import it in app/layout.tsx:
import './globals.css'
No tailwind.config.js or tailwind.config.ts needed. Delete them after migration.
Vite + React Setup
npm install -D @tailwindcss/vite
Update vite.config.ts:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [react(), tailwindcss()],
})
Update src/index.css:
@import "tailwindcss";
Import in your root component. No PostCSS configuration needed.
CLI-Only Setup
For projects without a bundler:
npm install -D @tailwindcss/cli
Build your CSS:
npx @tailwindcss/cli -i src/input.css -o dist/output.css --watch
The input CSS file uses the same @import "tailwindcss" syntax.
Plugin Compatibility
Plugin compatibility is the primary concern for teams considering the migrate to tailwind 4 process. Here is the status of major plugins.
Fully Compatible
| Plugin | Status | Notes |
|---|---|---|
| Headless UI | Works | No changes needed. Headless components use className props |
| Radix UI | Works | Headless library, applies Tailwind classes normally |
| shadcn/ui | Works | Updated for v4 CSS variables approach |
| Tailwind Typography | Works | Updated for v4 as @tailwindcss/typography |
| Tailwind Forms | Works | Updated for v4 as @tailwindcss/forms |
Requires Updates
| Plugin | Status | Notes |
|---|---|---|
| DaisyUI | Updated | Check DaisyUI docs for v4-compatible version |
| Flowbite | Updated | Requires latest version for v4 support |
| Custom JS plugins | Migrate | Use @plugin directive or @config bridge |
Using Legacy Plugins
If a plugin has not been updated for tailwind v4, use the @config bridge:
@import "tailwindcss";
@config "../tailwind.config.js";
Then keep the plugin registration in your JavaScript config. This runs the legacy plugin system alongside the new engine. It is a bridge, not a permanent solution. Plan to remove it once the plugin ships a v4-compatible version.
Before and After: Code Comparisons
Seeing the differences side by side makes the v4 changes concrete.
Project Configuration
v3 project structure:
tailwind.config.js # JavaScript config
postcss.config.js # PostCSS with tailwindcss plugin
src/styles/globals.css # @tailwind directives
v4 project structure:
postcss.config.js # PostCSS with @tailwindcss/postcss (optional)
src/styles/globals.css # @import + @theme
Custom Colors
v3:
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
brand: {
50: '#eef2ff',
500: '#6366f1',
900: '#312e81',
}
}
}
}
}
v4:
@theme {
--color-brand-50: #eef2ff;
--color-brand-500: #6366f1;
--color-brand-900: #312e81;
}
Usage remains identical in both versions: bg-brand-500, text-brand-50, etc.
Custom Components
v3:
@layer components {
.btn-primary {
@apply px-6 py-3 bg-blue-500 text-white rounded-lg
hover:bg-blue-600 transition-colors;
}
}
v4:
@layer components {
.btn-primary {
@apply px-6 py-3 bg-blue-500 text-white rounded-lg
hover:bg-blue-600 transition-colors;
}
}
@apply works the same way inside @layer blocks. The difference is that v4 uses native CSS cascade layers, giving you guaranteed specificity ordering.
Dark Mode
v3 (with config):
// tailwind.config.js
module.exports = {
darkMode: 'class',
}
v4 (with CSS):
@import "tailwindcss";
@variant dark (&:where(.dark, .dark *));
Usage in HTML remains the same: dark:bg-gray-900, dark:text-white.
Common Migration Errors and Fixes
These are the issues developers hit most frequently when upgrading to v4.
"It looks like you're trying to use tailwindcss directly as a PostCSS plugin"
Cause: Your postcss.config.js still references tailwindcss instead of @tailwindcss/postcss.
Fix:
// Replace this:
plugins: { tailwindcss: {} }
// With this:
plugins: { '@tailwindcss/postcss': {} }
Missing default styles or broken dark mode
Cause: Incomplete config migration. Default values changed between v3 and v4.
Fix: Explicitly define any v3 defaults you relied on in your @theme block. For dark mode, add the @variant dark directive if you use the class strategy.
Utility classes being purged unexpectedly
Cause: Automatic content detection is missing some files. This can happen with files outside the project root or dynamically generated class names.
Fix: Add explicit source paths:
@source "../packages/shared-ui/**/*.tsx";
@source "./data/**/*.json";
Plugin errors after upgrade
Cause: JavaScript plugins reference v3 API methods that no longer exist.
Fix: Add the @config bridge temporarily:
@import "tailwindcss";
@config "../tailwind.config.js";
Then update or replace the plugin when a v4-compatible version ships.
Border colors changed
Cause: Default border color changed from gray-200 to currentColor in v4.
Fix: Add explicit border colors where you previously relied on the default:
<!-- Add explicit color where borders look wrong -->
<div class="border border-gray-200">...</div>
Or set the default globally:
@layer base {
*,
::after,
::before {
border-color: var(--color-gray-200);
}
}
Should You Upgrade Now?
The decision to migrate to tailwind 4 depends on your project's circumstances.
Upgrade now if:
- You are starting a new project. There is zero reason to start with v3 in 2026. Version 4 is the default
- Build performance matters. If your dev server or CI pipeline is slow because of Tailwind processing, the 2-5x speedup is worth the migration effort
- You want CSS-first configuration. The
@themeapproach is simpler, more portable, and eliminates the JavaScript config dependency - You need container queries or 3D transforms. These features are only available in v4
- Your project uses standard plugins. If you rely on Headless UI, Radix, shadcn/ui, or official Tailwind plugins, compatibility is confirmed
Wait if:
- Critical plugins have not been updated. If your project depends on niche plugins without v4 support, wait for those updates
- You are mid-sprint on a deadline. Migration is low-risk but not zero-risk. Do it during a dedicated maintenance window
- Your project is in maintenance mode. If you are not actively developing the project, the migration effort has no payback
For most teams, the answer is to upgrade. The tailwind 4 changes deliver immediate performance benefits, the migration tool handles the heavy lifting, and the new CSS-first workflow is genuinely better for day-to-day development.
Ship apps faster with AI
Generate production-ready Next.js apps from a prompt. Full code ownership, deploy anywhere, stunning design output.
Conclusion
Tailwind CSS 4.0 is a generational upgrade. The Oxide engine makes builds measurably faster. CSS-first configuration eliminates the JavaScript config file and makes your design tokens native CSS custom properties. Automatic content detection removes an entire class of configuration bugs. And new features like container queries and 3D transforms expand what you can build with utility classes alone.
The migration path from v3 is well-supported. Run npx @tailwindcss/upgrade, update your build configuration, convert your theme to @theme, and test. Most projects complete the migration in 1 to 2 hours. Complex projects with custom plugins may take a day.
Start with the upgrade tool. Fix any build errors. Verify your UI visually. Then delete your tailwind.config.js and enjoy the faster, cleaner workflow that tailwind 4 delivers.
Related Resources
Frequently Asked Questions
-
Yes. Tailwind 4 reached stable production-ready status in 2025 with official Vite and PostCSS plugin support. The Oxide engine has been battle-tested across thousands of production sites, and the v4.1 patch releases have addressed the remaining edge cases from early adoption. New projects should start with Tailwind 4 by default. Existing projects can migrate confidently using the official upgrade tool.
-
Yes. Tailwind 4 works with Next.js including the App Router. You can integrate it through the @tailwindcss/postcss plugin or the @tailwindcss/vite plugin depending on your Next.js setup. Replace the old PostCSS configuration, switch your CSS entry point to use @import "tailwindcss", and migrate your tailwind.config.js to CSS-based @theme configuration. The Next.js community has reported no blockers with Tailwind 4 integration.
-
Tailwind 4 supports incremental migration. The official upgrade tool handles most conversions automatically, and you can migrate files progressively. For large codebases, a staged rollout works well. Start by running the upgrade tool, fix any build errors, then gradually convert custom configurations and plugins to the new CSS-first approach. The tailwind.config.js file is still supported through the @config directive for backward compatibility during migration.
-
Tailwind 4 replaces tailwind.config.js with CSS-first configuration using the @theme directive. Instead of defining colors, fonts, and spacing in JavaScript, you define them as CSS custom properties directly in your stylesheet. The JavaScript config file still works through the @config directive for backward compatibility, but the recommended approach is full CSS-based configuration. This eliminates a build dependency and makes your design tokens available as native CSS variables.
-
Significantly faster. The new Oxide engine built in Rust delivers 2 to 5 times faster build times compared to Tailwind 3. Large projects that took 600 milliseconds to build in v3 complete in roughly 120 milliseconds with Tailwind 4. Hot module replacement updates are near-instant. The performance improvement is most noticeable on large codebases with thousands of utility classes.
-
Most popular plugins have been updated for Tailwind 4 compatibility. Headless UI and Radix work without changes since they are headless component libraries. DaisyUI and other theming plugins required updates to work with the new CSS-based configuration. Check your plugin repositories for v4 compatibility before upgrading. Plugins that relied heavily on the JavaScript config API may need the @config directive as a bridge during transition.
-
The three biggest breaking changes are the shift from JavaScript configuration to CSS-first @theme configuration, the replacement of @tailwind directives with @import "tailwindcss", and the removal of the tailwindcss PostCSS plugin in favor of @tailwindcss/postcss. Other changes include automatic content detection replacing the manual content array, renamed default color scale values, and updated dark mode configuration. The official upgrade tool handles most of these automatically.
-
Upgrade now for new projects without question. For existing projects, upgrade if you want faster builds, CSS-first configuration, and access to new features like container queries and 3D transforms. Wait only if your project depends heavily on plugins that have not been updated for v4 compatibility. The migration path is well documented, the upgrade tool automates most changes, and the community has validated the stability through widespread production adoption.
Next.js SaaS Starter Kit
Pre-built auth, billing, and dashboard. Launch your SaaS in days, not weeks.
Join 50k+ subscribers
Web dev, SaaS, growth & marketing. Weekly.
Keep Learning
More articles you might find interesting.