I have rebuilt the same Tailwind CSS and Next.js stack a handful of times over the past year. Each time I discover a small tweak that makes the developer experience smoother. The good news is that the official tooling has caught up. We no longer need custom Babel plugins, extra webpack hacks, or handwritten purge lists. In this post I will share the exact setup I now reach for when I want a clean, modern Tailwind project in Next.js 14.

Start with a fresh project

Kick things off with a new App Router project. I like the defaults from the starter because they wire up TypeScript, ESLint, and the test scaffolding for you:

pnpm create next-app@latest my-tailwind-app --typescript --eslint --app
cd my-tailwind-app

When the CLI finishes, remove the example styles so Tailwind can take over. Delete globals.css and the home page styles if you want a blank canvas.

Install and initialize Tailwind CSS

Next add the Tailwind packages and generate the starter config:

pnpm add -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

The -p flag creates both tailwind.config.ts and postcss.config.js. I usually replace the generated content with a minimal config that leans on the App Router folder structure:

// tailwind.config.ts
import type { Config } from 'tailwindcss'
 
const config: Config = {
  content: [
    './app/**/*.{ts,tsx,mdx}',
    './components/**/*.{ts,tsx,mdx}',
    './pages/**/*.{ts,tsx,mdx}',
  ],
  theme: {
    extend: {},
  },
  plugins: [require('@tailwindcss/typography')],
}
 
export default config

Adding the typography plugin keeps blog posts and rich text blocks looking polished without extra work.

Wire Tailwind into the global stylesheet

Create a new app/globals.css file with the Tailwind directives and any base styles you need:

@tailwind base;
@tailwind components;
@tailwind utilities;
 
:root {
  color-scheme: dark;
}
 
body {
  @apply bg-black text-zinc-100 font-sans;
}

In the root layout import the stylesheet and apply your font classes. I like pairing Inter for the main text with JetBrains Mono for code snippets:

// app/layout.tsx
import type { Metadata } from 'next'
import { Inter, JetBrains_Mono } from 'next/font/google'
import './globals.css'
 
const inter = Inter({ subsets: ['latin'], variable: '--font-inter' })
const jetbrains = JetBrains_Mono({ subsets: ['latin'], variable: '--font-jetbrains' })
 
export const metadata: Metadata = {
  title: 'My Tailwind Starter',
  description: 'A modern Tailwind CSS and Next.js setup.',
}
 
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" className={`${inter.variable} ${jetbrains.variable}`}>
      <body className="font-sans">{children}</body>
    </html>
  )
}

The font variables let you reference the same stacks inside Tailwind classes or custom CSS when needed.

Build a starter page

At this point I like to confirm everything works with a simple layout. Replace the default app/page.tsx with something that leans on Tailwind utilities and the typography plugin:

export default function HomePage() {
  return (
    <main className="mx-auto flex min-h-screen max-w-3xl flex-col gap-8 px-6 py-16">
      <section className="space-y-3">
        <h1 className="text-4xl font-semibold">Modern Tailwind CSS with Next.js</h1>
        <p className="text-lg text-zinc-400">
          This starter keeps the tooling light while giving you the niceties that ship with Tailwind 3.4 and the Next.js App Router.
        </p>
      </section>
 
      <article className="prose prose-invert">
        <h2>Why this combo works</h2>
        <p>
          The Next.js compiler understands Tailwind out of the box, so there is no custom Babel config to maintain. PostCSS handles the heavy lifting and the new `content` glob keeps tree shaking accurate even when you add MDX files.
        </p>
        <p>
          The App Router also makes it easy to colocate styles with your routes. I often drop component specific CSS Modules entirely because Tailwind utilities and the `@apply` directive cover most needs.
        </p>
      </article>
 
      <section className="rounded-xl border border-zinc-800 bg-zinc-950 p-6">
        <h2 className="text-2xl font-medium">Next steps</h2>
        <ul className="mt-4 space-y-2 text-sm text-zinc-300">
          <li>Add design tokens or custom colors inside `tailwind.config.ts`.</li>
          <li>Pull in component primitives like Radix UI or Headless UI if you want accessible interactions.</li>
          <li>Wire up MDX routes to take advantage of the typography plugin for long form writing.</li>
        </ul>
      </section>
    </main>
  )
}

When you run pnpm dev you should see the new fonts, the dark background, and the typography styles without touching a single config override.

Ship it

That is all you need for a production ready Tailwind and Next.js base. From here you can layer on design tokens, add a component library, or plug in CMS content without fighting the framework. I keep this setup bookmarked because it balances speed with long term maintainability. I hope it helps you ship your next idea faster.