Tailwind CSS is a utility-first CSS framework that lets you build designs directly in your markup using small, single-purpose classes. Instead of writing custom CSS for every component, you compose utilities like flex, pt-4, text-center to create any design. Next.js has built-in support for Tailwind, making setup effortless. Let's learn to configure Tailwind, customize the theme, and build beautiful interfaces rapidly!
Why Tailwind CSS?
❌ Traditional CSS
.button {
padding: 12px 24px;
background: #3b82f6;
color: white;
border-radius: 8px;
font-weight: 600;
}
.button:hover {
background: #2563eb;
}
/* Write CSS for every component */
/* Naming is hard */
/* CSS file grows forever */✅ Tailwind CSS
<button className="px-6 py-3 bg-blue-500 text-white rounded-lg font-semibold hover:bg-blue-600">
Click Me
</button>
{/* No CSS file needed */}
{/* Utilities compose to create design */}
{/* Reusable, consistent, fast */}Benefits of Tailwind
- Fast development: Build UIs without leaving your HTML
- No naming: No more "what should I call this class?"
- Consistent design: Design system built-in
- Small bundle: Only ships CSS you use (tree-shaking)
- Responsive: Mobile-first with responsive modifiers
- Customizable: Easy to extend and customize
Installing Tailwind in Next.js
Next.js 13+ projects created with create-next-app include Tailwind by default. If you need to add it manually:
Step 1: Install Dependencies
npm install -D tailwindcss postcss autoprefixer
# Initialize Tailwind config
npx tailwindcss init -p
# Creates:
# - tailwind.config.js
# - postcss.config.jsStep 2: Configure Template Paths
import type { Config } from 'tailwindcss';
const config: Config = {
content: [
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
'./app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {},
},
plugins: [],
};
export default config;
// ✅ content: tells Tailwind which files to scan
// ✅ Enables tree-shaking (removes unused CSS)
// ✅ Include all files that use Tailwind classesStep 3: Add Tailwind Directives
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Optional: Custom styles after Tailwind */
@layer base {
h1 {
@apply text-4xl font-bold;
}
}
@layer components {
.btn-primary {
@apply px-6 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600;
}
}
@layer utilities {
.text-shadow {
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
}
}
// ✅ @tailwind directives inject Tailwind's styles
// ✅ @layer lets you add custom styles properlyStep 4: Import Global Styles
import './globals.css';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
// ✅ Import globals.css in root layout
// ✅ Tailwind now available everywhereStep 5: Start Using Tailwind
export default function Home() {
return (
<div className="min-h-screen bg-gray-100 flex items-center justify-center">
<div className="bg-white p-8 rounded-lg shadow-lg max-w-md">
<h1 className="text-3xl font-bold text-gray-900 mb-4">
Welcome to Next.js + Tailwind!
</h1>
<p className="text-gray-600 mb-6">
Build beautiful interfaces rapidly with utility classes.
</p>
<button className="w-full px-6 py-3 bg-blue-500 text-white rounded-lg font-semibold hover:bg-blue-600 transition">
Get Started
</button>
</div>
</div>
);
}
// ✅ Use Tailwind classes directly
// ✅ No CSS file needed
// ✅ Responsive, customizable, fastCustomizing Tailwind Theme
Adding Custom Colors
import type { Config } from 'tailwindcss';
const config: Config = {
content: [
'./app/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
colors: {
// Add custom brand colors
brand: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6', // Main brand color
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
},
// Or simple single colors
primary: '#3b82f6',
secondary: '#6b7280',
accent: '#f59e0b',
},
},
},
plugins: [],
};
export default config;
// Usage:
// bg-brand-500
// text-brand-600
// border-primary
// hover:bg-accent
// ✅ extend: adds to default colors (keeps blue, red, etc.)
// ✅ without extend: replaces all colors
// ✅ Use color palette generator for shadesCustom Spacing
theme: {
extend: {
spacing: {
'18': '4.5rem', // 72px
'88': '22rem', // 352px
'128': '32rem', // 512px
},
},
}
// Usage:
// p-18 → padding: 4.5rem
// w-88 → width: 22rem
// h-128 → height: 32rem
// ✅ Keeps default spacing (4, 8, 12, etc.)
// ✅ Adds custom values for your needsCustom Fonts
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
serif: ['Merriweather', 'Georgia', 'serif'],
mono: ['Fira Code', 'monospace'],
display: ['Playfair Display', 'serif'],
},
},
}
// Usage:
// font-sans → Inter
// font-serif → Merriweather
// font-mono → Fira Code
// font-display → Playfair Display
// ✅ Default sans becomes Inter
// ✅ Add custom font families
// ✅ Use with next/font for optimizationCustom Breakpoints
theme: {
extend: {
screens: {
'xs': '475px',
'3xl': '1920px',
// Or custom names
'tablet': '640px',
'laptop': '1024px',
'desktop': '1280px',
},
},
}
// Usage:
// xs:text-sm → @media (min-width: 475px)
// 3xl:container → @media (min-width: 1920px)
// tablet:grid-cols-2
// ✅ Keeps default breakpoints (sm, md, lg, xl, 2xl)
// ✅ Adds custom breakpoints
// ✅ Mobile-first (min-width)Complete Custom Theme Example
import type { Config } from 'tailwindcss';
const config: Config = {
content: [
'./app/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
colors: {
brand: {
primary: '#3b82f6',
secondary: '#6b7280',
accent: '#f59e0b',
success: '#10b981',
warning: '#f59e0b',
error: '#ef4444',
},
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
heading: ['Poppins', 'sans-serif'],
},
spacing: {
'18': '4.5rem',
'88': '22rem',
},
borderRadius: {
'4xl': '2rem',
},
boxShadow: {
'soft': '0 2px 15px rgba(0, 0, 0, 0.08)',
'hard': '0 8px 30px rgba(0, 0, 0, 0.12)',
},
animation: {
'fade-in': 'fadeIn 0.5s ease-in-out',
'slide-up': 'slideUp 0.3s ease-out',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
slideUp: {
'0%': { transform: 'translateY(20px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
},
},
},
plugins: [],
};
export default config;
// ✅ Custom colors
// ✅ Custom fonts
// ✅ Custom spacing
// ✅ Custom animations
// ✅ Everything customizable!Tailwind Plugins
Official Plugins
# Forms plugin - better form styling
npm install -D @tailwindcss/forms
# Typography plugin - prose styles for articles
npm install -D @tailwindcss/typography
# Aspect ratio plugin
npm install -D @tailwindcss/aspect-ratio
# Line clamp plugin - truncate text
npm install -D @tailwindcss/line-clampimport type { Config } from 'tailwindcss';
const config: Config = {
content: ['./app/**/*.{js,ts,jsx,tsx,mdx}'],
theme: {
extend: {},
},
plugins: [
require('@tailwindcss/forms'),
require('@tailwindcss/typography'),
require('@tailwindcss/aspect-ratio'),
require('@tailwindcss/line-clamp'),
],
};
export default config;Using Typography Plugin
// Prose styles for markdown/rich text
export function Article({ content }: { content: string }) {
return (
<article className="prose prose-lg max-w-none">
<div dangerouslySetInnerHTML={{ __html: content }} />
</article>
);
}
// ✅ Styles all HTML elements nicely
// ✅ Headings, paragraphs, lists, code, etc.
// ✅ Perfect for blog posts and documentation
// Variants:
// prose-sm → smaller text
// prose-lg → larger text
// prose-xl → extra large
// prose-slate → color scheme
// dark:prose-invert → dark modeUsing Forms Plugin
// Better default form styling
export function Form() {
return (
<form className="space-y-4">
<input
type="email"
placeholder="Email"
className="w-full"
/>
<select className="w-full">
<option>Option 1</option>
<option>Option 2</option>
</select>
<textarea
placeholder="Message"
className="w-full"
/>
<input
type="checkbox"
className="rounded text-blue-500"
/>
</form>
);
}
// ✅ Forms plugin adds better default styles
// ✅ Consistent form element appearance
// ✅ Easy to customize furtherPractical Tailwind Examples
Example 1: Button Component
interface ButtonProps {
children: React.ReactNode;
variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
size?: 'sm' | 'md' | 'lg';
fullWidth?: boolean;
onClick?: () => void;
}
export function Button({
children,
variant = 'primary',
size = 'md',
fullWidth = false,
onClick,
}: ButtonProps) {
const baseStyles = 'rounded-lg font-semibold transition focus:outline-none focus:ring-2 focus:ring-offset-2';
const variants = {
primary: 'bg-blue-500 text-white hover:bg-blue-600 focus:ring-blue-500',
secondary: 'bg-gray-500 text-white hover:bg-gray-600 focus:ring-gray-500',
outline: 'border-2 border-blue-500 text-blue-500 hover:bg-blue-50 focus:ring-blue-500',
ghost: 'text-gray-700 hover:bg-gray-100 focus:ring-gray-500',
};
const sizes = {
sm: 'px-3 py-1.5 text-sm',
md: 'px-6 py-3 text-base',
lg: 'px-8 py-4 text-lg',
};
const className = `
${baseStyles}
${variants[variant]}
${sizes[size]}
${fullWidth ? 'w-full' : ''}
`.trim();
return (
<button className={className} onClick={onClick}>
{children}
</button>
);
}
// Usage:
// <Button variant="primary" size="lg">Click Me</Button>
// <Button variant="outline">Cancel</Button>
// <Button variant="ghost" size="sm">Delete</Button>
// ✅ Dynamic Tailwind classes
// ✅ Reusable component
// ✅ Type-safe propsExample 2: Card Component
interface CardProps {
title: string;
description: string;
image?: string;
badge?: string;
onAction?: () => void;
actionLabel?: string;
}
export function Card({
title,
description,
image,
badge,
onAction,
actionLabel = 'Learn More',
}: CardProps) {
return (
<div className="bg-white rounded-xl shadow-lg overflow-hidden hover:shadow-2xl transition-shadow duration-300">
{/* Image */}
{image && (
<div className="relative h-48 w-full overflow-hidden">
<img
src={image}
alt={title}
className="w-full h-full object-cover hover:scale-105 transition-transform duration-300"
/>
{badge && (
<span className="absolute top-4 right-4 px-3 py-1 bg-blue-500 text-white text-sm font-semibold rounded-full">
{badge}
</span>
)}
</div>
)}
{/* Content */}
<div className="p-6">
<h3 className="text-2xl font-bold text-gray-900 mb-2">
{title}
</h3>
<p className="text-gray-600 mb-4 line-clamp-3">
{description}
</p>
{/* Action */}
{onAction && (
<button
onClick={onAction}
className="w-full px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition font-semibold"
>
{actionLabel}
</button>
)}
</div>
</div>
);
}
// ✅ Responsive card design
// ✅ Hover effects
// ✅ Optional image and badge
// ✅ Built entirely with TailwindExample 3: Responsive Grid Layout
export default function Home() {
return (
<div className="min-h-screen bg-gray-50">
{/* Hero Section */}
<section className="bg-gradient-to-r from-blue-500 to-purple-600 text-white py-20">
<div className="container mx-auto px-4 text-center">
<h1 className="text-4xl md:text-6xl font-bold mb-4">
Welcome to Our Platform
</h1>
<p className="text-xl md:text-2xl mb-8 max-w-2xl mx-auto">
Build amazing things with Next.js and Tailwind CSS
</p>
<button className="px-8 py-4 bg-white text-blue-600 rounded-lg font-bold text-lg hover:bg-gray-100 transition">
Get Started
</button>
</div>
</section>
{/* Features Grid */}
<section className="container mx-auto px-4 py-16">
<h2 className="text-3xl font-bold text-center mb-12">
Our Features
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{[1, 2, 3, 4, 5, 6].map((i) => (
<Card
key={i}
title={`Feature ${i}`}
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
image={`/feature-${i}.jpg`}
badge="New"
/>
))}
</div>
</section>
{/* CTA Section */}
<section className="bg-blue-600 text-white py-16">
<div className="container mx-auto px-4 text-center">
<h2 className="text-3xl md:text-4xl font-bold mb-4">
Ready to get started?
</h2>
<p className="text-xl mb-8">
Join thousands of developers building with Next.js
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<button className="px-8 py-4 bg-white text-blue-600 rounded-lg font-bold hover:bg-gray-100 transition">
Sign Up Free
</button>
<button className="px-8 py-4 border-2 border-white text-white rounded-lg font-bold hover:bg-white hover:text-blue-600 transition">
View Demo
</button>
</div>
</div>
</section>
</div>
);
}
// ✅ Responsive design (mobile-first)
// ✅ Grid layout adapts to screen size
// ✅ Gradient backgrounds
// ✅ Hover effects
// ✅ No custom CSS neededTailwind Project Structure
Configuration files for Tailwind CSS in Next.js
Select a file or folder to see details
Tailwind Best Practices
1. Use Tailwind for Layout, CSS Modules for Complex Components
// ✅ GOOD: Tailwind for layout
<div className="container mx-auto px-4 py-8 grid grid-cols-3 gap-6">
{/* CSS Module for custom component */}
<CustomWidget />
</div>
// Tailwind: Fast layout, spacing, responsive
// CSS Modules: Complex animations, unique designs2. Extract Repeated Patterns to Components
// ❌ BAD: Repeated classes everywhere
<button className="px-6 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600">
<button className="px-6 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600">
<button className="px-6 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600">
// ✅ GOOD: Create reusable component
<Button>Click Me</Button>
<Button>Submit</Button>
<Button>Save</Button>
// Extract repeated patterns to components3. Use @apply for Component-Level Styles
@layer components {
.btn-primary {
@apply px-6 py-3 bg-blue-500 text-white rounded-lg font-semibold hover:bg-blue-600 transition;
}
.card {
@apply bg-white rounded-lg shadow-lg p-6;
}
.input {
@apply w-full px-4 py-2 border-2 border-gray-300 rounded-lg focus:border-blue-500 focus:outline-none;
}
}
// ✅ @apply extracts Tailwind utilities
// ✅ Keeps styles in CSS when it makes sense
// ✅ Balance between utility and component styles4. Organize Classes Consistently
// ✅ GOOD: Organize by category
<div className={
// Layout
"flex items-center justify-between " +
// Spacing
"px-4 py-3 mb-6 " +
// Colors/Appearance
"bg-white border border-gray-200 rounded-lg shadow " +
// Typography
"text-lg font-semibold " +
// States
"hover:shadow-lg transition"
}>
// Order: Layout → Spacing → Colors → Typography → States5. Use Variants for State Changes
// ✅ State variants
<button className="
bg-blue-500
hover:bg-blue-600
active:bg-blue-700
disabled:bg-gray-300
disabled:cursor-not-allowed
">
// ✅ Responsive variants
<div className="
text-sm
md:text-base
lg:text-lg
grid-cols-1
md:grid-cols-2
lg:grid-cols-3
">
// ✅ Dark mode variants
<div className="
bg-white
dark:bg-gray-900
text-gray-900
dark:text-white
">Key Takeaways
- Utility-first - compose designs with small, single-purpose classes
- Built into Next.js - zero configuration needed
- Customize in config - theme.extend for colors, fonts, spacing
- Tree-shaking - only ships CSS you use
- Responsive - mobile-first with breakpoint modifiers
- State variants - hover:, focus:, active:, disabled:
- Plugins - forms, typography, aspect-ratio
- @apply - extract utilities to component classes
What's Next?
You've mastered Tailwind CSS setup and configuration! Next, we'll explore Global Styles and CSS Variables—how to add global styles, create CSS custom properties for theming, set up dark mode, and manage design tokens. You'll learn to create a robust styling foundation for your application!
Global styles and CSS variables work great with Tailwind, providing centralized theme management and consistent design tokens across your entire application.
⚡ Tailwind + CSS Modules
Don't think it's "Tailwind vs CSS Modules"—use both! Tailwind for rapid layout and common patterns, CSS Modules for complex custom components. They complement each other perfectly!