Next.js Tailwind CSS
Introduction
Tailwind CSS has emerged as one of the most popular utility-first CSS frameworks in modern web development. When paired with Next.js, it creates a powerful combination that allows developers to build beautifully styled, responsive applications with minimal effort. In this guide, we'll explore how to integrate Tailwind CSS with Next.js and leverage its utility classes to create stunning user interfaces.
Unlike traditional CSS frameworks that provide pre-designed components, Tailwind CSS gives you low-level utility classes that let you build completely custom designs without ever leaving your HTML (or JSX in our case). This approach provides more flexibility and results in smaller CSS bundles compared to component-based frameworks.
Setting Up Tailwind CSS in Next.js
Setting up Tailwind CSS in a Next.js project is straightforward thanks to the official Tailwind CSS integration. Let's walk through the steps.
Step 1: Create a Next.js project
If you don't already have a Next.js project, create one:
npx create-next-app@latest my-tailwind-app
cd my-tailwind-app
When prompted with configuration options, you can say "yes" to using Tailwind CSS. If you do this, you can skip steps 2 and 3 as the setup will be done automatically!
Step 2: Install Tailwind CSS and its dependencies
If you need to add Tailwind to an existing project, install Tailwind CSS and its peer dependencies:
npm install -D tailwindcss postcss autoprefixer
Step 3: Initialize Tailwind CSS
Generate the configuration files:
npx tailwindcss init -p
This creates two files:
tailwind.config.js
: Configuration for Tailwindpostcss.config.js
: PostCSS configuration (which includes Tailwind)
Step 4: Configure your template paths
Update your tailwind.config.js
file to include the paths to all your template files:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./app/**/*.{js,ts,jsx,tsx,mdx}",
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {},
},
plugins: [],
}
Step 5: Add the Tailwind directives to your CSS
In your app/globals.css
or styles/globals.css
file, add the Tailwind directives:
@tailwind base;
@tailwind components;
@tailwind utilities;
Step 6: Import the CSS file
For the App Router, this should be done automatically in your app/layout.js
or app/layout.tsx
file:
import './globals.css'
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
For the Pages Router, import it in your pages/_app.js
or pages/_app.tsx
:
import '@/styles/globals.css'
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
That's it! You can now start using Tailwind CSS in your Next.js project.
Basic Usage of Tailwind CSS in Next.js
Let's create a simple page using Tailwind CSS utility classes:
// app/page.js (App Router) or pages/index.js (Pages Router)
export default function Home() {
return (
<div className="min-h-screen bg-gray-100 flex items-center justify-center">
<div className="max-w-md w-full bg-white p-8 rounded-lg shadow-lg">
<h1 className="text-3xl font-bold text-center text-blue-600 mb-4">
Hello, Tailwind CSS!
</h1>
<p className="text-gray-700 mb-6">
This is a simple example of using Tailwind CSS in a Next.js application.
Notice how we're applying styles directly in the className attribute.
</p>
<button className="w-full bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline transition duration-300">
Click me!
</button>
</div>
</div>
)
}
Here's what the above code creates:
- A full-height, light gray background
- A centered white card with padding and shadow
- A blue heading
- A paragraph with gray text
- A blue button that darkens on hover and has transition effects
Responsive Design with Tailwind CSS
Tailwind makes responsive design incredibly easy with its built-in breakpoint prefixes:
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div className="bg-white p-4 rounded shadow">Card 1</div>
<div className="bg-white p-4 rounded shadow">Card 2</div>
<div className="bg-white p-4 rounded shadow">Card 3</div>
</div>
In this example:
- On mobile screens (default), cards will be stacked in a single column
- On medium screens (
md:
), cards will display in 2 columns - On large screens (
lg:
), cards will display in 3 columns
The default breakpoints are:
sm
: 640pxmd
: 768pxlg
: 1024pxxl
: 1280px2xl
: 1536px
Customizing Tailwind CSS in Next.js
Tailwind CSS is highly customizable. You can modify colors, spacing, breakpoints, and more through the tailwind.config.js
file:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./app/**/*.{js,ts,jsx,tsx,mdx}",
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
colors: {
'brand-primary': '#FF6F61',
'brand-secondary': '#6B7FD7',
},
spacing: {
'128': '32rem',
},
fontFamily: {
'sans': ['Poppins', 'sans-serif'],
'display': ['Playfair Display', 'serif'],
},
},
},
plugins: [],
}
Now you can use these custom values in your components:
<div className="bg-brand-primary text-white p-4 font-display">
This uses my custom brand color and font!
</div>
Extracting Component Classes with @apply
While utility classes directly in HTML are Tailwind's main feature, you might want to extract common patterns into reusable classes. You can use the @apply
directive in your CSS:
/* In your global.css file */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.btn-primary {
@apply bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded;
}
.card {
@apply bg-white rounded-lg shadow-lg p-6;
}
}
Then use these custom classes in your components:
<button className="btn-primary">
Click me!
</button>
<div className="card">
Card content
</div>
Real-world Example: Building a Navigation Bar
Let's create a responsive navigation bar with Tailwind CSS in Next.js:
// components/Navbar.js
"use client"; // If using App Router
import { useState } from 'react';
import Link from 'next/link';
export default function Navbar() {
const [isOpen, setIsOpen] = useState(false);
return (
<nav className="bg-gray-800 text-white">
<div className="max-w-6xl mx-auto px-4">
<div className="flex justify-between items-center h-16">
{/* Logo */}
<div className="flex-shrink-0">
<Link href="/" className="text-xl font-bold">
MyApp
</Link>
</div>
{/* Desktop Menu */}
<div className="hidden md:block">
<div className="ml-10 flex items-baseline space-x-4">
<Link href="/" className="px-3 py-2 rounded-md text-sm font-medium hover:bg-gray-700">
Home
</Link>
<Link href="/about" className="px-3 py-2 rounded-md text-sm font-medium hover:bg-gray-700">
About
</Link>
<Link href="/services" className="px-3 py-2 rounded-md text-sm font-medium hover:bg-gray-700">
Services
</Link>
<Link href="/contact" className="px-3 py-2 rounded-md text-sm font-medium hover:bg-gray-700">
Contact
</Link>
</div>
</div>
{/* Mobile menu button */}
<div className="md:hidden">
<button
onClick={() => setIsOpen(!isOpen)}
className="inline-flex items-center justify-center p-2 rounded-md hover:bg-gray-700 focus:outline-none"
>
<svg
className="h-6 w-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
{isOpen ? (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" />
) : (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 6h16M4 12h16M4 18h16" />
)}
</svg>
</button>
</div>
</div>
</div>
{/* Mobile Menu */}
{isOpen && (
<div className="md:hidden">
<div className="px-2 pt-2 pb-3 space-y-1 sm:px-3">
<Link href="/" className="block px-3 py-2 rounded-md text-base font-medium hover:bg-gray-700">
Home
</Link>
<Link href="/about" className="block px-3 py-2 rounded-md text-base font-medium hover:bg-gray-700">
About
</Link>
<Link href="/services" className="block px-3 py-2 rounded-md text-base font-medium hover:bg-gray-700">
Services
</Link>
<Link href="/contact" className="block px-3 py-2 rounded-md text-base font-medium hover:bg-gray-700">
Contact
</Link>
</div>
</div>
)}
</nav>
);
}
This navigation bar:
- Uses a dark background with light text
- Shows a horizontal menu on desktop screens
- Shows a hamburger menu on mobile screens that toggles when clicked
- Has hover effects on menu items
- Is centered with a maximum width
You can import and use this component in your layout:
// app/layout.js (App Router)
import Navbar from '@/components/Navbar';
import './globals.css';
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<Navbar />
<main>{children}</main>
</body>
</html>
);
}
Using Tailwind CSS with Dark Mode
Tailwind CSS supports dark mode out of the box. First, enable it in your configuration:
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
// ...
],
darkMode: 'class', // or 'media' for OS-level preferences
theme: {
// ...
},
plugins: [],
}
Now you can use the dark:
prefix for dark mode styles:
<div className="bg-white dark:bg-gray-800 text-black dark:text-white">
This text adapts to dark mode
</div>
To enable dark mode with the 'class' strategy, you'll need to add the dark
class to your html
element. This is often done with a theme switcher:
// components/ThemeSwitcher.jsx
"use client"; // If using App Router
import { useState, useEffect } from 'react';
export default function ThemeSwitcher() {
const [darkMode, setDarkMode] = useState(false);
useEffect(() => {
// On page load, check if dark mode was previously enabled
if (localStorage.theme === 'dark' ||
(!('theme' in localStorage) &&
window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
setDarkMode(true);
} else {
document.documentElement.classList.remove('dark');
setDarkMode(false);
}
}, []);
const toggleDarkMode = () => {
if (darkMode) {
document.documentElement.classList.remove('dark');
localStorage.theme = 'light';
setDarkMode(false);
} else {
document.documentElement.classList.add('dark');
localStorage.theme = 'dark';
setDarkMode(true);
}
};
return (
<button
onClick={toggleDarkMode}
className="p-2 rounded-md bg-gray-200 dark:bg-gray-700"
>
{darkMode ? '🌙' : '☀️'}
</button>
);
}
Using Tailwind with CSS Modules
If you prefer CSS Modules for component-specific styles, you can still use Tailwind's utilities through the @apply
directive:
/* Button.module.css */
.button {
@apply px-4 py-2 rounded font-semibold transition-colors duration-200;
}
.primary {
@apply bg-blue-500 text-white hover:bg-blue-700;
}
.secondary {
@apply bg-gray-300 text-gray-800 hover:bg-gray-400;
}
Then use them in your component:
// components/Button.js
import styles from './Button.module.css';
export default function Button({ children, variant = 'primary', ...props }) {
return (
<button
className={`${styles.button} ${variant === 'primary' ? styles.primary : styles.secondary}`}
{...props}
>
{children}
</button>
);
}
This approach combines the component encapsulation of CSS Modules with the utility power of Tailwind.
Advanced Tailwind Features in Next.js
1. JIT (Just-In-Time) Mode
Since Tailwind CSS 3.0, JIT mode is enabled by default. This allows for:
- Faster development builds
- Smaller CSS files by only generating the classes you use
- Arbitrary value support like
w-[347px]
orbg-[#ff3300]
<div className="w-[22.5rem] bg-[#bada55] text-[1.5rem] p-[14px]">
Using arbitrary values!
</div>
2. Plugins
You can extend Tailwind with plugins. For example, to add typography styles:
npm install -D @tailwindcss/typography
// tailwind.config.js
module.exports = {
// ...
plugins: [
require('@tailwindcss/typography'),
// ...
],
}
Now you can use the typography plugin to style markdown or rich text:
<article className="prose lg:prose-xl">
<h1>Garlic bread with cheese: What the science tells us</h1>
<p>
For years parents have espoused the health benefits of eating garlic bread with cheese to their
children, with the food earning such an iconic status in our culture that kids will often dress
up as warm, cheesy loaf for Halloween.
</p>
<p>
But a recent study shows that the celebrated appetizer may be linked to a series of rabies cases
springing up around the country.
</p>
{/* ... */}
</article>
Summary
In this guide, we've covered:
- Setting up Tailwind CSS in a Next.js application
- Using Tailwind's utility classes for styling
- Creating responsive designs with breakpoint prefixes
- Customizing your Tailwind configuration
- Extracting common patterns with
@apply
- Building real-world components like navigation bars
- Implementing dark mode support
- Integrating Tailwind with CSS Modules
- Advanced features like JIT mode and plugins
Tailwind CSS provides a powerful and efficient approach to styling Next.js applications. Its utility-first philosophy allows for rapid development without leaving your JSX, while still offering the flexibility to customize and extend the framework to match your project's specific needs.
Additional Resources
- Official Tailwind CSS Documentation
- Tailwind CSS with Next.js Guide
- Tailwind UI - Pre-designed components using Tailwind CSS
- Headless UI - Completely unstyled, fully accessible UI components designed to integrate with Tailwind CSS
Exercises
- Create a responsive hero section with a heading, paragraph, and call-to-action button using Tailwind CSS.
- Build a product card component that includes an image, title, price, and "Add to Cart" button.
- Design a footer with multiple columns that stack on mobile devices.
- Implement a dark mode toggle that remembers the user's preference using local storage.
- Create a tabbed interface component using Tailwind CSS for styling and React state for the tab functionality.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)