← Back to React Course | Chapter 11: Styling in React | Lesson 1 of 8

CSS Modules

CSS Modules are like giving every component its own private locker for styles, so its class names never accidentally clash with another component's locker.

The Problem CSS Modules Solve

Regular CSS class names are global — a class called '.card' in one file can accidentally override or be overridden by a '.card' class defined elsewhere in a large app. CSS Modules solve this by automatically making class names locally scoped to whichever component imports that stylesheet.

Note: This problem grows worse as an app and team scale — CSS Modules are especially valuable in larger codebases with many contributors.

Warning: This is purely a build-time naming trick — it doesn't change how CSS itself works, just how class names are generated and matched up.

Example: The Problem CSS Modules Solve

markup
// Run in your local React project (npm install required)
/* Card.module.css */
.card { border: 1px solid #ccc; padding: 16px; }

// Card.jsx
import styles from './Card.module.css';
function Card() {
  return <div className={styles.card}>Content</div>;
}

How the Import Becomes an Object

Importing a .module.css file doesn't give you the raw CSS text — the build tool transforms it into a JavaScript object where each key is one of your original class names, and each value is a unique, generated class name (like card_a1b2c) that won't collide with anything else in the app.

Note: Console.log the imported styles object during development to see exactly what generated class names it contains.

Warning: Referencing styles.someClassName for a class that doesn't exist in the CSS file returns undefined, silently applying no class at all.

Example: How the Import Becomes an Object

markup
// Run in your local React project (npm install required)
import styles from './Button.module.css';

function Button() {
  console.log(styles); // { button: "button_a3f9c" }
  return <button className={styles.button}>Click me</button>;
}

Combining Multiple Classes

When a component needs more than one CSS Modules class at once (like a base style plus a conditional variant), you combine the generated class name strings with template literals or a small helper, since JSX's className just wants one final string.

Note: Libraries like classnames or clsx make combining multiple conditional classes cleaner than manual string concatenation.

Warning: Concatenating class names with a plain '+' instead of a template literal or space-joining can accidentally merge two class names into one invalid string.

Example: Combining Multiple Classes

markup
// Run in your local React project (npm install required)
import styles from './Button.module.css';

function Button({ primary }) {
  const className = primary ? `${styles.button} ${styles.primary}` : styles.button;
  return <button className={className}>Click me</button>;
}
Common Mistakes
  1. Forgetting the import syntax is different — you import the CSS file as an object (import styles from './App.module.css'), not as a side-effect import.
  2. Using regular string class names instead of the generated styles.className property.
  3. Naming the file just 'App.css' instead of 'App.module.css' — the '.module.css' suffix is what triggers CSS Modules processing in most build tools.
Chapter Summary
  • CSS Modules scope class names locally to the component that imports them, avoiding global naming collisions.
  • A file named Component.module.css is processed specially by the build tool (Vite, webpack, etc.).
  • Imported styles become a JS object mapping your original class names to generated, unique ones.
  • This is a build-tool feature, not a runtime library — no CDN equivalent exists for this preview sandbox.
Browser Support

Build-tool feature (Vite/webpack/Create React App all support it); no specific React version requirement.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.