Stars
Next.js and Tailwind CSS 2025 Guide: Setup, Tips, and Best Practices
calendar27 Mar 2025
read14 minute read

Next.js and Tailwind CSS 2025 Guide: Setup, Tips, and Best Practices

Introduction: Using Tailwind with Next.js in 2025

In 2025, Tailwind CSS and Next.js continue to lead the way in modern web development. Tailwind’s utility-first approach makes styling fast and consistent, while Next—with its App Router and React Server Components—offers powerful tools for building scalable, performant apps.

This guide is tailored for junior to intermediate developers looking to build clean, responsive UIs using this stack. From setup and layout styling to dark mode, performance tips, and project structure—you’ll learn everything you need to confidently use Tailwind with NextJs in your next project.

Setting Up Tailwind CSS in a Next.js Project

Before diving deep into the advanced techniques of using Tailwind CSS with Next, let's first quickly set up a modern Next.js project integrated seamlessly with Tailwind.

Step 1: Creating a Next.js App

Begin by initializing a new Next application. We'll use the official Next.js CLI to simplify the setup. Run the following command in your terminal:

npx create-next-app@latest my-next-app --typescript --eslint --app
cd my-next-app

Here, we've chosen TypeScript (--typescript), ESLint for code quality (--eslint), and the App Router (--app) introduced in Next 13+.

Step 2: Installing Tailwind CSS

Tailwind CSS (now at version 4) simplifies installation with minimal dependencies. Run the following command in your project's root directory:

npm install tailwindcss @tailwindcss/postcss postcss

This single command installs everything necessary to integrate Tailwind into your Next project.

Step 3: Configuring Tailwind and PostCSS

Next, generate the default Tailwind configuration:

npx tailwindcss init

This creates a tailwind.config.js file. Configure it to scan your project files for Tailwind classes:

