1. Code
  2. JavaScript

Learn React 18: Using CSS Modules

Scroll to top
60+ min read

CSS modules make sure that your CSS is locally scoped by adding a unique hash to your class names. You will understand it better once we use CSS modules for styling our random number generator app.

In this lesson from our free full-length beginner course on React 18, you'll learn how. 

It is possible to use React without any toolchain and integrate it directly into your existing website by using simple script tags. However, using toolchains will speed up your development, give you access to additional features, and allow you to detect mistakes in your code.

One popular tool for building new single-page apps is Create React App. You can get started with it by running a single command. Here, rgen-app is simply the name of your app.

1
npx create-react-app rgen-app

This will install a bunch of packages and tools for you like webpack, Babel, ESLint, etc. The feature of Create React App that we will discuss in this tutorial is the ability to use CSS modules.

What Are CSS Modules?

CSS rules have global scope. This means that any CSS rules that you define will apply to all elements with the same class. This is indeed what we want to happen. Defining the same rules again and again would be very cumbersome.

However, things can get tricky when we are working on large projects. One problem could be name collisions. For example, a lot of elements in your project might require wrapper div tags around them for proper styling. However, you probably want these wrappers to have different values for properties like margin and padding. This will require you to write CSS that uniquely targets these wrappers. It will get hard for you to keep track of all the unique class names or specific CSS selectors and avoid any collisions.

For another example, consider two developers who are working on two independent components for a larger project. They might use the same class names for some of the elements, which could ultimately result in conflicts due to the global scoping of CSS.

CSS modules will help you solve these problems. They make sure that your CSS is locally scoped by adding a unique hash to your class names. You will understand it better once we use CSS modules for styling our random number generator app.

Let's get started.

Setting Up the Project

Execute the following command in a directory of your choice to install everything needed for setting up the Create React App tool.

1
npx create-react-app rgen-app

This will create a bunch of files in different directories. However, we are concerned only with the src directory for now. Delete all the files in the directory and create three new files named index.js, RandomGenerator.js, and RandomGenerator.css.

Place the following code inside the index.js file:

1
import React from 'react';
2
import ReactDOM from 'react-dom/client';
3
import RandomGenerator from './RandomGenerator';
4
5
const root = ReactDOM.createRoot(document.getElementById('root'));
6
root.render(
7
  <RandomGenerator low={500} numRange={1000} />

8
);

Place the following code inside the RandomGenerator.js file:

1
import React from 'react';
2
import './RandomGenerator.css';
3
4
class RandomGenerator extends React.Component {
5
    constructor(props) {
6
      super(props);
7
      
8
      this.rangeInput = React.createRef();
9
      this.lowInput = React.createRef();
10
  
11
      this.state = {
12
        numberRange: props.numRange,
13
        lowerLimit: props.low,
14
        randomNumber: Math.floor(Math.random() * props.numRange) + props.low
15
      };
16
      
17
      this.handleSubmission = this.handleSubmission.bind(this);
18
    }
19
    
20
    handleSubmission(e) {
21
      e.preventDefault();
22
      
23
      let newRange = parseInt(this.rangeInput.current.value);
24
      let newLow = parseInt(this.lowInput.current.value);
25
  
26
      
27
      if(isNaN(newRange) || newRange < 0) {
28
        newRange = 0;
29
      }
30
      
31
      if(isNaN(newLow) || newLow < 0) {
32
        newLow = 0;
33
      }
34
      
35
      this.setState({numberRange: newRange, lowerLimit: newLow});
36
      
37
      this.lowInput.current.focus();
38
    }
39
  
40
    componentDidMount() {
41
      this.numTimer = setInterval(() => {
42
        let tempVal = Math.floor(Math.random() * this.state.numberRange) + this.state.lowerLimit;
43
        this.setState({ randomNumber: tempVal });
44
      }, 2000);
45
    }
46
  
47
    componentWillUnmount() {
48
      clearInterval(this.numTimer);
49
    }
50
  
51
    render() {
52
      return (
53
        <div class="container">
54
          <h1>Random Number Generator</h1>

55
          <div className="range-container">
56
          <p className="capitalized">Lower Limit: {this.state.lowerLimit}</p>

57
          <p className="capitalized">Upper Limit: {this.state.lowerLimit + this.state.numberRange}</p>

58
          </div>

59
          <h2 className="random-number">{this.state.randomNumber}</h2>

60
          <form>
61
            <input type="number" ref={this.lowInput} />

62
            <input type="number" ref={this.rangeInput} />

63
            <button type="submit" onClick={this.handleSubmission}>Submit</button>

64
          </form>

65
        </div>

66
      );
67
    }
68
}
69
70
export default RandomGenerator;

