Introduction
In normal CSS, every class lives in the global scope. If two files both define .button, they can override each other.
CSS Modules fix that. A CSS Module is a CSS file where class names are scoped locally by default. Your build tool turns .button into a unique name like .Button_button__a1b2c3.
How it works
- Write styles in a file named like
Button.module.css - Import that file in your component
- Use the exported object for class names
/* Button.module.css */
.button {
padding: 12px 20px;
background: #3498db;
color: #fff;
border: none;
border-radius: 8px;
}
.primary {
background: #2ecc71;
}
import styles from "./Button.module.css";
export function Button() {
return (
<button className={styles.button}>
Click me
</button>
);
}
styles.button is the unique generated class. Another component can also use .button without collisions.
Combining classes
<button className={`${styles.button} ${styles.primary}`}>
Save
</button>
Or with a small helper:
const classNames = [styles.button, styles.primary].filter(Boolean).join(" ");
Global styles when you need them
Sometimes you still want a global class (reset, third-party widget). Opt out with :global:
:global(.legacy-tooltip) {
z-index: 9999;
}
Use this rarely. Local scope is the default for a reason.
Composition
You can reuse styles from another class with composes:
.base {
font-size: 14px;
font-weight: 600;
}
.danger {
composes: base;
background: #e74c3c;
color: #fff;
}
Why use CSS Modules?
| Problem | CSS Modules help by |
| --- | --- |
| Name collisions (.title, .card) | Generating unique class names |
| Unclear style ownership | Importing styles next to the component |
| Hard-to-delete CSS | Making unused modules easier to spot |
| Manual BEM everywhere | Giving local scope without heavy naming rules |
Simple rule of thumb
- Component styles → CSS Modules (
.module.css) - App-wide tokens / resets → global CSS or CSS variables
- Need runtime theme changes → CSS custom properties (still work inside modules)
CSS Modules keep your styles next to your components and stop accidental global overrides. Write normal CSS, import it, and let the build tool handle unique class names.