JavaScript and React course Β· Module 14: Advanced Styling

CSS Modules - Isolated Control Panels

4 min read
In this lesson8

CSS Modules are one of the most popular ways to style in React. They work like a system of isolated control panels on a spaceship -- each module has its own controls that don't interfere with others.

What are CSS Modules?

CSS Modules are regular CSS files with the .module.css extension. The difference is that all class names are automatically unique -- React generates unique identifiers, so styles from one component never override styles of another.

1/* SpacePanel.module.css */
2.panel {
3  background: #0d1137;
4  border: 2px solid #3949ab;
5  border-radius: 12px;
6  padding: 20px;
7}
8
9.title {
10  color: #7c4dff;
11  font-size: 24px;
12  margin-bottom: 16px;
13}
14
15.statusActive {
16  color: #00e676;
17  font-weight: bold;
18}

Importing and using

We import a CSS Module as an object and reference classes like object properties:

1import styles from './SpacePanel.module.css';
2
3function SpacePanel({ title, status }) {
4  return (
5    <div className={styles.panel}>
6      <h2 className={styles.title}>{title}</h2>
7      <span className={styles.statusActive}>
8        Status: {status}
9      </span>
10    </div>
11  );
12}

In the generated HTML, the panel class becomes something like SpacePanel_panel_x7k2f -- completely unique!

Combining classes

We often need to combine multiple CSS classes. In CSS Modules, we can do this in several ways:

1// Template literal
2<div className={`${styles.panel} ${styles.dark}`}>
3
4// With condition
5<div className={`${styles.panel} ${isActive ? styles.active : ''}`}>
6
7// Array with join
8<div className={[styles.panel, styles.dark].join(' ')}>

Compose -- style inheritance

CSS Modules offer a special composes directive that allows inheriting styles from other classes:

1/* base.module.css */
2.baseButton {
3  padding: 10px 20px;
4  border: none;
5  border-radius: 6px;
6  cursor: pointer;
7  font-size: 14px;
8  transition: all 0.3s ease;
9}
10
11/* SpaceButton.module.css */
12.primaryButton {
13  composes: baseButton from './base.module.css';
14  background: #7c4dff;
15  color: white;
16}
17
18.dangerButton {
19  composes: baseButton from './base.module.css';
20  background: #f44336;
21  color: white;
22}

Thanks to composes, we avoid duplicating styles while maintaining isolation.

Global styles in CSS Modules

If you need a global class (e.g., for external libraries), use :global:

1/* Layout.module.css */
2:global(.container) {
3  max-width: 1200px;
4  margin: 0 auto;
5}
6
7.localClass {
8  /* This is local */
9  color: #64b5f6;
10}

Dynamic classes with CSS Modules

We often need to change styles based on props or state. Here are some patterns:

1import styles from './Alert.module.css';
2
3function Alert({ type = 'info', message }) {
4  // Dynamically selecting a class based on the prop
5  const alertClass = styles[type]; // styles.info, styles.warning, styles.error
6
7  return (
8    <div className={`${styles.alert} ${alertClass}`}>
9      <p className={styles.message}>{message}</p>
10    </div>
11  );
12}
1/* Alert.module.css */
2.alert {
3  padding: 16px;
4  border-radius: 8px;
5  border-left: 4px solid;
6}
7
8.info {
9  background: #1a237e;
10  border-color: #64b5f6;
11  color: #90caf9;
12}
13
14.warning {
15  background: #4a3800;
16  border-color: #ffa726;
17  color: #ffcc80;
18}
19
20.error {
21  background: #4a0000;
22  border-color: #f44336;
23  color: #ef9a9a;
24}

Notice that styles[type] lets you dynamically select a class based on a variable -- it's like switching modes on a control panel.

The classnames / clsx library

For more complex class combinations, it's worth using the clsx or classnames library:

1import clsx from 'clsx';
2import styles from './Button.module.css';
3
4function SpaceButton({ variant, size, disabled }) {
5  return (
6    <button className={clsx(
7      styles.button,
8      styles[variant],     // styles.primary or styles.secondary
9      styles[size],         // styles.small or styles.large
10      { [styles.disabled]: disabled }  // conditionally
11    )}>
12      Engage!
13    </button>
14  );
15}

Advantages of CSS Modules

  • Style isolation -- no class name conflicts, each component has its own "capsule"
  • Plain CSS -- no need to learn new syntax, you use the same CSS as always
  • Native support in Create React App and Next.js -- zero configuration
  • Lightweight -- no additional libraries in the bundle, zero runtime JS
  • Easy migration from existing CSS -- just change the file extension and import
  • Typing -- you can generate TypeScript types for CSS classes (e.g., via typed-css-modules)

CSS Modules are a solid, proven choice for styling React components. They're like reliable control panels on a spaceship -- simple, predictable, and conflict-free.

Code for this lesson: App.jsx
1import React from 'react';
2import styles from './SpacePanel.module.css';
3
4function StatusIndicator({ label, status }) {
5  const statusClass = status === 'active'
6    ? styles.statusActive
7    : status === 'warning'
8    ? styles.statusWarning
9    : styles.statusCritical;
10
11  return (
12    <div className={styles.statusRow}>
13      <span className={styles.label}>{label}</span>
14      <span className={statusClass}>{status.toUpperCase()}</span>
15    </div>
16  );
17}
18
19function SpacePanel({ title, children }) {
20  return (
21    <div className={styles.panel}>
22      <h2 className={styles.title}>{title}</h2>
23      {children}
24    </div>
25  );
26}
27
28export default function App() {
29  return (
30    <div className={styles.app}>
31      <h1 className={styles.heading}>CSS Modules in React</h1>
32      <p className={styles.subtitle}>Isolated styles -- each component has its own unique classes</p>
33
34      <SpacePanel title="Navigation system">
35        <StatusIndicator label="Main engine" status="active" />
36        <StatusIndicator label="Thermal shield" status="warning" />
37        <StatusIndicator label="Backup fuel" status="critical" />
38      </SpacePanel>
39
40      <SpacePanel title="Communication">
41        <StatusIndicator label="Radio link" status="active" />
42        <StatusIndicator label="Backup antenna" status="active" />
43        <StatusIndicator label="SOS signal" status="active" />
44      </SpacePanel>
45    </div>
46  );
47}

Spotted a mistake in this lesson?

Check yourself

Answer the questions from this lesson. Pick an answer to see right away whether it is correct.

  1. 1. What is the correct syntax for referencing a CSS Variable in a stylesheet?

  2. 2. What is the main advantage of CSS Modules compared to regular CSS files?

Hands-on tasks in the game

  • Code editor

    Implement an AlertBox component with CSS Modules

  • Click in order

    Arrange the CSS Module import syntax in React:

  • Code editor

    Implement a tabs component with CSS Modules and useState

Useful articles