Styling: CSS Modules, Tailwind and Global CSS

beginner
10 min

Styling: CSS Modules, Tailwind and Global CSS

Next.js does not force a styling approach on you - it supports several first-class options and makes each easy to set up. In this lesson you will learn the three most common approaches, when each one shines, and how to combine them cleanly in a single project.

Global CSS: Site-Wide Foundations

Global styles apply everywhere and are imported exactly once, in the root layout:

css
/* app/globals.css */ :root { --brand: #0070f3; --max-width: 72rem; } body { margin: 0; font-family: system-ui, sans-serif; line-height: 1.6; }
jsx
// app/layout.js import './globals.css';

Use global CSS for resets, CSS variables, base typography, and little else. If every component's styles are global, class name collisions become inevitable as the project grows - that is the problem CSS Modules solve.

CSS Modules: Scoped Styles Built In

Any file ending in .module.css is a CSS Module. Class names inside it are automatically made unique at build time, so styles can never leak between components:

css
/* app/components/Card.module.css */ .card { border: 1px solid #e5e5e5; border-radius: 8px; padding: 1.25rem; } .title { color: var(--brand); font-size: 1.25rem; }
jsx
// app/components/Card.js import styles from './Card.module.css'; export default function Card({ title, children }) { return ( <div className={styles.card}> <h2 className={styles.title}>{title}</h2> {children} </div> ); }

In the browser, styles.card becomes something like Card_card__x7Kd2 - unique per component. CSS Modules need zero dependencies, work in Server Components, and keep styles colocated with the component that owns them.

Tailwind CSS: Utility-First Speed

Tailwind takes the opposite approach: instead of writing CSS files, you compose small utility classes directly in JSX. create-next-app offers to configure it for you.

jsx
export default function Card({ title, children }) { return ( <div className='rounded-lg border border-gray-200 p-5 shadow-sm'> <h2 className='text-xl font-semibold text-blue-600'>{title}</h2> <div className='mt-2 text-gray-700'>{children}</div> </div> ); }

Why teams love it: no naming decisions, no dead CSS accumulating, consistent spacing and color scales, and responsive design with prefixes like md:flex. The trade-off is busier markup and a small learning curve for the class vocabulary. Unused classes are stripped at build time, so production CSS stays tiny.

Which Should You Choose?

  • Tailwind: fast iteration, design consistency, most popular choice in the Next.js community today.
  • CSS Modules: you prefer real CSS files, or you are migrating existing CSS - great default without new syntax.
  • Global CSS: variables, resets, and typography only.

These are not exclusive. A very common setup is Tailwind for most UI plus globals.css for design tokens, with the occasional CSS Module for something genuinely complex like an animation-heavy component.

A Note on CSS-in-JS

Runtime CSS-in-JS libraries like styled-components require client-side JavaScript, which conflicts with Server Components - they only work in Client Components and need extra configuration. For new App Router projects, prefer CSS Modules or Tailwind; they are zero-runtime and work everywhere.

Key Takeaways

  • Import global CSS once in the root layout; keep it to tokens, resets, and typography.
  • CSS Modules scope class names automatically and work perfectly with Server Components.
  • Tailwind offers utility-first speed with build-time purging; create-next-app sets it up.
  • Mix approaches deliberately: tokens globally, utilities for layout, modules for special cases.

Next up: Lesson 11 covers two of Next.js's biggest performance wins - automatic image and font optimization.

Styling: CSS Modules, Tailwind and Global CSS - Next.js | CodeYourCraft | CodeYourCraft