Tailwind v3 vs v4: what changed in utility classes

v4 is not a light reskin: the team rebuilt the engine and moved customization toward CSS-native patterns while keeping the utility mental model. If you have been on v3.x since 2022, the move feels less like a routine bump and more like opting into a new authoring environment that happens to ship the same class names you already know.
Tailwind CSS v4.0 is an all-new version of the framework optimized for performance and flexibility, with a reimagined configuration and customization experience.
TL;DR
- No more JS config for most projects —
@themeblocks in CSS replace large parts oftailwind.config.js. - No more `content` array — Tailwind walks your project automatically and respects
.gitignore; you only reach for@sourcewhen you import classes from a sibling package or a build artifact. - Faster — full builds and incremental passes are an order of magnitude quicker on the published benchmarks.
- OKLCH by default — the palette ships in a wide-gamut color space, and gradients can interpolate in it.
- Container queries are core — no plugin install.
- 3D transforms, masks, conic gradients, `@starting-style` — utilities you previously needed plugins or hand-rolled CSS for.
Why a rewrite happened
Tailwind Labs wrote v3 against a single assumption: PostCSS plus a JavaScript-heavy compiler is fast enough for the long tail of projects. By late 2024 that assumption was creaking — giant monorepos, design systems with thousands of tokens, and JIT scans hitting node_modules made cold builds painful. v4 introduces a new engine (often called "Oxide" in early posts) with a tight Rust core for hot paths and Lightning CSS for parsing, vendor prefixing, and bundling @import. The mental model — utility classes generated from a theme — is unchanged. What shifted is where customization lives and how the compiler finds your classes.
Configuration: from JS to CSS
In v3 a typical project ended up with a tailwind.config.js that wired plugins, declared colors, defined breakpoints, and sometimes pulled type definitions from elsewhere. v4 lets that file shrink dramatically — or disappear:
@import "tailwindcss";
@theme {
--color-brand-500: oklch(0.70 0.22 142);
--font-display: "Inter Display", ui-sans-serif, system-ui;
--breakpoint-3xl: 120rem;
--spacing: 0.25rem;
}A few things to notice:
- The theme is a CSS variable namespace (
--color-*,--font-*,--breakpoint-*,--spacing, …). Each namespace seeds a category of utilities. - You can still keep a
tailwind.config.jsfor plugins and escape hatches, but a lot of projects no longer need it day-to-day. - Because the theme is a real CSS file, runtime tools (devtools, Storybook, Figma plugins) can read variable names directly without bouncing through Node.
Content paths: gone unless you opt in
v3 made you maintain a content array; forget a folder and your component would render unstyled. v4 walks the project automatically, skipping node_modules, file types it cannot scan, and anything ignored by Git.
Three new at-rules give you control when automation is not enough:
- `@source "../packages/ui/dist/.js"`** — pull classes out of a sibling package or a build artifact.
- `@source not "../legacy"` (added in v4.1) — exclude a folder you don't want scanned.
- `@source inline("hidden md:flex")` — safelist classes that only ever exist as runtime strings.
Most apps go from a careful content-path checklist to "it just works".
Performance, for real
Tailwind Labs published numbers that line up with what teams report in practice:
- Full builds 3–5× faster on representative apps.
- Incremental rebuilds in the microsecond range when no new classes are emitted — the compiler skips work entirely if your edit didn't introduce a new utility.
- Lower memory ceiling, which matters in CI and on cheap dev machines.
If your DX pain in v3 was "save a file, wait half a second", that pain mostly evaporates.
Color, gradients, and modern CSS
The default palette switches from sRGB hex values to OKLCH, a perceptually uniform space that takes advantage of P3-capable displays. Two consequences:
- The same step on the lightness scale (
-500vs-600) feels visually equivalent across hues. In v3 some hues looked muddy at the same step. - Gradient utilities can interpolate in OKLCH to avoid the gray middle that linear sRGB blends produce.
v4 expands gradient utilities to include conic and radial helpers (bg-conic-*, bg-radial-*) and linear directional ones (bg-linear-to-r, bg-linear-to-br). 3D transform utilities (rotate-x-*, translate-z-*, perspective-*) ship in core for card flips and depth tricks that previously required hand-rolled CSS.
Variants you didn't have
- `starting:` — pairs with
@starting-stylefor popovers and discrete property transitions, finally usable without a polyfill. - `not-*` and `in-*` — express "not in :hover" and "inside an open details", composing with the rest of the variant system.
- `details-content:` — style only the content of a
<details>element after the marker.
Plugins folded into core
If you carried these in v3, drop them on upgrade:
@tailwindcss/container-queries—@containerand the@-prefixed variants live in core.@tailwindcss/aspect-ratio— modernaspect-*utilities already shipped; v4 finalizes the move.
Plugins still exist (forms, typography), but the surface that needed them is smaller.
Migration: what actually breaks
The official upgrade guide ships an automated tool (npx @tailwindcss/upgrade) that handles most of the mechanical edits:
- Rewrites
@tailwind base/components/utilitiesto@import "tailwindcss". - Moves theme overrides from JS to a
@themeblock. - Collapses arbitrary values that can now be expressed as dynamic utilities.
- Flags class renames (a few, mostly around opacity-as-modifier syntax).
What the tool can't do for you:
- Decide whether to keep a
tailwind.config.jsfor plugins or fully migrate. - Audit custom plugins that hooked into the v3 internals — those plugin APIs changed.
- Re-test visual regressions if your design tokens were tuned against the old sRGB palette and now interpolate differently.
A reasonable order: bump on a branch, run the upgrade tool, take screenshots of representative pages, diff colors and gradients, then deal with plugin-specific edge cases.
When *not* to upgrade today
- You're on a frozen LTS line because of a contractual lock — wait for v4.x to settle (v4.2 already covers webpack and logical properties; v4.1 added text shadows and masks).
- You ship a UI library that has to support consumers on v3 and v4 simultaneously — the engine differences make a single class output non-trivial; document a peer range and let downstream pick.
- Your toolchain depends on a v3 plugin that hasn't released a v4-compatible build yet.
For everything else, the cost is "an afternoon for a normal repo, two days for a complex monorepo", and the win is durable: faster builds, less config, and better defaults.
Further reading
- Tailwind CSS v4.0 announcement
- Upgrade guide
- v4.1 highlights — text shadows, masks, pointer variants (blog)
- v4.2 release — webpack plugin, logical utilities, four new palettes
Source / further reading: Tailwind CSS blog + upgrade guide