# Tailwind + Next.js: The Complete Setup Guide (2026)

> Set up Tailwind CSS with Next.js 15 step by step — v4 config, App Router, dark mode, shadcn/ui, custom fonts, performance, and common troubleshooting.

Source: https://designrevision.com/blog/tailwind-nextjs-setup

---

Tailwind CSS and Next.js are the dominant pairing in modern web development. Over 70% of Next.js projects use Tailwind for styling. The combination ships with first-class support: run `create-next-app` with the `--tailwind` flag and you have a working setup in 30 seconds.

But "working" and "optimized" are different things. The default setup misses dark mode configuration, custom theme tokens, shadcn/ui integration, font optimization, and the performance tuning that separates a prototype from a production application.

This tailwind nextjs guide covers everything from initial setup to production optimization. Whether you are starting a new project or upgrading an existing one, you will have a complete, production-ready Tailwind configuration by the end.
## Key Takeaways

> If you remember nothing else:
>
> * **`create-next-app --tailwind`** gives you a working setup in 30 seconds. The rest of this guide makes it production-ready
> * **Tailwind v4** replaces `tailwind.config.js` with CSS-first `@theme` configuration and automatic content detection
> * **shadcn/ui** is the standard component library for tailwind nextjs projects. Install it early
> * **next-themes** handles dark mode without hydration issues
> * **next/font** eliminates layout shift and self-hosts Google Fonts automatically
> * Production CSS output is **6-25 KB gzipped** with Tailwind v4

## Table of Contents

