Tailwind v4 + Next.js: what actually changed for app router projects
Source: Malahim's blog
Release notes tell you what changed. Framework-shaped write-ups answer the question every Next.js team actually asks: "what breaks in our repo, and what is just different?". Malahim's walkthrough is the right kind of community piece for that — opinionated, concrete, and willing to call out the rough edges.
Great for teams asking: "What breaks in our Next.js repo, and what is just different?"
TL;DR for Next.js teams
- No more `tailwind.config.ts` for most projects —
@themeblocks in your global CSS replace it. - `@tailwind base/components/utilities` is gone — one line:
@import "tailwindcss". - `postcss.config.js` shrinks to the v4 PostCSS plugin only;
postcss-importandautoprefixerare no longer required. - Source detection finds your
app/,pages/,components/, andsrc/automatically. Monorepos still need an@sourceline per workspace. - Visual regressions are real — the OKLCH palette is slightly different from the v3 sRGB defaults; budget a design review pass.
The PostCSS path (still the default in Next.js)
Next.js uses PostCSS under the hood. v4 ships a first-party plugin that replaces the old tailwindcss PostCSS entry plus its companions:
// postcss.config.mjs (Next.js, v4)
export default {
plugins: {
"@tailwindcss/postcss": {},
},
};Compare to a typical v3 file with three or four entries (postcss-import, tailwindcss/nesting, tailwindcss, autoprefixer). Lightning CSS handles @import, vendor prefixes, and modern syntax internally, so the chain collapses.
If you previously customized the chain (CSS modules tweaks, custom nesting rules), keep those plugins; the v4 plugin coexists with the rest of the chain.
The Vite path (for app-router projects on a non-Next runtime)
The @tailwindcss/vite plugin is the recommended path if you're on Astro, Remix's Vite runtime, or a fresh React + Vite app. Drop it in:
// vite.config.ts
import tailwindcss from "@tailwindcss/vite";
export default {
plugins: [tailwindcss()],
};The mental model is the same as PostCSS — same @theme, same @source rules, same output — but the integration is tighter because Vite hands the plugin a richer file watcher.
For Next.js itself, keep the PostCSS path. The Next team's recommended pipeline is well-tested with the PostCSS plugin and there's no current advantage to detouring through Vite-specific tooling.
Where `@import "tailwindcss"` goes
In Next.js apps, your global stylesheet is usually app/globals.css (App Router) or styles/globals.css (Pages Router). The v4 entry is one line:
/* app/globals.css */
@import "tailwindcss";
@theme {
--color-brand-500: oklch(0.70 0.22 142);
--font-display: "Inter Display", ui-sans-serif, system-ui;
--radius-card: 0.75rem;
}
/* Your app-level overrides — variables, base styles, anything custom */
:root {
--bg: oklch(0.98 0.005 145);
}Two things to watch:
- The
@importmust be before your@themeblock, since the theme namespaces are defined by the import. - Don't double-import in component CSS modules — that produces duplicate utilities and inflates the bundle.
Source detection in a Next.js layout
The compiler walks the project automatically. For a typical Next.js app, that's exactly what you want — app/**, components/**, src/** are all scanned without configuration.
Three places where you still need @source:
- Pulling classes out of a sibling package in a monorepo:
@source "../../packages/ui/dist/**/*.{js,mjs}";- Excluding a folder you don't want scanned (e.g. legacy templates being phased out):
@source not "../legacy-pages/**";- Safelisting runtime-only strings that don't appear as literals in source:
@source inline("bg-success-500 bg-warning-500 bg-danger-500");If you build a class name dynamically from a database column (status colors, theme keys), this is the cleanest pattern — better than the old safelist array.
What disappears from your codebase
After the upgrade, expect these files to either shrink or vanish:
- `tailwind.config.ts` — most projects no longer need it. Keep only if you're using third-party plugins like
@tailwindcss/formsor@tailwindcss/typography. - `postcss.config.mjs` — usually one plugin entry left.
- Type imports —
import type { Config } from "tailwindcss"is no longer relevant for the theme path.
What stays:
- Plugin imports for
forms,typography, and any third-party plugin you rely on. - `globals.css` — gains a
@themeblock, loses some hand-rolled CSS that's now expressible as utilities.
Plugins still in flux
By 2026, the major Tailwind plugins have v4-compatible releases:
- `@tailwindcss/forms` — forms reset for input/select/textarea.
- `@tailwindcss/typography` —
proseclasses for Markdown-driven content. - shadcn/ui copy-paste components — updated to v4 idioms.
Smaller community plugins may still trail. If a plugin you depend on hasn't shipped v4 support, the migration tool will flag it; either contribute a fix or temporarily pin a v3.x release for that one package.
Things that surprise teams
A short list of "wait, what?" moments from Next.js upgrades:
- OKLCH colors look different. Your brand swatch will render slightly more saturated on P3 displays. Most teams accept the change; if not, override the variable in
@themeto lock in the old hex. - Container queries work without a plugin. If you previously installed
@tailwindcss/container-queries, remove the import; the variants exist natively. - CSS-modules + Tailwind still work, but the nested syntax in modules now goes through Lightning CSS, not the old PostCSS nesting plugin. Strict CSS-spec-compliant nesting is fine; some quirks tolerated by the old plugin aren't.
- Storybook picks up the new
@themeautomatically because it's just CSS — no extra wiring needed.
Caveat: third-party posts age fast
Malahim's piece, like any community write-up, freezes the state of the world on its publish date. Tailwind shipped v4.1 (text shadows, masks) and v4.2 (webpack plugin, logical properties, four new palettes) after most third-party posts went live. Cross-check version pins with the official upgrade guide and the GitHub releases before you merge a v4 bump to main — you may want to land on the latest 4.x rather than the announcement-day version.
Suggested upgrade order
1. Branch. Don't upgrade in main.
2. Run `npx @tailwindcss/upgrade`. Review the diff carefully.
3. `npm install` the latest tailwindcss, @tailwindcss/postcss, and any plugins.
4. Smoke test the dev server. Most failures show up immediately as missing utilities or unstyled components.
5. Visual diff the homepage, settings, and one data-heavy screen.
6. Migrate plugins, removing the ones now in core.
7. Delete `tailwind.config.ts` if you can; otherwise prune to plugin-only.
8. Merge.
The upgrade is reversible until step 8. Most Next.js teams ship it inside a single afternoon.
References
- Original write-up: Tailwind CSS v4 — what actually changed for your Next.js project
- Official: Tailwind CSS upgrade guide
- Official: Tailwind CSS v4.0 announcement
Source / further reading: Malahim's blog