Tailwind CSS v4 Migration Guide What Broke, What Changed, and How to Fix It
Author
Muhammad Awais
Published
May 20, 2026
Reading Time
12 min read
Views
18.6k

Tailwind CSS v4 is not an incremental update. It is a ground-up rewrite that changes the configuration model, the build pipeline, the dark mode system, and how design tokens work all at once. If you have a production Next.js app running Tailwind v3 and you run npm install tailwindcss@latest without reading this first, your build will break. This guide explains every breaking change, why the Tailwind team made these decisions, and the exact steps to migrate without losing a day to debugging.
Why Tailwind v4 Is a Different Animal
Tailwind v3 was built on PostCSS and required a JavaScript configuration file tailwind.config.js to define your design system. Colors, spacing, fonts, breakpoints, dark mode strategy, plugins, content paths everything lived in that one file. It worked, but it had a ceiling: the PostCSS pipeline was slow on large projects, the config file grew unwieldy, and customizing theme values required re-learning the extension API every time.
Tailwind v4 replaces the PostCSS-based engine with a new Rust-powered compiler called Oxide. Build times drop dramatically the Tailwind team reports full builds that took 3.5 seconds in v3 completing in under 100ms in v4 on the same codebase. Incremental builds are near-instant. But the speed gain comes with a trade-off: the entire configuration model changed to make the new engine possible.
The most disorienting change for v3 users is that tailwind.config.js is no longer the primary way to configure Tailwind. In v4, your CSS file is your configuration. Design tokens, theme values, plugins, and dark mode strategy are all defined inside your main CSS file using native CSS custom properties and new @theme directives. Your JavaScript config file becomes optional and for most projects, unnecessary.
Breaking Change #1 The Config File Is Gone (By Default)
In v3, every Tailwind project started with npx tailwindcss init to generate tailwind.config.js. In v4, that file is not generated by default and Tailwind does not look for it unless you explicitly import it. If you are migrating a v3 project, your existing config file will be silently ignored unless you add an explicit import directive in your CSS.
/* v3 globals.css — minimal, config lives in tailwind.config.js */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* v4 globals.css — config now lives here */
@import "tailwindcss";
@theme {
--color-brand: #6366f1;
--color-brand-dark: #4f46e5;
--font-sans: "Inter", sans-serif;
--radius-card: 0.75rem;
--spacing-section: 5rem;
}The @theme block is where you define every custom token. Tailwind v4 reads these CSS custom properties and automatically generates utility classes from them. Define --color-brand and you get bg-brand, text-brand, border-brand automatically. Define --spacing-section and you get py-section, mt-section, and every spacing utility derived from it. The token-to-utility mapping is automatic and zero-config.
This approach aligns perfectly with modern design token systems where CSS custom properties are the source of truth shared between your CSS, your JavaScript, and your design tools. The deeper patterns behind CSS variable-based design token architecture and how they power dark mode, theming, and component libraries without runtime overhead are covered in our guide on CSS variables and design tokens for dark mode systems in 2026.
Breaking Change #2 Dark Mode Strategy Changed
In v3, dark mode was configured in tailwind.config.js with the darkMode key. Setting it to "class" meant Tailwind generated dark variants that activated when a parent element had the dark class. Setting it to "media" used the prefers-color-scheme media query. Most production apps used "class" because it gave user-controlled toggle support.
In v4, dark mode defaults to the prefers-color-scheme media query. If you were using class-based dark mode in v3, your dark mode toggle will silently stop working after migration. The fix is a single line in your CSS, but you need to know it exists.
/* v4 globals.css — restore class-based dark mode */
@import "tailwindcss";
@variant dark (&:where(.dark, .dark *));
@theme {
/* your tokens here */
}The @variant directive in v4 lets you redefine how any variant selector works. By mapping dark to an ancestor class selector, you get the exact same behavior as v3's darkMode: "class" dark utilities activate when any ancestor has the dark class. The fact that this change is a one-liner does not make it less of a trap it is the most common reason Next.js apps lose their dark mode after a Tailwind v4 upgrade. The full pattern for preventing flash-of-incorrect-theme (FOUC) during dark mode initialization, which becomes especially relevant after this migration, is covered in our post on fixing dark mode flicker and FOUC in Next.js.
Breaking Change #3 Content Path Detection Is Now Automatic
In v3, you had to explicitly list every file path Tailwind should scan for class names in the content array of your config. Forgetting to add a path meant classes used in that file were purged from the production build, leading to missing styles that only appeared in production.
// v3 tailwind.config.js — manual content paths (now unnecessary in v4)
module.exports = {
content: [
"./app/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/**/*.{js,ts,jsx,tsx,mdx}",
],
// ...
};In v4, Tailwind automatically discovers all files in your project using the same heuristic logic as modern bundlers. It follows your import graph from the CSS entry point, respects .gitignore patterns, and scans everything in the project directory that is not explicitly excluded. For the vast majority of Next.js projects, the content array in your v3 config can be deleted entirely.
The edge case to watch: if you have Tailwind classes being generated dynamically constructed as template literal strings at runtime those classes could still be purged in production. This is not a v4 regression; it was always a risk in v3 too. The fix is the same: use @source directives to explicitly safelist patterns, or restructure the code to use complete class names rather than partial strings.
Breaking Change #4 Removed and Renamed Utilities
Several utility classes were renamed in v4 for consistency, and a handful were removed entirely. The ones that trip up the most developers in real codebases are listed here. If your build was fine and you are now seeing unexpected layout shifts or missing styles after upgrading, check this list first.
shadow-sm renamed to shadow-xs. The previous shadow-sm is now shadow-xs, and a new shadow-sm exists at a slightly larger size. If you used shadow-sm for subtle card elevation, your cards will look slightly heavier after migration unless you update the class.
blur-sm renamed to blur-xs. Same pattern as shadows the entire blur scale shifted by one step. blur-sm in v4 is visibly larger than blur-sm in v3.
ring-offset utilities removed. ring-offset-* classes no longer exist in v4. Rings are now rendered differently using box-shadow layers, and the offset effect is achieved by setting --tw-ring-offset-width and --tw-ring-offset-color as CSS variables in your component styles.
overflow-ellipsis renamed to text-ellipsis. A minor naming fix, but overflow-ellipsis will silently do nothing in v4. If you had text truncation with the old class, it breaks invisibly.
Flex and grid gap shorthands changed. The gap-x-* and gap-y-* utilities still work, but the underlying CSS variable names they set have changed. If you were referencing --tw-gap-x directly in any custom CSS, those variables no longer exist.
The v4 Plugin API Is Completely Different
If your v3 setup used plugins either third-party ones from npm or custom plugins defined in your config this is where migration gets complicated. The v3 plugin API used JavaScript functions that received a utility-injection API:
// v3 plugin — no longer works in v4
const plugin = require("tailwindcss/plugin");
module.exports = {
plugins: [
plugin(function ({ addUtilities, theme }) {
addUtilities({
".text-balance": { "text-wrap": "balance" },
});
}),
],
};In v4, custom utilities are added directly in CSS using the @utility directive. Third-party plugins that were written for v3 need to publish v4-compatible versions before they will work. If you depend on plugins like @tailwindcss/typography, @tailwindcss/forms, or @tailwindcss/aspect-ratio, check that you are on their v4-compatible releases before migrating.
/* v4 globals.css — custom utilities in CSS, no plugin function needed */
@import "tailwindcss";
@utility text-balance {
text-wrap: balance;
}
@utility scrollbar-hide {
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}How Tailwind v4 Changes the Headless Component Ecosystem
The shift to CSS-native configuration has significant implications for how component libraries built on Tailwind work. Libraries like shadcn/ui, Radix UI with Tailwind wrappers, and Headless UI are all in active v4 migration phases. The good news is that the CSS-variable-based token system in v4 makes component theming dramatically simpler a component library can expose its design tokens as CSS variables, and you override them in your @theme block without touching any JavaScript.
The shift to CSS-native configuration has significant implications for how component libraries built on Tailwind work. The broader context of why Material UI and similar libraries are losing ground to this model is covered in our analysis of the death of Material UI and the rise of headless components with Tailwind.
Step-by-Step Migration Checklist for Next.js Projects
Run through these steps in order. Each step has a clear pass/fail condition so you know when it is done before moving on.
1 Update packages. Run
npm install tailwindcss@latest @tailwindcss/vite@latest(or the PostCSS adapter if you are not on Vite). For Next.js projects, use@tailwindcss/postcss. Verify the installed version is 4.x.2 Replace your CSS entry point. Delete the
@tailwind base/components/utilitiesdirectives. Replace them with a single@import "tailwindcss";at the top of your globals.css.3 Migrate your theme. Move every custom color, font, spacing, and breakpoint from
tailwind.config.jsinto an@themeblock in your CSS file, using the--color-*,--font-*,--spacing-*naming convention.4 Fix dark mode. If you used
darkMode: "class"in v3, add@variant dark (&:where(.dark, .dark *));to your CSS. Test your dark mode toggle immediately after this step.5 Migrate plugins. Convert any
addUtilitiesoraddComponentscalls to@utilityand@layer componentsblocks in CSS. Check every third-party plugin for v4 compatibility before assuming it works.6 Audit renamed utilities. Search your codebase for
shadow-sm,blur-sm,ring-offset-*, andoverflow-ellipsis. Rename to their v4 equivalents. Do a visual diff of your UI at each breakpoint in both light and dark mode.7 Run a production build and check bundle size. You should see a measurable drop in CSS output size and a faster build time. If your CSS output is larger or styles are missing, re-check that all dynamic class names are using full class strings rather than concatenated partials.
What v4 Means for Your Design System
The most underrated benefit of Tailwind v4 is not the build speed. It is the fact that your design tokens colors, spacing, typography, radii are now plain CSS custom properties available to every part of your codebase without any JavaScript import or Tailwind configuration context. Your design system variables are accessible in inline styles, in CSS Modules, in SVG files, in third-party components, and in any browser DevTools inspector session. They exist in the cascade, not in a build-time abstraction.
This also means that runtime theming switching between different brand color palettes, tenant-specific themes in a multi-tenant SaaS, or accessibility-driven high-contrast modes becomes straightforward. You swap a set of CSS variable values at a scope level and every component using those tokens updates instantly, without any JavaScript re-render or class name manipulation. The design thinking behind clean, token-driven interfaces that take advantage of this model is explored in our article on clean brutalism and minimalist SaaS UI design in 2026.
The migration requires an afternoon of careful work for an average Next.js project. The reward is a faster build pipeline, a cleaner architecture, and a design token system that is genuinely easier to extend and maintain than anything v3 could offer.
Frequently Asked Questions
Do I have to migrate to v4 right now?
No. Tailwind v3 will continue to receive security patches and critical bug fixes. But the Oxide compiler, the CSS-native config model, and all new utilities are v4-only. If you are starting a new project in 2026, start with v4. If you have a stable production app, schedule migration during a maintenance window rather than rushing it. The breaking changes are real but manageable with a methodical approach.
Can I still use tailwind.config.js in v4?
Yes, but with limitations. You can import a JavaScript config file from your CSS using @config "./tailwind.config.js". This allows gradual migration you keep your existing config while moving tokens to the CSS @theme block incrementally. However, not all v3 config options are supported when imported this way, and the JavaScript config path will eventually be deprecated.
Is shadcn/ui compatible with Tailwind v4?
shadcn/ui released v4-compatible component styles in early 2025. If you initialize a new shadcn project, it will use v4 by default. Existing shadcn projects on v3 can migrate using the shadcn CLI's built-in migration command, which updates component styles to use the new CSS variable token format. Always run the migration command before manually editing shadcn component files.
Why are some of my utility classes missing after upgrading?
The most common reason is dynamic class name construction building class strings like "bg-" + color at runtime. Tailwind's scanner cannot statically detect these and they get purged. Use complete class names in your source code: "bg-red-500" rather than "bg-" + "red-500". Alternatively, add a @source directive to safelist specific patterns.
Does the Tailwind v4 upgrade affect Vercel deployment?
No. Vercel's build pipeline runs your standard Next.js build command, which includes the Tailwind compilation step. The Oxide engine works in Node.js build environments including Vercel, Netlify, and GitHub Actions without any configuration changes. The only thing to verify is that your package.json has the correct v4 package names so the right version is installed during CI builds.
Continue Reading
View All HubLevel Up Your Workflow
Free professional tools mentioned in this article
Robots.txt & LLMs.txt Generator
Generate robots.txt and llms.txt files instantly with AI bot presets for GPTBot, ClaudeBot, and PerplexityBot. Control who crawls your site in 2026.
CSS to Tailwind CSS Converter
Convert legacy CSS to modern Tailwind CSS utility classes instantly. 100% secure, free, and runs entirely in your browser. Boost your core web vitals today.
JSON to TypeScript Converter
Convert any JSON object into clean TypeScript interfaces instantly. Supports nested objects, arrays, and optional fields free, no signup, runs entirely in your browser.
Tailwind SVG Background Pattern Generator
The ultimate visual builder for Dot Grids, Plus Signs, and geometric SVG background patterns. Generate optimized Tailwind CSS classes for your SaaS landing pages.




