Back to Blog

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

DesignRevision Editorial DesignRevision Editorial · SaaS, frontend & developer tooling
Updated July 2, 2026 17 min read
Human Written
Share:

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
  2. Project Structure
  3. Tailwind v4: What Changed
  4. Custom Theme Configuration
  5. Adding Custom Fonts
  6. Dark Mode Setup
  7. Installing shadcn/ui
  8. Common Component Patterns
  9. Performance Optimization
  10. VS Code Setup
  11. CSS Modules vs Tailwind
  12. Troubleshooting Common Issues
  13. Conclusion

Quick Start: 30-Second Setup

The fastest path to a working tailwind nextjs project:

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:

/* 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:

/* src/app/globals.css - Tailwind v3 */
@tailwind base;
@tailwind components;
@tailwind utilities;
// 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)

/* 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

// 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

// 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:

@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:

// 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

npm install next-themes

Step 2: Create a Theme Provider

// 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

// 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

<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

// 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

npx shadcn-ui@latest init

The CLI prompts you for preferences:

Adding Components

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 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

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

Card Grid

<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

<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

<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="[email protected]"
  />
  <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 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

// 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:

// 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:

{
  "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.

// 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
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 -
Hosting Vercel or Railway Render, Netlify

This stack powers the majority of new Next.js applications. If you are building a SaaS, most Next.js SaaS templates ship with this exact configuration.

Ship apps faster with AI

Generate production-ready Next.js apps from a prompt. Full code ownership, deploy anywhere, stunning design output.

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 covers the full stack from payments to analytics.


Related Resources

Frequently Asked Questions

Yes. Tailwind CSS is the most popular styling approach for Next.js projects in 2026, used by over 70 percent of Next.js applications. Next.js includes first-class Tailwind support with automatic configuration through create-next-app. The utility-first approach works especially well with React components because styles are colocated with markup, eliminating context switching between files. Tailwind with Next.js also produces smaller CSS bundles than most CSS-in-JS alternatives because unused utilities are automatically removed at build time.

Both Tailwind CSS v3 and v4 work with Next.js 15. Tailwind v3 remains the stable default when you run create-next-app with the tailwind flag. Tailwind v4 introduces CSS-first configuration with the @theme directive, automatic content detection, and smaller bundle sizes. For new projects in 2026, Tailwind v4 is recommended for its performance improvements and simpler configuration. Existing v3 projects continue working without changes.

Use Tailwind for most projects. It provides faster development, consistent design tokens, and smaller production bundles. Use CSS Modules when you need complex CSS animations, when overriding third-party component styles, or when migrating a legacy CSS codebase. The two approaches work together in the same Next.js project. Use Tailwind for layout and component styling. Use CSS Modules for the rare cases where utility classes are insufficient.

Use the next/font package to load Google Fonts or local fonts in your root layout.tsx. Assign the font to a CSS variable using the variable property. Then reference that variable in your Tailwind theme configuration using font-family custom. This approach ensures fonts are self-hosted, eliminates layout shift, and integrates cleanly with Tailwind utility classes like font-custom throughout your components.

No. Tailwind v4 with its native CSS engine generates only the classes you use, producing production CSS of 6 to 25 KB gzipped depending on project size. Build time impact is negligible. In benchmarks, Tailwind adds less than 5 seconds to a Next.js production build. The JIT compiler generates utilities on demand during development, so the dev server starts and hot-reloads in milliseconds regardless of how many Tailwind classes exist in the framework.

Install the next-themes library. Wrap your application in a ThemeProvider component with the attribute set to class or data-mode. Add suppressHydrationWarning to your html tag to prevent hydration mismatches. Then use Tailwind dark mode variants like dark:bg-slate-900 and dark:text-white throughout your components. The class strategy is recommended because it gives you programmatic control over the theme, allowing you to build a toggle button and respect user preferences.

Use shadcn/ui for most projects. It provides accessible, well-tested components built on Radix UI primitives and styled with Tailwind. Unlike a component library, shadcn/ui copies components into your project, giving you full ownership and customization control. Build custom components only for UI patterns that shadcn/ui does not cover or when your design system requires fundamentally different component behavior.

Tailwind v4 replaces the JavaScript-based tailwind.config.js with CSS-first configuration using the @theme directive. Content detection is automatic, eliminating the need for content paths in configuration. The native CSS engine replaces PostCSS for faster builds. Production CSS is roughly 70 percent smaller than v3 output. All design tokens are exposed as CSS custom properties. The @import tailwindcss directive replaces the @tailwind base, components, and utilities directives from v3.

RevKit

Next.js SaaS Starter Kit

Pre-built auth, billing, and dashboard. Launch your SaaS in days, not weeks.

200+ devs shipped with RevKit
Get RevKit
Next.js 15 Supabase Stripe TypeScript

Join 50k+ subscribers

Web dev, SaaS, growth & marketing. Weekly.

Thanks for subscribing! Check your email.

No spam, unsubscribe anytime.