1. [Quick Start: 30-Second Setup](#quick-start-30-second-setup)
2. [Project Structure](#project-structure)
3. [Tailwind v4: What Changed](#tailwind-v4-what-changed)
4. [Custom Theme Configuration](#custom-theme-configuration)
5. [Adding Custom Fonts](#adding-custom-fonts)
6. [Dark Mode Setup](#dark-mode-setup)
7. [Installing shadcn/ui](#installing-shadcnui)
8. [Common Component Patterns](#common-component-patterns)
9. [Performance Optimization](#performance-optimization)
10. [VS Code Setup](#vs-code-setup)
11. [CSS Modules vs Tailwind](#css-modules-vs-tailwind)
12. [Troubleshooting Common Issues](#troubleshooting-common-issues)
13. [Conclusion](#conclusion)

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "HowTo",
  "name": "How to Set Up Tailwind CSS with Next.js",
  "description": "Step-by-step guide to setting up Tailwind CSS in a Next.js 15 project with App Router, dark mode, custom themes, and shadcn/ui.",
  "totalTime": "PT30M",
  "estimatedCost": {
    "@type": "MonetaryAmount",
    "currency": "USD",
    "value": "0"
  },
  "tool": [
    { "@type": "HowToTool", "name": "Node.js 18+" },
    { "@type": "HowToTool", "name": "VS Code" },
    { "@type": "HowToTool", "name": "Terminal" }
  ],
  "step": [
    {
      "@type": "HowToStep",
      "position": 1,
      "name": "Create a Next.js project with Tailwind",
      "text": "Run npx create-next-app@latest my-app --typescript --tailwind --eslint --app --src-dir to scaffold a Next.js 15 project with Tailwind CSS pre-configured."
    },
    {
      "@type": "HowToStep",
      "position": 2,
      "name": "Configure custom theme tokens",
      "text": "Edit src/app/globals.css to add custom colors, fonts, and spacing using the @theme directive for Tailwind v4 or theme.extend in tailwind.config.ts for v3."
    },
    {
      "@type": "HowToStep",
      "position": 3,
      "name": "Set up custom fonts with next/font",
      "text": "Import fonts from next/font/google in layout.tsx, assign to CSS variables, and reference in Tailwind theme configuration."
    },
    {
      "@type": "HowToStep",
      "position": 4,
      "name": "Install and configure dark mode",
      "text": "Install next-themes, wrap your app in ThemeProvider, and use Tailwind dark: variants for dark mode styles."
    },
    {
      "@type": "HowToStep",
      "position": 5,
      "name": "Add shadcn/ui components",
      "text": "Run npx shadcn-ui@latest init and add components like button, card, and form for accessible, Tailwind-styled UI primitives."
    },
    {
      "@type": "HowToStep",
      "position": 6,
      "name": "Optimize for production",
      "text": "Enable CSS optimization in next.config.js, verify bundle size with build output, and test Core Web Vitals."
    }
  ]
}
</script>

## Quick Start: 30-Second Setup

The fastest path to a working tailwind nextjs project:

```bash
npx create-next-app@latest my-app --typescript --tailwind --eslint --app --src-dir
cd my-app
npm run dev
```

That is it. `create-next-app` with the `--tailwind` flag configures everything: PostCSS, the Tailwind config file, `globals.css` with Tailwind directives, and an example page using utility classes.

Open `http://localhost:3000`. If you see styled content, Tailwind is working. The rest of this guide turns this default setup into a production-ready configuration.

## Project Structure

After scaffolding, your tailwind css nextjs project looks like this:

```
my-app/
├── src/
│   ├── app/
│   │   ├── globals.css        ← Tailwind directives live here
│   │   ├── layout.tsx         ← Root layout, font imports
│   │   └── page.tsx           ← Home page
│   └── components/
│       └── ui/                ← shadcn/ui components (added later)
├── public/
├── tailwind.config.ts         ← Theme customization (v3) or removed (v4)
├── postcss.config.mjs
├── next.config.ts
└── package.json
```

The key files for Tailwind configuration are `globals.css` (where Tailwind loads), `tailwind.config.ts` (where you customize the theme), and `layout.tsx` (where fonts and providers wrap the app).

## Tailwind v4: What Changed

Tailwind v4 is the biggest update since Tailwind's launch. If you are starting a new nextjs tailwind project in 2026, these changes matter.

### CSS-First Configuration

Tailwind v3 required a `tailwind.config.js` file for theme customization. Tailwind v4 moves configuration into CSS using the `@theme` directive:

```css
/* src/app/globals.css - Tailwind v4 */
@import "tailwindcss";

@theme {
  --color-primary: #1d4ed8;
  --color-primary-light: #3b82f6;
  --color-background: #ffffff;
  --color-foreground: #0a0a0a;
  --font-family-sans: 'Inter', ui-sans-serif, system-ui, sans-serif;
  --spacing-18: 4.5rem;
}
```

Compare this to Tailwind v3:

```css
/* src/app/globals.css - Tailwind v3 */
@tailwind base;
@tailwind components;
@tailwind utilities;
```

```typescript
// tailwind.config.ts - Tailwind v3
import type { Config } from 'tailwindcss'

export default {
  content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
  theme: {
    extend: {
      colors: {
        primary: '#1d4ed8',
      },
    },
  },
  plugins: [],
} satisfies Config
```

The v4 approach eliminates the config file entirely. One file instead of two. CSS instead of JavaScript. Design tokens as CSS custom properties that work everywhere, not just in Tailwind classes.

### Automatic Content Detection

Tailwind v3 required explicit `content` paths to know which files to scan for class names. Tailwind v4 detects content automatically. No configuration needed. No more "my styles are not applying because I forgot to add a path" debugging sessions.

### Smaller Output

Tailwind v4 produces production CSS roughly 70% smaller than v3. A typical nextjs tailwind project outputs 6-12 KB gzipped with v4 compared to 20-30 KB with v3. The native CSS engine replaces PostCSS, which also speeds up build times.

### Migration from v3

If you have an existing Tailwind v3 project, migration is straightforward:

1. Update Tailwind: `npm install tailwindcss@latest`
2. Replace `@tailwind base; @tailwind components; @tailwind utilities;` with `@import "tailwindcss";`
3. Move theme customizations from `tailwind.config.ts` into a `@theme` block in `globals.css`
4. Delete `tailwind.config.ts` (optional, v4 still reads it as a fallback)

Both v3 and v4 work with Next.js 15. Choose v4 for new projects. Keep v3 for existing projects until you are ready to migrate.

## Custom Theme Configuration

Every tailwind nextjs project needs custom design tokens. Here is how to set up colors, spacing, and typography for both Tailwind versions.

### Tailwind v4 (Recommended)

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

@theme {
  /* Brand Colors */
  --color-brand: #1d4ed8;
  --color-brand-light: #3b82f6;
  --color-brand-dark: #1e40af;

  /* Semantic Colors */
  --color-background: #ffffff;
  --color-foreground: #0a0a0a;
  --color-muted: #6b7280;
  --color-border: #e5e7eb;

  /* Spacing Scale Extensions */
  --spacing-18: 4.5rem;
  --spacing-22: 5.5rem;

  /* Border Radius */
  --radius-xl: 1rem;
  --radius-2xl: 1.5rem;
}
```

Usage in components: `bg-brand`, `text-muted`, `p-18`, `rounded-xl`.

### Tailwind v3

```typescript
// tailwind.config.ts
import type { Config } from 'tailwindcss'

export default {
  content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
  theme: {
    extend: {
      colors: {
        brand: {
          DEFAULT: '#1d4ed8',
          light: '#3b82f6',
          dark: '#1e40af',
        },
        background: '#ffffff',
        foreground: '#0a0a0a',
        muted: '#6b7280',
        border: '#e5e7eb',
      },
      spacing: {
        '18': '4.5rem',
        '22': '5.5rem',
      },
      borderRadius: {
        xl: '1rem',
        '2xl': '1.5rem',
      },
    },
  },
  plugins: [],
} satisfies Config
```

The theme tokens you define here become the design system for your entire application. Define them once, use them everywhere. This consistency is one of the strongest arguments for the tailwind css nextjs combination.

## Adding Custom Fonts

Next.js provides `next/font` for optimized font loading. Here is how to integrate it with Tailwind.

### Step 1: Import the Font

```tsx
// src/app/layout.tsx
import { Inter, JetBrains_Mono } from 'next/font/google'
import './globals.css'

const inter = Inter({
  subsets: ['latin'],
  variable: '--font-inter',
})

const jetbrainsMono = JetBrains_Mono({
  subsets: ['latin'],
  variable: '--font-mono',
})

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en" className={`${inter.variable} ${jetbrainsMono.variable}`}>
      <body className="font-sans antialiased">{children}</body>
    </html>
  )
}
```

### Step 2: Reference in Tailwind

**Tailwind v4:**
```css
@theme {
  --font-family-sans: var(--font-inter), ui-sans-serif, system-ui, sans-serif;
  --font-family-mono: var(--font-mono), ui-monospace, monospace;
}
```

**Tailwind v3:**
```typescript
// tailwind.config.ts
theme: {
  extend: {
    fontFamily: {
      sans: ['var(--font-inter)', 'ui-sans-serif', 'system-ui', 'sans-serif'],
      mono: ['var(--font-mono)', 'ui-monospace', 'monospace'],
    },
  },
},
```

Now `font-sans` uses Inter and `font-mono` uses JetBrains Mono. Fonts are self-hosted by Next.js, eliminating external network requests and preventing layout shift.

## Dark Mode Setup

Dark mode is expected in 2026. Here is the production-ready setup for tailwind next.js projects.

### Step 1: Install next-themes

```bash
npm install next-themes
```

### Step 2: Create a Theme Provider

```tsx
// src/components/theme-provider.tsx
'use client'

import { ThemeProvider as NextThemesProvider } from 'next-themes'
import { type ThemeProviderProps } from 'next-themes'

export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
  return <NextThemesProvider {...props}>{children}</NextThemesProvider>
}
```

### Step 3: Wrap Your Layout

```tsx
// src/app/layout.tsx
import { ThemeProvider } from '@/components/theme-provider'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <ThemeProvider
          attribute="class"
          defaultTheme="system"
          enableSystem
          disableTransitionOnChange
        >
          {children}
        </ThemeProvider>
      </body>
    </html>
  )
}
```

### Step 4: Use Dark Variants

```tsx
<div className="bg-white dark:bg-slate-900 text-slate-900 dark:text-white">
  <h1 className="text-2xl font-bold">This adapts to dark mode</h1>
  <p className="text-slate-600 dark:text-slate-400">So does this text</p>
</div>
```

The `suppressHydrationWarning` on `<html>` is required. Without it, the theme class applied by `next-themes` causes a hydration mismatch warning because the server does not know the user's theme preference.

### Step 5: Build a Theme Toggle

```tsx
// src/components/theme-toggle.tsx
'use client'

import { useTheme } from 'next-themes'

export function ThemeToggle() {
  const { theme, setTheme } = useTheme()

  return (
    <button
      onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
      className="rounded-lg border border-border px-3 py-2 text-sm hover:bg-muted/50 transition-colors"
    >
      {theme === 'dark' ? 'Light mode' : 'Dark mode'}
    </button>
  )
}
```

## Installing shadcn/ui

shadcn/ui is the standard component library for tailwind nextjs projects. It provides accessible, customizable components built on Radix UI primitives.

### Setup

```bash
npx shadcn-ui@latest init
```

The CLI prompts you for preferences:
- **Style:** Default or New York
- **Base color:** [Slate, Gray, Zinc, Neutral, or Stone](/tools/tailwind-colors#shadcn-base-colors)
- **CSS variables:** Yes (recommended)

### Adding Components

```bash
npx shadcn-ui@latest add button card input label form
```

Components are copied into `src/components/ui/`. You own the code. Customize freely. No npm dependency, no version lock-in.

### Why shadcn/ui Over Custom Components

Building accessible, keyboard-navigable, screen-reader-compatible components from scratch takes weeks per component. shadcn/ui provides that foundation with Tailwind styling you can modify. For SaaS projects, check our [best Next.js SaaS templates guide](/blog/best-nextjs-saas-templates) where most top templates use shadcn/ui as their component layer.

## Common Component Patterns

Here are the patterns you will use most often in a tailwind nextjs project.

Every tailwind nextjs application reuses the same foundational patterns. Here are the ones you will reach for daily.

### Responsive Container

```tsx
<main className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-8">
  {children}
</main>
```

### Card Grid

```tsx
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
  {items.map((item) => (
    <div
      key={item.id}
      className="rounded-2xl border border-border bg-white dark:bg-slate-900 p-6 shadow-sm hover:shadow-md transition-shadow"
    >
      <h3 className="text-lg font-semibold">{item.title}</h3>
      <p className="mt-2 text-sm text-muted">{item.description}</p>
    </div>
  ))}
</div>
```

### Responsive Navigation

```tsx
<nav className="flex items-center justify-between px-6 py-4 border-b border-border">
  <div className="text-xl font-bold">Logo</div>
  <div className="hidden md:flex items-center gap-6">
    <a href="#" className="text-sm text-muted hover:text-foreground transition-colors">Features</a>
    <a href="#" className="text-sm text-muted hover:text-foreground transition-colors">Pricing</a>
    <a href="#" className="text-sm text-muted hover:text-foreground transition-colors">Docs</a>
  </div>
  <button className="md:hidden">Menu</button>
</nav>
```

### Form with Validation States

```tsx
<div className="space-y-2">
  <label className="text-sm font-medium" htmlFor="email">Email</label>
  <input
    id="email"
    type="email"
    className="w-full rounded-lg border border-border bg-background px-4 py-2 text-sm
               placeholder:text-muted focus:outline-none focus:ring-2 focus:ring-brand
               disabled:cursor-not-allowed disabled:opacity-50"
    placeholder="you@example.com"
  />
  <p className="text-sm text-red-500">Please enter a valid email address</p>
</div>
```

These patterns form the building blocks for any application. For more component examples, our [Next.js templates guide](/blog/nextjs-templates) shows how production templates implement these patterns at scale.

## Performance Optimization

Tailwind CSS is already optimized by default. These additional steps squeeze out the last bit of performance for production.

### Enable CSS Optimization

```typescript
// next.config.ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  experimental: {
    optimizeCss: true,
  },
}

export default nextConfig
```

This enables Critters-based CSS inlining, which extracts critical CSS and inlines it in the HTML head for faster first paint.

### Production Bundle Size

Tailwind v4 output size by project scale:

| Project Size | Production CSS (gzipped) |
|-------------|-------------------------|
| Empty project | ~6 KB |
| Small app (20 components) | ~10-12 KB |
| Medium app (50+ components) | ~15-20 KB |
| Large app (100+ components) | ~20-25 KB |

For comparison, a single unoptimized CSS framework like Bootstrap is 22 KB gzipped. Your entire Tailwind project will likely produce less CSS than that.

### Avoid Dynamic Class Names

Tailwind scans your source code for class names at build time. Dynamically constructed class names break this scan:

```tsx
// BAD: Tailwind cannot detect this class
const color = 'red'
<div className={`bg-${color}-500`} />

// GOOD: Use complete class names
<div className={isError ? 'bg-red-500' : 'bg-green-500'} />
```

Use complete class strings. Map conditions to full class names. This is the most common source of "my styles are not working" bugs in nextjs tailwind projects.

## VS Code Setup

Install the **Tailwind CSS IntelliSense** extension for autocomplete, linting, and color previews.

Add to `.vscode/settings.json`:

```json
{
  "tailwindCSS.experimental.classRegex": [
    "cn\\(([^)]*)\\)",
    "cva\\(([^)]*)\\)"
  ],
  "tailwindCSS.includeLanguages": {
    "typescript": "html",
    "typescriptreact": "html"
  },
  "editor.quickSuggestions": {
    "strings": "on"
  }
}
```

The `classRegex` patterns enable IntelliSense inside `cn()` (shadcn/ui's class merge utility) and `cva()` (class variance authority). Without these, autocomplete only works in `className` props.

## CSS Modules vs Tailwind

Both CSS Modules and Tailwind work in Next.js. Here is when to use each in a nextjs tailwind project.

| Use Tailwind When | Use CSS Modules When |
|-------------------|---------------------|
| Building new components | Complex keyframe animations |
| Rapid prototyping | Overriding third-party component styles |
| Team needs consistent styling | Migrating legacy CSS |
| Component styles are utility-heavy | Styles require dynamic calc() or advanced selectors |

**The hybrid approach works.** Use Tailwind for 95% of your styling. Use CSS Modules for the edge cases where utility classes are awkward: complex animations, pseudo-element content, and deeply nested selector overrides.

```tsx
// Hybrid: Tailwind for layout, CSS Module for animation
import styles from './hero.module.css'

export function Hero() {
  return (
    <section className="relative min-h-screen flex items-center justify-center px-8">
      <div className={styles.floatingGradient} />
      <h1 className="text-5xl font-bold text-foreground relative z-10">
        Ship faster
      </h1>
    </section>
  )
}
```

## Troubleshooting Common Issues

These are the issues developers hit most often when setting up tailwind css nextjs for the first time.

### Styles Not Applying

**Cause:** Tailwind directives missing from `globals.css` or `globals.css` not imported in `layout.tsx`.

**Fix:** Verify `globals.css` contains either `@import "tailwindcss";` (v4) or `@tailwind base; @tailwind components; @tailwind utilities;` (v3). Verify `layout.tsx` imports `./globals.css`. Restart the dev server.

### Hydration Mismatch Warning

**Cause:** `next-themes` applies a class to `<html>` on the client that does not match the server-rendered HTML.

**Fix:** Add `suppressHydrationWarning` to the `<html>` element in `layout.tsx`.

### Dark Mode Flash on Load

**Cause:** The theme is determined client-side, causing a flash of the wrong theme.

**Fix:** Use `next-themes` with `defaultTheme="system"` and `enableSystem`. The library injects a script that applies the theme before React hydrates, eliminating the flash.

### Classes Not Purged in Production

**Cause:** Dynamic class names that Tailwind cannot detect at build time.

**Fix:** Use complete class strings instead of string interpolation. Map dynamic values to complete class names using objects or conditional expressions.

### shadcn/ui Components Unstyled

**Cause:** `shadcn-ui init` was run before Tailwind was configured, or the CSS variables are missing.

**Fix:** Re-run `npx shadcn-ui@latest init` after verifying Tailwind works. Check that `globals.css` includes the CSS variables shadcn/ui generates during initialization.

## The Tailwind Next.js Stack in 2026

The tailwind nextjs ecosystem has consolidated around a standard stack. Here is what most production projects use.

| Layer | Standard Choice | Alternative |
|-------|----------------|-------------|
| **Framework** | Next.js 15 (App Router) | See [Next.js vs React](/blog/nextjs-vs-react) |
| **Styling** | Tailwind CSS v4 | CSS Modules (hybrid) |
| **Components** | shadcn/ui + Radix | Headless UI |
| **Class Utilities** | clsx + tailwind-merge (cn) | cva for variants |
| **Dark Mode** | next-themes | Manual class toggle |
| **Fonts** | next/font (self-hosted) | Google Fonts CDN |
| **ORM** | [Prisma or Drizzle](/blog/prisma-vs-drizzle) | - |
| **Hosting** | [Vercel or Railway](/blog/vercel-vs-railway) | Render, Netlify |

This stack powers the majority of new Next.js applications. If you are building a SaaS, most [Next.js SaaS templates](/blog/best-nextjs-saas-templates) ship with this exact configuration.

{{ partial:cta/forge }}

## Conclusion

Setting up tailwind nextjs is a 30-second task. Making it production-ready takes 30 minutes. The steps covered in this guide, custom theme tokens, font optimization, dark mode, shadcn/ui, and performance tuning, transform the default scaffold into a professional foundation.

Here is your setup checklist:

1. **Scaffold:** `create-next-app --tailwind --typescript --app --src-dir`
2. **Theme:** Define custom colors, spacing, and typography in `globals.css` (v4) or `tailwind.config.ts` (v3)
3. **Fonts:** Load via `next/font`, assign CSS variables, reference in Tailwind theme
4. **Dark mode:** Install `next-themes`, wrap in `ThemeProvider`, use `dark:` variants
5. **Components:** Run `shadcn-ui init`, add components as needed
6. **VS Code:** Install Tailwind IntelliSense, configure `classRegex` for `cn()` support
7. **Production:** Enable `optimizeCss`, verify bundle size, avoid dynamic class names

The tailwind css nextjs combination works because both tools share the same philosophy: provide sensible defaults, get out of the way, and let developers build. Tailwind handles styling. Next.js handles rendering and routing. shadcn/ui handles components. Together, they cover everything between your idea and a deployed application.

For the broader development ecosystem around your Next.js project, our [complete SaaS tools guide](/blog/saas-tools-for-startups) covers the full stack from payments to analytics.

---

## Related Resources

- [Tailwind CSS 4 Migration: What Changed & How to Upgrade](/blog/tailwind-4-migration)
- [Best Tailwind Component Libraries](/blog/best-tailwind-component-libraries)
- [shadcn Dashboard Tutorial](/blog/shadcn-dashboard-tutorial)
- [Best shadcn Templates](/blog/best-shadcn-templates)
- [Best Next.js Boilerplates](/blog/best-nextjs-boilerplates)
