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.

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
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
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
Quick check
Two components each have .title in their own .module.css with different colours. What happens?
Go deeper
- 1Convert a global stylesheet into a `.module.css` file for one component. Validate your understanding by applying it in a small practical example.
- 2Notice how class names get hashed/scoped in the compiled output. Validate your understanding by applying it in a small practical example.
- 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.