# Tailwind v4 Nextjs 16 Setup: Config, Fonts and 5 Fixes

> Set up a Tailwind v4 Nextjs 16 app: PostCSS plugin or the new Turbopack loader, @theme tokens in CSS, next/font variables and fixes for five errors.

- Author: [Serban Rusu](https://wingo-ui.com/blog/authors/serban), Founder of Wingo UI
- Published: Nov 6, 2026
- Category: [Next.js and Tailwind CSS](https://wingo-ui.com/blog/category/nextjs-tailwind)
- Reading time: 9 min
- Canonical: https://wingo-ui.com/blog/tailwind-v4-nextjs

## TL;DR

Tailwind v4 in Next.js 16 needs two packages (tailwindcss and @tailwindcss/postcss), a postcss.config.mjs with the plugin, @import "tailwindcss"; at the top of app/globals.css and that file imported once in the root layout. There is no tailwind.config.js and no content array: theme values live in @theme blocks in the CSS, and next/font variables plug in through @theme inline. Since create-next-app 16.4.0 (October 6, 2026) a new app gets the @tailwindcss/turbopack loader in next.config.ts instead of PostCSS: both work under Turbopack, only PostCSS also works with webpack.

You want Tailwind v4 in a Next.js 16 app and the guides disagree: one says `postcss.config.mjs`, a fresh `create-next-app` writes something else, and your old `tailwind.config.js` does nothing. This Tailwind v4 Nextjs guide sets it up from scratch, explains what each line does, wires `next/font` into the theme, and fixes the five errors that make Tailwind v4 look like it is not working.

What we checked on November 6, 2026: the templates inside `create-next-app` 16.4.5 (published November 3), the Next.js 16 docs that ship in `node_modules/next/dist/docs`, Tailwind's own Next.js guide, and the CSS that Tailwind 4.3.3 compiles for every snippet below. This site, wingo-ui.com, runs Next.js 16.3 with Tailwind 4.3.3 and the PostCSS plugin.

## What does a Tailwind v4 setup in Next.js 16 need?

Four things, fewer than v3 needed. A Tailwind v4 Nextjs 16 setup is complete with:

- two dev dependencies: `tailwindcss` and a bundler integration (`@tailwindcss/postcss` or `@tailwindcss/turbopack`),
- one config entry that registers the integration,
- `@import "tailwindcss";` at the top of `app/globals.css`,
- `import "./globals.css"` in `app/layout.tsx`.

What is gone: `tailwind.config.js`, the `content` array, the three `@tailwind` directives, `autoprefixer` and `postcss-import`. Tailwind v4 finds your class names by scanning the project (it skips `node_modules` and anything in `.gitignore`), adds vendor prefixes itself, and reads the theme from CSS.

## How do I install Tailwind v4 in Next.js?

Let `create-next-app` write the setup for a new app. An existing app takes four small edits with the PostCSS plugin.

### New app: let create-next-app write it

```bash
npx create-next-app@latest my-app --yes
cd my-app
```

`--yes` takes your saved preferences or the defaults, which include TypeScript, ESLint, the App Router, the `@/*` alias and Tailwind. Since version 16.4.0 (October 6, 2026) the Tailwind part changed. We read the 16.4.5 templates: when the bundler is Turbopack, the default, the CLI installs `@tailwindcss/turbopack` instead of `@tailwindcss/postcss`, skips `postcss.config.mjs` and writes a loader rule into `next.config.ts`:

```ts
// next.config.ts: the rule create-next-app 16.4.5 adds (its other default options left out)
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  /* config options here */
  turbopack: {
    rules: {
      "*.css": {
        loaders: ["@tailwindcss/turbopack"],
        as: "*.css",
      },
    },
  },
};

export default nextConfig;
```

That rule is Turbopack configuration, so a webpack build (`next dev --webpack`) never reads it. If you need webpack for a plugin, use the PostCSS route below instead. Version 16.3.6 and earlier still wrote `postcss.config.mjs`, which is why tutorials from this summer show a different file than your new app has.

### Existing app: install Tailwind v4 with PostCSS

This is the route the Next.js CSS docs and Tailwind's Next.js guide both document, and it works under Turbopack and webpack.

```bash
npm install -D tailwindcss @tailwindcss/postcss
```

```js
// postcss.config.mjs
const config = {
  plugins: {
    "@tailwindcss/postcss": {},
  },
};

export default config;
```

```css
/* app/globals.css */
@import "tailwindcss";
```

```tsx
// app/layout.tsx
import "./globals.css";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}
```

Run `npm run dev`, put `className="text-3xl font-bold"` on a heading, and it should render bold and large. That is all an install of Tailwind v4 in Next.js 16 needs. Use one integration, not both: either `postcss.config.mjs` or the Turbopack loader rule.

## Where does the Tailwind v4 config go in Next.js?

In `app/globals.css`, right after the import. The Tailwind v4 config is CSS: every variable in an `@theme` block becomes both a CSS variable and a set of utilities.

```css
@import "tailwindcss";

@theme {
  --color-brand: #d63f00;
  --radius-card: 1.25rem;
  --breakpoint-3xl: 120rem;
}
```

That gives you `bg-brand`, `text-brand`, `rounded-card` and a `3xl:` breakpoint. The variable's namespace decides which utilities it feeds: `--color-*` for colors, `--font-*` for font families, `--radius-*`, `--shadow-*`, `--breakpoint-*` and so on.

Values that change between light and dark belong in plain CSS variables, mapped with `@theme inline`:

```css
:root {
  --surface: #ffffff;
  --surface-foreground: #1f1f1f;
}

.dark {
  --surface: #1b1b1f;
  --surface-foreground: #ededed;
}

@theme inline {
  --color-surface: var(--surface);
  --color-surface-foreground: var(--surface-foreground);
}
```

With `inline`, Tailwind writes the reference into the utility, so `bg-surface` compiles to `background-color: var(--surface)` and follows the nearest `.dark`. Without `inline`, the utility points at `--color-surface`, which resolves where it is declared, on `:root`. That still works while `.dark` sits on `<html>`, as next-themes puts it, but a `.dark` section deeper in the page keeps the light value. The pillar post on [Tailwind v4 design tokens](https://wingo-ui.com/blog/tailwind-v4-design-tokens) goes through a complete token set: surfaces, text levels, tones, elevation and a contrast check.

## How do I use next/font with Tailwind v4?

`next/font` exposes each font as a CSS variable, and `@theme inline` turns that variable into `font-sans` or `font-mono`. The layout sets the variables on `<html>`:

```tsx
// app/layout.tsx
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";

const geistSans = Geist({ variable: "--font-geist-sans", subsets: ["latin"] });
const geistMono = Geist_Mono({ variable: "--font-geist-mono", subsets: ["latin"] });

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
      <body>{children}</body>
    </html>
  );
}
```

```css
@theme inline {
  --font-sans: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif;
  --font-mono: var(--font-geist-mono), ui-monospace, monospace;
}
```

Use `inline` here, as the Next.js font docs do. A plain `@theme` reference resolves on `:root`, which only works while the font classes sit on `<html>`. Move them to `<body>` and `font-sans` silently falls back to the system font; with `inline` it works on either element.

Tailwind's base styles set the `html` font to `--font-sans`, so the whole page picks up Geist with no class. There is one catch in the 16.4.5 starter: its `globals.css` ends with `body { font-family: Arial, Helvetica, sans-serif; }`. The demo page hides that by putting `font-sans` on its wrapper div. Replace `page.tsx` with your own markup and everything renders in Arial. Delete that line. Adding `font-sans` to `<body>` does not help, for the reason in error 3 below.

## Why is Tailwind v4 not working in Next.js?

Usually one of five setup errors, each with a message or a symptom you can match.

### 1. "It looks like you're trying to use tailwindcss directly as a PostCSS plugin"

Your `postcss.config.mjs` still says `tailwindcss: {}`. In v4 the PostCSS plugin moved to its own package. Install `@tailwindcss/postcss`, replace the key, and remove `autoprefixer` while you are there.

### 2. tailwind.config.js changes do nothing

v4 does not look for a config file. Move `theme.extend` into `@theme`, `darkMode` into `@custom-variant` (error 4) and plugins into `@plugin "plugin-name";` lines. For a gradual move you can load the old file with `@config "../tailwind.config.js";` under the import. The same change explains why `npx tailwindcss init` fails: the `tailwindcss` package no longer ships a command line tool. Migrating a shadcn project? The [shadcn Tailwind v4 migration guide](https://wingo-ui.com/blog/shadcn-tailwind-v4-migration) covers the codemod and what it misses.

### 3. Some classes are missing or lose to other CSS

Open DevTools on the element. If the class has no rule at all, Tailwind never saw it:

- **Files outside the project root.** In a monorepo, components in `packages/ui` are not scanned from `apps/web`. Add `@source "../../../packages/ui/src";` to `apps/web/app/globals.css` (the path is relative to the CSS file).
- **Class names built at runtime.** `` `bg-${tone}-500` `` never appears as text, so it is never generated. Write the full names in a map, `{ danger: "bg-red-500", success: "bg-green-500" }`.
- **`@apply` in a CSS module.** A `.module.css` file is compiled on its own and fails with "Cannot apply unknown utility class". Put `@reference "../globals.css";` (the path from the module to your stylesheet) at the top of the module.

If the rule exists but is crossed out, cascade layers are the cause. v4 puts utilities in `@layer utilities`, and any rule outside a layer beats every layered rule, whatever its specificity. That is why the starter's unlayered `body { font-family: Arial }` wins over `font-sans` on the same element. Wrap your own global rules in `@layer base { ... }` and utilities win again.

### 4. dark: classes ignore the theme toggle

By default `dark:` compiles to `@media (prefers-color-scheme: dark)`, so it follows the operating system and ignores the class that next-themes sets. Add one line:

```css
@custom-variant dark (&:where(.dark, .dark *));
```

Then pass `attribute="class"` to the next-themes `ThemeProvider` and delete the starter's `@media (prefers-color-scheme: dark)` block, or the system setting keeps overriding your toggle. The [Tailwind v4 dark mode guide](https://wingo-ui.com/blog/tailwind-v4-dark-mode) covers tokens and the toggle, and the [next-themes hydration error post](https://wingo-ui.com/blog/next-themes-hydration-error) covers the flash on reload.

### 5. New classes appear only after a restart

You add a class, save, and nothing changes until you restart `next dev`. A [Tailwind discussion from May 4, 2026](https://github.com/tailwindlabs/tailwindcss/discussions/20006) reports exactly this on Next.js 16. Since Next.js 16.1, Turbopack also keeps its dev cache on disk in `.next/dev/cache/turbopack` and reuses it on restart, so a stale result can survive one. The order that works for us:

1. Stop the dev server and delete the cache: `rm -rf .next`.
2. Check that only one integration runs (no PostCSS config next to a Turbopack loader rule).
3. To isolate the bundler, run `next dev --webpack` with the PostCSS setup. If the problem disappears, it is a Turbopack issue worth reporting with a reproduction.

## How do I add components on top of this setup?

A component library built for Tailwind v4 only needs the tokens it uses to exist in your `@theme`. Wingo UI's init command merges them into `app/globals.css` and adds the `cn` helper:

```bash
npx wingo-ui@latest init
npx wingo-ui@latest add button card ui-config
```

The [Button](https://wingo-ui.com/components/button), [Card](https://wingo-ui.com/components/card) and [UIProvider](https://wingo-ui.com/components/ui-config) are free items. They use the semantic tokens from the sections above (`bg-surface`, `shadow-raised`, `text-muted-foreground`), so they switch with your `.dark` class without a single `dark:` prefix. A server page can render them directly:

```tsx
// app/page.tsx
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";

export default function Page() {
  return (
    <main className="mx-auto max-w-md p-6">
      <Card
        title="Tailwind v4 is running"
        description="Tokens from globals.css, fonts from next/font, components from Wingo UI."
        footer={<Button tone="brand">Continue</Button>}
      />
    </main>
  );
}
```

> Live demo (Card): Hover or press the card: it lifts onto a deeper shadow and dips while pressed, and its fill and text come from the surface tokens in both themes. Try it at [Card](https://wingo-ui.com/components/card) and install it with `npx wingo-ui@latest add card`.

Shared defaults go in the root layout, so every button in the app gets the same shape without repeating props. `UIProvider` is a client component; a server layout can render it and pass plain objects:

```tsx
// app/layout.tsx (inside <body>)
import { UIProvider } from "@/lib/ui-config";

<UIProvider defaults={{ button: { radius: "full", size: "lg" } }}>{children}</UIProvider>
```

> Live demo (Button): Click the button: it dips while pressed, then shows its loading state at the same width, and every color it uses comes from the tokens in globals.css. Try it at [Button](https://wingo-ui.com/components/button) and install it with `npx wingo-ui@latest add button`.

The [Next.js component library setup guide](https://wingo-ui.com/blog/next-js-component-library-setup) walks through init, `"use client"` boundaries and updates in detail, and the [theming docs](https://wingo-ui.com/docs/theming) list every token. More posts on this stack live under [Next.js and Tailwind CSS](https://wingo-ui.com/blog/category/nextjs-tailwind).

## What do the final files look like?

For an existing app on the PostCSS route, three files hold the entire setup:

```js
// postcss.config.mjs
const config = { plugins: { "@tailwindcss/postcss": {} } };
export default config;
```

```css
/* app/globals.css */
@import "tailwindcss";

@custom-variant dark (&:where(.dark, .dark *));

:root {
  --background: #ffffff;
  --foreground: #1f1f1f;
  --surface: #ffffff;
}

.dark {
  --background: #111113;
  --foreground: #ededed;
  --surface: #1b1b1f;
}

@theme inline {
  --color-background: var(--background);
  --color-foreground: var(--foreground);
  --color-surface: var(--surface);
  --font-sans: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif;
  --font-mono: var(--font-geist-mono), ui-monospace, monospace;
}

@layer base {
  body {
    background: var(--background);
    color: var(--foreground);
  }
}
```

The layout is the `next/font` version above. On the create-next-app route, swap `postcss.config.mjs` for the `turbopack.rules` entry in `next.config.ts`; `globals.css` and the layout stay the same.

## Next step

If the setup works and you want components that already speak these tokens, `npx wingo-ui@latest add button card` installs both free components from this post, with `UIProvider` as a dependency, and needs no account. All 75 free items install the same way, and the [installation docs](https://wingo-ui.com/docs/installation) cover Vite and existing projects.

## Components in this post

- [Button](https://wingo-ui.com/components/button) (Free): The React button component every screen starts with: five variants, six tones, all sizes, and a loading state that never jumps. Install: `npx wingo-ui@latest add button`
- [Card](https://wingo-ui.com/components/card) (Free): A React card component for listings, stats, settings and plans: media, header, body and footer parts, and a lift-and-press mode. Install: `npx wingo-ui@latest add card`
- [UIProvider](https://wingo-ui.com/components/ui-config) (Free): A React config provider for Wingo UI: app-wide default props for every component, the Intl locale and a reduced-motion policy. Install: `npx wingo-ui@latest add ui-config`

## FAQ

### Does Tailwind v4 need a tailwind.config.js in Next.js?

No. Tailwind v4 reads its configuration from CSS: theme values go in @theme blocks in app/globals.css and source files are detected without a content array. A v3 config file is ignored unless you load it with @config "../tailwind.config.js"; in the stylesheet.

### Should I use @tailwindcss/postcss or @tailwindcss/turbopack?

Both compile the same CSS under Turbopack, the default bundler in Next.js 16. create-next-app 16.4 and later writes the Turbopack loader; the Tailwind and Next.js docs still show the PostCSS plugin, which also works when you run next dev --webpack. Pick one, not both.

### Why are my Tailwind classes not applied in Next.js?

Check four things in order: the class exists in the built CSS (DevTools), the file is inside the detected sources (add @source for monorepo packages), the class name is written out in full (no bg-${color}-500), and no unlayered rule in another stylesheet overrides it.

### Do I still need autoprefixer with Tailwind v4?

No. Tailwind v4 adds vendor prefixes and handles @import itself, so the PostCSS config needs only @tailwindcss/postcss. Remove autoprefixer and postcss-import from older configs.

### Which browsers does Tailwind v4 support?

Tailwind's compatibility page lists Chrome 111, Safari 16.4 and Firefox 128 as the minimum, because v4 relies on cascade layers, @property and color-mix(). If you must support older browsers, stay on Tailwind v3.4.

---

Source: https://wingo-ui.com/blog/tailwind-v4-nextjs