// tailwind.config.js
module.exports = {
  content: [
    "./app/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};

Then create a postcss.config.mjs file in the root directory to ensure Tailwind compiles correctly:

// postcss.config.mjs
export default {
  plugins: {
    '@tailwindcss/postcss': {},
  },
};

Step 4: Including Tailwind Styles

Create a global CSS file (usually located at app/globals.css) and import Tailwind CSS:

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

Finally, include this global stylesheet in your root layout component (app/layout.tsx):

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

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

Step 5: Verifying the Setup

Let's quickly verify the setup. Modify your homepage (app/page.tsx) to use Tailwind classes:

// app/page.tsx
export default function Page() {
  return (
    <div className="flex h-screen items-center justify-center bg-gray-100">
      <h1 className="text-3xl font-bold text-blue-600">Hello, Tailwind with Nextjs!</h1>
    </div>
  );
}

Now, run the development server:

npm run dev

Visit http://localhost:3000. You should see a centered heading styled beautifully with Tailwind's utility classes.

Key Takeaways:

  • Tailwind v4 has simplified integration with Next, requiring fewer configuration steps.
  • Always ensure your tailwind.config.js scans the correct directories for Tailwind classes.
  • Global CSS imports in layout.tsx ensure consistent styling across your entire application.

With this solid foundation set up, we're now ready to explore how Tailwind integrates with the Next App Router and React Server Components.

Tailwind CSS in the Next.js App Router and Server Components

With Tailwind CSS successfully integrated into your Next project, let’s explore how it naturally complements Next.js’s App Router architecture and its powerful React Server Components introduced in recent updates.

Understanding the App Router in Next.js

In 2025, Next.js leverages the App Router as its primary routing system. Unlike the traditional Pages Router, the App Router encourages developers to structure apps around a nested layout model using server components by default.

The key benefit of this architecture is better performance due to more server-side rendering (SSR) and streamlined data fetching. Tailwind CSS fits perfectly here, as it applies styles directly via class names, making styling predictable, modular, and efficient.

Styling React Server Components with Tailwind

React Server Components (RSC) are rendered on the server and delivered as static HTML. Since Tailwind applies styles through static class names rather than dynamic JavaScript interactions, it's fully compatible with server components.

Here’s a visual breakdown of the process:

Next.js App Router Rendering Flow (Diagram Suggestion):

User Request
    ↓
Next.js Server → Server Components (JSX + Tailwind classes)
    ↓
Tailwind CSS compiled at build-time (only used classes included)
    ↓
Optimized HTML + Single CSS bundle sent to client
    ↓
Browser caches CSS; Fast subsequent page loads

Practical Example: Server Components with Tailwind

Let’s create a simple yet practical server-rendered component styled with Tailwind.

In your project, add a new component at components/UserCard.tsx:

// components/UserCard.tsx
export default function UserCard({ name, role }: { name: string; role: string }) {
  return (
    <div className="rounded-lg border p-4 shadow-sm">
      <h2 className="text-lg font-semibold text-gray-800">{name}</h2>
      <p className="text-sm text-gray-500">{role}</p>
    </div>
  );
}

Now use this server component in your main page (app/page.tsx):

// app/page.tsx
import UserCard from "@/components/UserCard";

export default function Page() {
  return (
    <div className="flex min-h-screen flex-col items-center justify-center bg-gray-50 p-4">
      <UserCard name="Alex Johnson" role="Frontend Developer" />
    </div>
  );
}

Because this component is server-rendered by default (no "use client" directive), Next.js pre-renders the styled HTML on the server, delivering a quick and responsive experience without any JavaScript overhead on the client.

Why Tailwind Works So Well with Server Components:

  • Static Styling: Tailwind’s utility-first classes generate styles at build time, resulting in extremely efficient and predictable CSS.
  • No Runtime Cost: Because classes are resolved at compile-time, server components don’t incur additional JavaScript costs for styling.
  • Consistency and Caching: With a single optimized CSS bundle shared across the app, users experience fast load times and consistent styles across routes.

Key Takeaways:

  • Tailwind integrates seamlessly into Next’s App Router and React Server Components, enhancing both performance and developer experience.
  • Using Tailwind with Server Components helps maintain clarity and performance, eliminating unnecessary JavaScript styling overhead.
  • Tailwind's approach aligns naturally with Next's server-side rendering strategy, delivering optimal performance out-of-the-box.

Next, we'll explore best practices when using Tailwind CSS in your Next projects with TypeScript, providing you with practical examples and development tips.

Integrating Tailwind with TypeScript in Next (The Easy Way!)

Now that you're comfortable using Tailwind in your Next project, let's explore how to smoothly combine it with TypeScript. Using TypeScript with Tailwind not only gives you better tooling and fewer bugs, but it also streamlines your frontend workflow. Let's walk through this process together—step-by-step, in a conversational style.

Why TypeScript with Tailwind?

If you're like most developers in 2025, you're probably already using TypeScript for its safety and clarity. But how does it fit with Tailwind?

The good news: Tailwind CSS and TypeScript are a natural fit. Tailwind doesn't require any special configuration just for TypeScript, but there are still a few helpful tips to make the most of this powerful combo.

Making Your Editor Help You

First things first—enable Tailwind IntelliSense in your editor (especially if you’re using VS Code). This little extension auto-suggests Tailwind class names as you type. It catches typos instantly, saving you from the frustration of missing styles later.

Imagine typing bg- and instantly seeing all your color options. It's like having a friendly assistant right there with you.

Clean, Conditional Classes (Without the Mess)

We often need dynamic or conditional styling based on user interactions or data. Here's an easy, readable way to handle these situations with Tailwind in TypeScript:

import clsx from "clsx";

function Notification({ success, message }: { success: boolean; message: string }) {
  return (
    <div
      className={clsx(
        "rounded p-4 text-sm",
        success ? "bg-green-100 text-green-700" : "bg-red-100 text-red-700"
      )}
    >
      {message}
    </div>
  );
}

This approach makes your code easy to understand at a glance—no more messy string concatenations!

Easily Override Tailwind Classes

Sometimes you might want to tweak styles for a particular instance of a reusable component. Instead of wrestling with conflicting Tailwind classes, try using a small helper called tailwind-merge. Here's how it works in a real-world component:

import { twMerge } from "tailwind-merge";

function Button({ className, children }: { className?: string; children: React.ReactNode }) {
  return (
    <button className={twMerge("rounded bg-blue-600 px-4 py-2 text-white", className)}>
      {children}
    </button>
  );
}

This setup lets you pass additional classes to your components without worrying about conflicting styles. Easy, right?

Keeping Things Simple (And Human-Friendly)

You might feel tempted to separate your CSS into multiple files to organize things. But the beauty of Tailwind—and a key reason developers love it—is that styling stays right next to your components. Keeping your styles inline with your JSX helps maintain clarity and makes debugging quicker.

Here's a quick example:

<div className="rounded-lg p-6 shadow-md">
  <h2 className="mb-2 text-xl font-semibold">Inline and Clear!</h2>
  <p className="text-gray-600">Everything you need, all in one place.</p>
</div>

Your JSX clearly communicates exactly how your UI will look—without needing to search through multiple CSS files.

What to Avoid: Dynamic Class Pitfalls

Tailwind analyzes your code at build time. That means very dynamic class generation, like bg-${color}-500, won't work because Tailwind won't detect it. Instead, explicitly list out your possible classes:

// 🚫 Problematic:
<div className={`bg-${color}-500`} />

// ✅ Better approach:
<div className={clsx(color === "red" && "bg-red-500", color === "blue" && "bg-blue-500")} />

Using Tailwind and TypeScript together doesn't have to be complicated. Just keep styles close to your JSX, leverage handy helpers like clsx and tailwind-merge, and let your editor's IntelliSense guide you. With these tips, your development experience will feel smooth and natural.

Now, let's tackle another exciting topic: implementing Dark Mode with Tailwind in Next.js.

Adding Dark Mode to Your Next.js App with Tailwind

Dark mode isn’t just a nice-to-have anymore—it’s something users expect. And with Tailwind CSS, implementing it in a Next project is surprisingly simple.

Let’s walk through how to enable dark mode the modern way in 2025, using Tailwind’s built-in utilities and a little React magic for toggling themes.

Step 1: Decide How You Want Dark Mode to Work

Tailwind supports two strategies for dark mode:

  • Media-based (default): Follows the user’s system settings (dark or light).
  • Class-based: You manually toggle dark mode by adding a dark class to a parent element (usually <html>).

We’ll go with the class-based approach because it gives us full control—and works great with Next.

Step 2: Configure Tailwind for Class-Based Dark Mode

In your tailwind.config.js, enable class-based dark mode:

// tailwind.config.js
module.exports = {
  darkMode: "class",
  content: [
    "./app/**/*.{ts,tsx}",
    "./components/**/*.{ts,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};

This tells Tailwind to apply dark: styles only when the dark class is present on a parent element.

Step 3: Create a Theme Toggle

To let users switch between light and dark modes, we’ll build a simple toggle using React.

First, create a client component:

// components/ThemeToggle.tsx
"use client";

import { useEffect, useState } from "react";

export default function ThemeToggle() {
  const [isDark, setIsDark] = useState(false);

  useEffect(() => {
    const root = window.document.documentElement;
    const stored = localStorage.getItem("theme");

    if (stored === "dark") {
      root.classList.add("dark");
      setIsDark(true);
    } else {
      root.classList.remove("dark");
    }
  }, []);

  const toggleTheme = () => {
    const root = window.document.documentElement;
    const isDarkMode = root.classList.contains("dark");

    if (isDarkMode) {
      root.classList.remove("dark");
      localStorage.setItem("theme", "light");
      setIsDark(false);
    } else {
      root.classList.add("dark");
      localStorage.setItem("theme", "dark");
      setIsDark(true);
    }
  };

  return (
    <button
      onClick={toggleTheme}
      className="rounded bg-gray-200 px-4 py-2 text-sm dark:bg-gray-800 dark:text-white"
    >
      {isDark ? "☀️ Light Mode" : "🌙 Dark Mode"}
    </button>
  );
}

This button toggles the dark class on the <html> tag and stores the user's preference in localStorage.

Step 4: Use Dark Mode Styles in Your Components

Once enabled, you can use dark: variants in your Tailwind classes to style your app accordingly:

<div className="min-h-screen bg-white text-black dark:bg-gray-900 dark:text-white">
  <h1 className="text-3xl font-bold">Welcome to Dark Mode</h1>
</div>

Tailwind will automatically generate the necessary CSS, and your UI will respond instantly when the theme is toggled.

(Optional) Step 5: Show Toggle on Every Page

To make dark mode available everywhere, place the ThemeToggle component inside your layout.tsx or a persistent navigation bar.

// app/layout.tsx
import "./globals.css";
import ThemeToggle from "@/components/ThemeToggle";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body className="transition-colors duration-300">
        <ThemeToggle />
        {children}
      </body>
    </html>
  );
}

This adds a smooth transition and makes switching between themes a polished experience.

Key Takeaways

  • Tailwind makes dark mode styling easy with dark: variants.
  • Using class-based dark mode gives you full control and works seamlessly with Next.js.
  • Persisting the user’s theme in localStorage ensures a consistent experience across sessions.

🚀 Optimizing Tailwind in Next.js for Production

Tailwind CSS is fast out of the box, but here are a few quick tips to make sure your Next.js app stays lean and snappy in production:

✅ Use Tailwind’s JIT Compiler

Tailwind includes only the classes you use. Just ensure your tailwind.config.js has accurate content paths:

content: [
  "./app/**/*.{ts,tsx}",
  "./components/**/*.{ts,tsx}",
]

⚠️ Avoid Dynamic Class Names

Don’t do this:

<div className={`bg-${color}-500`} />

Instead, use clsx or hardcoded conditionals so Tailwind can detect them.

🧠 Stick to One Shared CSS File

Next.js handles one CSS bundle best—no need to split styles by route. It’s faster due to caching and fewer network requests.

🎯 Optimize Fonts & Images

Use next/font and next/image for better performance—pairs well with Tailwind’s utility classes.

🧱 Structuring Tailwind for a Scalable Codebase

As your Next.js project grows, so will your styles. Tailwind keeps things maintainable, but a little structure goes a long way. Here’s how to keep your styling clean and scalable:

📁 1. Organize Your Components

Group related UI pieces under folders like:

components/
├── ui/
│   └── Button.tsx
├── layout/
│   └── Navbar.tsx

Keep utility classes inside the JSX unless they become repetitive.

🪄 2. Use @apply for Reusable Styles

Got the same set of classes everywhere? Abstract them in your global CSS:

/* app/globals.css */
.btn {
  @apply px-4 py-2 rounded font-medium bg-blue-600 text-white hover:bg-blue-700;
}

Then use it like:

<button className="btn">Click me</button>

Great for buttons, cards, badges, etc.

🎨 3. Customize the Theme

In tailwind.config.js, define your own color palette, spacing, or fonts:

theme: {
  extend: {
    colors: {
      brand: "#1e40af",
    },
  },
}

Use semantic class names like bg-brand to stay consistent across the app.

🏁 Conclusion

Using Tailwind CSS with Next.js in 2025 gives you a modern, efficient, and scalable frontend workflow. With minimal setup, you can take advantage of utility-first styling, the powerful App Router, and features like server components and dark mode—all while keeping your codebase clean and maintainable.

Whether you're just starting out or scaling a production app, this stack empowers you to move fast without sacrificing structure or performance.

Looking to go further? Explore the official docs for deeper insights:

Now you’re ready to build fast, beautiful UIs the smart way—with Tailwind and Next.js.

Code Icon
Fasttrack Frontend
Development using CodeParrot AI
Background
CodeParrot Logo

CodeParrot

Ship stunning UI Lightning Fast

Y Combinator