CSS Modules
Overview
In traditional web development, all CSS is globally scoped. If you write .btn { color: red; } in a file for the Header, it will accidentally turn every single button on your entire website red. This global collision issue is a nightmare in large-scale React applications built by multiple teams.
CSS Modules fix this by creating locally scoped CSS classes. By simply appending .module.css to your stylesheet file name (e.g., Button.module.css), the build tool (Vite/Webpack) intervenes.
When you import and apply a class from a CSS Module, the build tool secretly renames the class by adding a random hash to the end of it (e.g., .btn_x8z9A). This guarantees that your class name is mathematically unique. You can safely name your classes .card, .title, or .container without ever worrying about them colliding with another engineer's code in a different component.
Syntax
// 1. Create a file named: UserCard.module.css
/*
.card {
border-radius: 8px;
background: white;
}
.titleText {
font-weight: bold;
}
*/
// 2. Import and use in UserCard.jsx
import styles from './UserCard.module.css';
function UserCard() {
return (
// Access the classes as properties of the imported 'styles' object
<div className={styles.card}>
<h2 className={styles.titleText}>Jane Doe</h2>
</div>
);
}
// In the browser DOM, it renders as: <div class="card_3x9Ab titleText_8yX2">Common Pitfalls
- Using hyphens in class names: If you name your CSS class
.main-title, you cannot access it via dot notation in JS (styles.main-titleis invalid syntax). You must use bracket notation:styles['main-title']. For this reason, always usecamelCasefor CSS Module classes (e.g.,.mainTitle).
Interview Questions
CSS Modules solve the problem of global namespace collisions. They automatically generate unique class names at build time, ensuring that CSS styles are strictly scoped locally to the component that imports them, preventing CSS from leaking and breaking other parts of the app.
CSS Modules require you to write traditional CSS in a separate file, but automatically scope those classes. Tailwind CSS is a utility-first framework where you write predefined utility classes (like flex p-4 bg-red-500) directly inline within the JSX. Tailwind eliminates context switching between files and reduces CSS bundle size, while CSS Modules keep your JSX cleaner.
Real-World Example
Combining Dynamic Classes with CSS Modules: Because styles is just a JavaScript object, you can dynamically select which CSS class to apply based on component props. The type prop could be 'error', allowing us to apply styles['error'] dynamically.
import styles from './Alert.module.css';
function Alert({ type, message }) {
// We want to combine the base 'alert' class with a dynamic type class (success, error, warning)
// Using Template Literals (Industry Standard)
const dynamicClass = `${styles.alert} ${styles[type]}`;
return (
<div className={dynamicClass}>
<span>{message}</span>
</div>
);
}Check Your Knowledge
Test your understanding of CSS Modules with these quick questions.