Stop 03 · CSS3Styling Tools & Libraries · 5/5

CSS Modules

Part of the Frontend Developer Roadmap

In short

CSS Modules is plain CSS, but every class name is locally scoped to the component that imports it. Next.js supports it out of the box, no extra library needed.

A way to write plain CSS files where class names are automatically scoped locally to the component that imports them, avoiding global name collisions. Local scoping reduces accidental collisions while retaining familiar CSS syntax, making CSS Modules a practical middle ground between global stylesheets and CSS-in-JS.

The jaadu mascot stands between two classroom doors where two students hold the same star badge.

Think of it like this

Roll numbers in different sections

Section A and Section B can both have a student with roll number 12, and nobody gets confused because the section is attached. CSS Modules attaches a 'section' to each class, so .title in two files never collides.

Watch it happen

How .title stays unique

.module.css
Build
Class map
HTML

1/4You write a plain .title class in Card.module.css.

The big ideas

What you need to know

Name it .module.css

Any file ending in .module.css is treated as a CSS Module by Next.js, Vite and most modern tools.

Classes become unique

.title becomes something like Card_title__x7a2 in the final build, so it can't clash with other files.

Import as an object

import styles from "./Card.module.css" then use className={styles.title}.

Zero runtime

It's plain CSS at build time, so it works in Server Components and adds no JavaScript to the page.

See it in code

Walk through the code

tsx
1/* Card.module.css:2   .card { padding: 1rem; border: 1px solid #eee; }3   .featured { border-color: #ec4899; }4   .title { font-size: 1.25rem; } */5import styles from "./Card.module.css";6 7type Props = { title: string; featured?: boolean };8 9export function Card({ title, featured }: Props) {10  const cls = featured ? `${styles.card} ${styles.featured}` : styles.card;11  return (12    <article className={cls}>13      <h3 className={styles.title}>{title}</h3>14    </article>15  );16}

1/4The CSS file is plain CSS with short, simple class names.

The CSS file is shown in the comment. Both .card and .featured are combined into one className string when the card is featured.

Save yourself the pain

Common mistakes

Oops

Writing className="title" and wondering why no style applies.

Do this instead

The real class name is generated. Always use className={styles.title}.

Oops

Putting global resets inside a .module.css file.

Do this instead

Keep global styles like body and resets in a normal global CSS file imported once.

Your mission

Scope a component

~20 min

Quick check

Two components each have .title in their own .module.css with different colours. What happens?

Go deeper

  1. 1Convert a global stylesheet into a `.module.css` file for one component. Validate your understanding by applying it in a small practical example.
  2. 2Notice how class names get hashed/scoped in the compiled output. Validate your understanding by applying it in a small practical example.
  3. 3Compare CSS Modules to Styled Components — both solve scoping, differently.

More styling tools & libraries at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge