TypeScript

Tailwindcss not working with Next.js: fix the config

A Next.js build that finishes clean but ships a page with zero Tailwind styling almost always has one wrong setting: the paths Tailwind scans for class names.

Mahdi Ben RhoumaMahdi Ben Rhouma
7 min read

TL;DR#

If next build finishes with no error but the page renders with zero Tailwind styling (or just the body/heading resets), the usual cause is a content array in tailwind.config.js that does not match the files holding your className strings — not a broken PostCSS pipeline. Point content at every folder that actually contains your components, delete the stale .next cache, and make sure your CSS file is imported exactly once, in the root layout.

This is the failure mode behind a 213,000-view Stack Overflow question, "Tailwindcss not working with next.js; what is wrong with the configuration?" — reproduced below on Tailwind CSS 3.4.19 and Next.js 14.2.5.

text
warn - No utility classes were detected in your source files. If this is unexpected, double-check the `content` option in your Tailwind CSS configuration.
warn - https://tailwindcss.com/docs/content-configuration
 ✓ Compiled successfully

Why it happens#

Tailwind does not ship a stylesheet with every possible class pre-built. Its own documentation describes the mechanism plainly: it "works by scanning all of your HTML files, JavaScript components, and any other templates for class names, generating the corresponding styles and then writing them to a static CSS file" (Tailwind CSS docs, Installing with PostCSS). If a file is outside the globs Tailwind scans, every className inside it is invisible to the compiler — next build has no way to know those strings were ever meant to become CSS, so it does not treat it as an error. The build compiles, the page renders, and the only thing missing is the styling.

Reproduced in a throwaway Next.js 14.2.5 + Tailwind CSS 3.4.19 App Router project, 8 October 2026: with tailwind.config.js pointing content at ./src/**/*.{js,ts,jsx,tsx,mdx} while every component actually lived under ./app, the build printed the warning above and the compiled stylesheet under .next/static/css/ contained only Tailwind's Preflight reset — no .text-3xl rule at all. Changing nothing else but the glob to ./app/**/*.{js,ts,jsx,tsx,mdx} and rebuilding made the warning disappear and put the .text-3xl rule back in the output. One setting, same files, same command — that is the entire bug.

The highest-voted answer on the source question (score 121) names the same cause for the App Router layout specifically: a tailwind.config.js generated for the old pages/ directory, or copied from a tutorial, that never lists ./app/**/*.{js,ts,jsx,tsx,mdx}.

A second, independent cause on the same question (score 104) is a stale .next build cache: Next.js caches compiled CSS and JS output between runs, so after a config change that should fix the styling, an old cached build can still ship the pre-fix stylesheet until the cache is cleared.

Fix#

1. Confirm which Tailwind major you are actually running — the fix is different for each one.

bash
npm ls tailwindcss

2. On Tailwind CSS v3, point content at every folder that holds a className. For the Next.js App Router:

js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './app/**/*.{js,ts,jsx,tsx,mdx}',
    './pages/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx,mdx}',
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

If your project still uses the pre-v3 purge key instead of content (the config in the source question does), it is not the problem by itself: Tailwind 3.4.19 normalises purge into content with a deprecation warning, not silently. Rename the key for clarity, but the glob pattern is what has to be correct either way.

3. Delete the Next.js build cache after any content/config change, then rebuild:

bash
rm -rf .next
npm run dev

4. Make sure the Tailwind directives are imported exactly once, in the root layout (app/layout.js / app/layout.tsx), not re-declared in a page or a component:

jsx
import './globals.css'
 
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}
css
@tailwind base;
@tailwind components;
@tailwind utilities;

Next.js's own install guide for Tailwind v3 uses this exact three-directive globals.css imported from the root layout (Next.js docs, Tailwind CSS v3). If a project instead imports fonts or other @import rules above the directives in the same file, keep @tailwind base/components/utilities as the first rules in the file — some PostCSS setups fail or reorder output when an @import for an external URL comes first. On the Pages Router there is no root layout: the stylesheet belongs in pages/_app.js, and importing it from a component instead triggers the Pages Router Global CSS import error.

Verify the fix#

After rebuilding, check the compiled output directly instead of trusting the page visually (a cached browser tab can hide a real fix, and a dev-server hot-reload can hide a real break):

bash
grep -rl "text-3xl" .next/static/css/

A match means the utility class made it into the stylesheet. No match, with a clean build and no warning, means content still is not scanning the file that uses the class — check the exact path again, including the file extension list.

One email a month — the latest explained errors

What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.

Which Tailwind major are you actually on?#

The source question is from 2020 and its tailwind.config.js uses purge and future.removeDeprecatedGapUtilities, both v3-era settings. A plain npm install tailwindcss today installs Tailwind CSS v4, whose PostCSS setup has no content array at all: install tailwindcss and @tailwindcss/postcss, add @tailwindcss/postcss to postcss.config.mjs, and @import "tailwindcss"; in the CSS file (Tailwind CSS docs, Installing with PostCSS). If classes are missing on v4, the content-array fix above does not apply — check the PostCSS plugin name instead, covered in Fix: Tailwind PostCSS Plugin Has Moved (Vite + React).

Two further v4-only failure modes to rule out before touching content:

Next error to expect#

Fixing the content globs resolves classes that are missing everywhere. If instead only some classes are missing — commonly ones built from a dynamic string like `text-${color}-500` — the cause is different: Tailwind's scanner needs the complete class name as a literal string somewhere in the file, so a template literal that only exists at runtime never gets detected. That is a separate bug from a wrong content path and needs the class names written out literally (for example in a lookup object), not a change to the glob. If the font-family class is present on the element but a custom font still renders as the default, the @font-face rule is what failed, and the Tailwind custom font @font-face fix covers the invalid format('ttf') hint and the nested quotes that stop the family name from matching.

A build that compiles without the warning above, but whose styles still don't match in production while they work in next dev, is usually a different problem again: a style that depends on a dynamic class name surviving minification, not a scanning gap. Treat it as a separate investigation rather than assuming the same content fix applies.

Further reading: Fix: TypeError: Invalid PostCSS Plugin found at: plugins[0].

Frequently Asked Questions

|

Have more questions? Contact us

Still stuck? Paste your error.

Paste the error message or stack trace — we check it against every fix and tool on the site.

Matching happens in your browser. When you search, a cleaned error signature is sent so we can spot errors we do not cover yet — never the raw text, your IP address or your e-mail. Do not paste passwords or keys. Details

Written by

Mahdi Ben Rhouma
Mahdi Ben Rhouma

Full-Stack Dev — Next.js & Supabase

Solo developer building SaaS products with Next.js and Supabase. Writing about production patterns the official docs skip.

Remote