Place the following code inside the RandomGenerator.css file:

1
body {
2
    margin: 20px auto;
3
    font-family: 'Lato';
4
    font-weight: 300;
5
    text-align: center;
6
}
7
8
h1 {
9
    font-family: 'Aclonica';
10
    font-size: 3.5rem;
11
}
12
13
.random-number {
14
    font-size: 4rem;
15
    background: orangered;
16
    color: white;
17
    width: fit-content;
18
    display: inline-block;
19
    padding: 0 1rem;
20
    border-radius: 10px;
21
}
22
23
.capitalized {
24
    text-transform: uppercase;
25
    font-weight: bold;
26
}
27
28
form {
29
    display: flex;
30
    gap: 2rem;
31
    margin: 0 auto;
32
    justify-content: center;
33
    flex-direction: column;
34
    width: 80%;
35
}
36
37
input {
38
    border: none;
39
    border-bottom: 2px solid black;
40
    font-size: 2rem;
41
    font-family: 'Lato';
42
    outline: none;
43
}
44
45
input:focus {
46
    border-bottom: 2px solid blue;
47
}
48
49
button {
50
    border: 2px solid black;
51
    font-size: 2rem;
52
    padding: 0.5rem 2rem;
53
    font-family: 'Lato';
54
    text-transform: uppercase;
55
    font-weight: bold;
56
    cursor: pointer;
57
}
58
59
.range-container {
60
    display: flex;
61
    gap: 4rem;
62
    justify-content: center;
63
}
64
65
.range-container p {
66
    font-size: 1.5rem;
67
}

The output of the code above should be similar to the following image:

Create React App Visual AppearanceCreate React App Visual AppearanceCreate React App Visual Appearance

The markup generated by the app should look as shown below:

Create React App Old MarkupCreate React App Old MarkupCreate React App Old Markup

Using CSS Modules

You only have to make small changes to the RandomGenerator.js and RandomGenerator.css files to start using CSS modules.

Let's begin with the CSS file. First, rename it to RandomGenerator.module.css. Now, convert your class names that are in kebab-case to camelCase. For example, range-container becomes rangeContainer, and random-number becomes randomNumber. The CSS file should be updated to have the following rules:

1
.randomNumber {
2
    font-size: 4rem;
3
    background: orangered;
4
    color: white;
5
    width: fit-content;
6
    display: inline-block;
7
    padding: 0 1rem;
8
    border-radius: 10px;
9
}
10
11
.rangeContainer {
12
    display: flex;
13
    gap: 4rem;
14
    justify-content: center;
15
}
16
17
.rangeContainer p {
18
    font-size: 1.5rem;
19
}

When modifying the RandomGenerator.js file, we will begin by updating the import statement for our CSS.

1
import './RandomGenerator.css';

will become:

1
import styles from './RandomGenerator.module.css';

Using styles is a convention here, and you can use another word if you like. This styles object will give you access to the class selectors from the CSS file.

Modify the render() method of the component so that it looks as shown below:

1
render() {
2
  return (
3
    <div class="container">
4
      <h1>Random Number Generator</h1>

5
      <div className={styles.rangeContainer}>
6
      <p className={styles.capitalized}>Lower Limit: {this.state.lowerLimit}</p>

7
      <p className={styles.capitalized}>Upper Limit: {this.state.lowerLimit + this.state.numberRange}</p>

8
      </div>

9
      <h2 className={styles.randomNumber}>{this.state.randomNumber}</h2>

10
      <form>
11
        <input type="number" ref={this.lowInput} />

12
        <input type="number" ref={this.rangeInput} />

13
        <button type="submit" onClick={this.handleSubmission}>Submit</button>

14
      </form>

15
    </div>

16
  );
17
}

Our random number generator app will look the same, but the class names attached to different elements will have changed. Here is what the new markup will look like:

Create React App New MarkupCreate React App New MarkupCreate React App New Markup

Did you notice that React prepended and appended the class name with the component name and a hash respectively? This way, it makes sure that there are no name collisions in selectors.

Final Thoughts

Using CSS modules when developing your React app means that you no longer have to worry about new style rules messing up your layout. You can also use them in conjunction with regular CSS to get the best of both worlds.

Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.