← Back to Chapters

CSS Modules

? CSS Modules

? Quick Overview

CSS Modules provide a way to write CSS that is scoped to a single React component instead of being global.

Each component imports its own .module.css file, and the build system generates unique class names so styles never clash between components.

This helps you build clean, maintainable, and truly component-based UIs without worrying about global CSS conflicts.

? Key Concepts

  • Local scoping: Styles apply only to the component where the module is imported.
  • Generated class names: Simple class names like .title are transformed into unique ones such as:
? View Code Example
// Example of a compiled CSS Module class name
card__title__3H8sd
  • Styles object: When you import a CSS Module, each class becomes a property on a JavaScript object (commonly called styles).
  • File naming: Files must end with .module.css to be treated as CSS Modules.

? Syntax & Theory

  • Define styles in a file named like Card.module.css.
  • Import the module in your React component: import styles from "./Card.module.css";
  • Use the classes via className={styles.className}.
  • Under the hood, the bundler (like Webpack, Vite, or CRA) generates unique class names and maps them to the keys in styles.
  • Regular CSS features (pseudo-classes, media queries, animations, etc.) work normally inside module files.

? Code Example: Card Component with CSS Module

Here’s a simple Card component styled using Card.module.css.

? Card.module.css

? View Code Example
// Card.module.css - styles local to the Card component
.card {
  background-color: #f8f9fa;
  border-radius: 8px;
  padding: 20px;
  box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}

.title {
  color: #007bff;
  margin-bottom: 10px;
}

.text {
  color: #555;
}

⚛️ Card.js

? View Code Example
// Card.js - React component using the Card.module.css styles
import React from "react";
import styles from "./Card.module.css";

function Card() {
  return (
    <div className={styles.card}>
      <h3 className={styles.title}>CSS Modules Example</h3>
      <p className={styles.text}>
        This card is styled with scoped CSS using modules.
      </p>
    </div>
  );
}

export default Card;

Each CSS class (card, title, text) becomes a property on the imported styles object, and React uses the generated unique class names in the DOM.

? Combining CSS Modules with Dynamic Logic

You can dynamically toggle or combine class names using state and template literals (or utilities like classnames).

? View Code Example
// Button.js - toggling CSS Module classes using component state
import React, { useState } from "react";
import styles from "./Button.module.css";

function Button() {
  const [active, setActive] = useState(false);

  return (
    <button
      className={`${styles.button} ${active ? styles.active : ""}`}
      onClick={() => setActive(!active)}
    >
      {active ? "Active" : "Inactive"}
    </button>
  );
}

export default Button;

// Button.module.css - base and active styles for the button
.button {
  padding: 10px 20px;
  border: none;
  color: white;
  border-radius: 4px;
  background-color: #6c757d;
}

.active {
  background-color: #28a745;
}

This pattern lets you keep styles scoped while still reacting to component state (like hovered, active, or selected UI states).

? Live Output / Explanation

When the Card component is rendered:

  • The outer <div> gets a unique class (for example Card_card__1AbcD) that applies the card layout and shadow.
  • The heading uses a unique title class, giving it the blue color and spacing.
  • The text uses a separate text class for muted body color.

When the Button component is rendered:

  • Initially, only styles.button is applied (grey background).
  • Clicking the button toggles the active state, which adds styles.active on top of the base class.
  • The background changes to green while the label switches between “Active” and “Inactive”.

In browser DevTools, you’ll see long, hashed class names instead of plain .button or .card, confirming that CSS Modules are scoping your styles.

? File Naming & Folder Structure

  • Files must end with .module.css to be treated as CSS Modules.
  • Imported styles appear as an object. For example: styles.card, styles.title.
  • Normal .css files stay global until you rename them to use the .module.css convention.

A typical folder structure might look like this:

? View Code Example
// Example folder structure using CSS Modules
? src/
 ┣ ? components/
 ┃ ┣ ? Card.js
 ┃ ┣ ? Card.module.css
 ┃ ┣ ? Button.js
 ┃ ┗ ? Button.module.css
 ┗ ? App.js

? Advantages of CSS Modules

  • ✅ Styles are scoped locally to components, eliminating global conflicts.
  • ✅ Support all regular CSS features like pseudo-classes, media queries, and animations.
  • ✅ Keep styles modular and easier to maintain in large React applications.
  • ✅ Work out of the box with tools like Create React App, Vite, and Next.js.

⚠️ Limitations

  • ❌ Not inherently dynamic for runtime theming; you may still need CSS-in-JS or custom theme logic.
  • ❌ Slightly more verbose import and usage syntax than plain global CSS.
  • ❌ Requires a build setup that supports CSS Modules (included by default in many modern React toolchains).

? Tips & Best Practices

  • Keep class names simple and semantic — the compiler makes them unique for you.
  • Place module files next to their components (e.g., Card.js and Card.module.css in the same folder).
  • Use descriptive names like .cardTitle instead of generic ones like .title for readability.
  • Combine CSS Modules with a small set of global styles for layout, typography, or brand colors.
  • Avoid over-nesting selectors; keep rules flat and component-focused.

? Try It Yourself

  1. Create a component called ProfileCard and style it using ProfileCard.module.css.
  2. Add two classes: active and inactive, and toggle them using React state.
  3. Create another component that also uses classes named active and inactive, and confirm that the styles remain scoped and do not conflict.
  4. Open DevTools, inspect elements, and observe the transformed CSS class names generated for each component.

Goal: Practice using CSS Modules to build scoped, component-based styling in React and verify that styles don’t leak across components.