Next.js vs. Tailwind CSS: They Don't Compete, and the Glue Is 5 Lines
Next.js routes and renders; Tailwind generates CSS. Measured on this repo: 876 static pages, one 15 KB gzipped stylesheet, no Tailwind config file.
Next.js and Tailwind CSS are not competitors: Next.js is a React framework that routes, renders and bundles your app, and Tailwind is a CSS generator that turns the class names in your markup into a stylesheet. Neither can do the other's job. You pick Next.js or another framework, and Tailwind or another way to write CSS — and this storefront uses both, with five lines of glue between them.
The numbers below come from that codebase: 876 prerendered pages, one 85 KB stylesheet (15 KB gzipped), no tailwind.config file, and a 133-line globals.css.
What each one actually is
| Next.js | Tailwind CSS | |
|---|---|---|
| Layer | Application framework | Styling tool |
| Decides | URLs, rendering (static/server/client), data loading, bundling | What CSS exists and what the classes are named |
| Output | HTML, JavaScript chunks, route handlers | One CSS file |
| Runs when | Build time and request time | Build time only |
| Replaces | Create React App, Remix, Astro, a hand-rolled router | Sass, CSS Modules, styled-components, hand-written CSS |
| Needs the other? | No — it ships CSS support but no opinion about it | No — it works in any bundler, framework or plain HTML |
The confusion has a reason. Both appear in the first command of most tutorials, and both show up in package.json of nearly every modern landing page. But the question they answer is different. "Should I use Next.js or Remix?" is a real fork. "Should I use Next.js or Tailwind?" has no fork in it: there is no version of the project where choosing one removes the need to decide about the other.
The glue is five lines
Next.js supports global CSS and PostCSS out of the box, and Tailwind v4 ships as a PostCSS plugin. That is the whole integration. This is the entire postcss.config.mjs:
const config = {
plugins: {
"@tailwindcss/postcss": {},
},
};
export default config;
And the top of src/app/globals.css, imported once from src/app/layout.tsx:
@import "tailwindcss";
/* Class-based dark mode (toggled by adding `dark` to <html>) */
@custom-variant dark (&:where(.dark, .dark *));
@theme {
--font-sans: var(--font-outfit), ui-sans-serif, system-ui, sans-serif;
/* Brand / primary scale */
--color-primary: #465fff;
--color-primary-25: #f2f7ff;
--color-primary-50: #ecf3ff;
Nothing else connects them. Next.js does not know Tailwind exists; it sees a CSS import that passes through PostCSS. Tailwind does not know about Next.js; it scans the source files for class names and emits matching rules. The @theme block holds 38 colour tokens, and each one becomes both a CSS variable and a family of utilities (bg-primary, text-primary-50, border-primary). There is no tailwind.config.js — in v4 the configuration lives in the stylesheet. If you want the token side in depth, Tailwind design tokens walks through it.
What the pairing produced here
Measured from npm run build on this repository (Next.js 16.2.9, @tailwindcss/postcss 4.3.1):
| Measurement | Value |
|---|---|
| Prerendered pages | 876 |
| CSS files emitted | 1 |
| CSS size | 85,390 bytes raw, 15,139 bytes gzipped |
tsx files in src | 139 |
Route folders with a page.tsx directly under src/app | 23 |
Source lines carrying a dark: variant | 336 |
| Config files for Tailwind | 0 (just the 5-line PostCSS config) |
The single CSS file is the number that matters. Tailwind only emits rules for classes it finds in your source, so every one of those 876 pages shares one cacheable stylesheet instead of per-page CSS that grows with the site. The pages themselves are statically generated by Next.js — the framework half of the stack — which is why none of them pay a runtime styling cost either. Tailwind has no runtime; there is no CSS-in-JS library in the client bundle.
That split is the real answer to "which is faster": they affect different things. Next.js decides when and where HTML is produced. Tailwind decides how much CSS arrives with it. Neither can be traded for the other.
When each choice is actually a choice
The forks that do exist sit next to each of them, not between them.
| If you are choosing… | Real alternatives | This repo's call |
|---|---|---|
| A framework | Remix, Astro, plain React with Vite | Next.js — see React vs. Next.js for the trade |
| A styling approach | Sass, CSS Modules, styled-components | Tailwind — see Sass vs. Tailwind |
| Components on top of the styling | shadcn/ui, Mantine, hand-written | Hand-written — see shadcn vs. Tailwind |
| How to ship dark mode | prefers-color-scheme only, or class-based | Class-based, via the @custom-variant above — Tailwind v4 dark mode |
Two of those rows are worth a note. The first is that Tailwind works equally well inside Next.js's main competitors; the pairing is popular because both are mainstream, not because they depend on each other. The second is that Next.js's own CSS support (global stylesheets, CSS Modules, Sass) remains available alongside Tailwind. You can run Tailwind for the layout and utilities and a CSS Module for the one component whose styles you want scoped.
Where the two do interact
There are three places where the pairing has real consequences, and this codebase hit each.
Dev-server caching of @theme. Next.js's Turbopack dev server does not hot-reload changes to the @theme block. Add --color-brand: #123456 and bg-brand silently generates nothing until you restart npm run dev. This is documented in the project's CLAUDE.md precisely because it looks like a Tailwind bug and costs people twenty minutes the first time. Editing class names hot-reloads fine; editing tokens does not.
Content Security Policy. The CSP in next.config.ts has to allow 'unsafe-inline' for styles, because Tailwind output and Next.js's own runtime both inject inline <style> and <script> tags. A nonce-based policy would force every route to render dynamically per request, which would break the static pages. This is a framework-and-styling interaction neither tool's docs warn about on their own. The tradeoff is spelled out in Next.js Content Security Policy.
Class sorting. With 818 className literals across the components, unsorted utility classes turn diffs into noise. The Prettier setup uses prettier-plugin-tailwindcss with "tailwindStylesheet": "./src/app/globals.css" so the plugin reads the v4 tokens rather than a config file that does not exist.
Which should I learn first
| If you… | Start with |
|---|---|
| Know React and want to ship a site with routing and SEO | Next.js — it gives you pages, metadata and static generation |
| Know HTML/CSS and want to build layouts quickly | Tailwind — it works in a single HTML file |
| Are building a marketing page | Both; this is the stack the repository's templates are built on |
| Are building a pure client-side app with no SEO need | Tailwind alone, with whatever bundler you already have |
Learning order matters less than people expect, because the two barely touch. A weekend on Tailwind transfers to any framework you move to later.
Mistakes and troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| "Should I pick Next.js or Tailwind?" | Category error — they solve different problems | Pick a framework and a styling approach independently |
bg-brand renders nothing after adding a token | Turbopack does not hot-reload @theme | Restart npm run dev |
| Tailwind classes are missing in production only | The class name is built dynamically (`bg-${color}-500`) so the scanner never sees it | Write complete class names, or map to them with a lookup object |
| Styles work in dev but a nonce-strict CSP blocks them | Inline <style> injected by the toolchain | Allow 'unsafe-inline' for styles, or move to per-request nonces and accept dynamic rendering |
| Dark mode does not toggle | The dark variant defaults to prefers-color-scheme | Declare @custom-variant dark as in globals.css and toggle .dark on <html> |
| Class order churns in every pull request | No formatter owns the order | Add prettier-plugin-tailwindcss and point tailwindStylesheet at the CSS entry |
Frequently asked questions
Is Tailwind CSS a replacement for Next.js? No. Tailwind generates CSS; it has no routing, rendering or data loading. If you remove Next.js you still need something to build pages and serve them.
Do I need Tailwind to use Next.js? No. Next.js includes support for global CSS, CSS Modules and Sass, so it works with any styling approach. Tailwind is a popular default, not a requirement.
Does Tailwind slow down a Next.js site? It does not add runtime cost: it compiles at build time and ships plain CSS. This repo's 876 pages share one 15 KB gzipped stylesheet.
Can I use Tailwind CSS with Next.js server components? Yes. Classes are just strings in markup, so they work in Server and Client Components alike. All seven atoms in this codebase are Server Components.
Templates in this post
ASoc Coin is an online-banking marketing site built around a cash-overview dashboard mock and a three-tier pricing page. ASoc Compound is an automated-investing marketing site with goal-based portfolios and three-tier pricing. ASoc Cortex is an AI-agency marketing site covering services, industries, case studies and a lead funnel.
All three are Next.js + Tailwind v4 projects wired exactly as above. Browse the full sets: Next.js landing page templates, Tailwind landing page templates.
