The Benefits Of Using CSS Modules
Published: 2024-03-31 13:02:48

by [Christiana Uloma](https://blog.openreplay.com/authors/christiana-uloma)


<blockquote><em>
CSS is a great way to style your web pages but can also be a pain to manage. However, when you have a lot of code and components that need to work together, you might end up with styles that affect the wrong elements, clash with each other, or are hard to change. The solution to this is using CSS modules, as this article shows.
</em></blockquote>

<div style="background-color:#efefef; border-radius:8px; padding:10px; display:block;">
<hr/>
<h3><em>Session Replay for Developers</em></h3> 
<p><em>Uncover frustrations, understand bugs and fix slowdowns like never before with <strong><a href="https://github.com/openreplay/openreplay" target="_blank">OpenReplay</a></strong> — an open-source session replay suite for developers. It can be <strong>self-hosted</strong> in minutes, giving you complete control over your customer data.</em></p>
<img alt="OpenReplay" style="margin-top:5px; margin-bottom:5px;" width="768" height="400" src="https://raw.githubusercontent.com/openreplay/openreplay/main/static/openreplay-git-hero.svg" class="astro-UXNKDZ4E" loading="lazy" decoding="async">
<p><em>Happy debugging! <a href="https://openreplay.com" target="_blank">Try using OpenReplay today.</a></em><p>
<hr/>
</div>

[CSS modules](https://github.com/css-modules/css-modules) are a way to write CSS code that is easy to reuse and organize. With CSS modules, each CSS file is like a module that has its class names that you can use in other files or components. This way, you can ensure that your styles only apply to the elements you want and don't interfere with others. 

Also, with CSS modules, you can avoid some common problems with [CSS in large-scale projects](https://dev.to/hellonehha/css-for-large-projects-cmi), such as: [global scope](https://www2.microstrategy.com/producthelp/Current/WebSDK/Content/topics/webcusteditor/WCE_Global_CSS.htm), [specificity wars](https://www.smashingmagazine.com/2007/07/css-specificity-things-you-should-know/), [naming collisions](https://stackoverflow.com/questions/37480495/how-to-avoid-name-collisions-of-css-class-names), etc. 

Before we explore the benefits of using CSS modules, let’s see how we can set up our project to use them. This will help us understand how CSS modules work and how they differ from regular CSS files. We will use [webpack](https://webpack.js.org/) as our bundler, but you can use any other tool that supports CSS modules. 

We will create a new project directory and navigate to our integrated terminal.

After that, run the following command to initialize a new `Node.js` project and generate a `package.json` file:
```bash
npm init -y 
```
Now, we will install Webpack, `style-loader`, and `css-loader` by running the following command:
```bash
npm install webpack webpack-cli style-loader css-loader --save-dev 
```
Next, we will create the following directory structure within the project directory:
```bash
- src
  - index.js
  - styles
    - style.css
```
After that, we will create a `webpack.config.js` file in the root of the project directory and add the following configuration: 
```javascript
const path = require('path');
module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: {
                localIdentName: '[name]__[local]--[hash:base64:5]',
              },
            },
          },
        ],
      },
    ],
  },
};
```
This will tell webpack to bundle all the CSS files that you import in your JavaScript modules and inject them into the `<head>` of your HTML document. It will also enable the modules option in the `css-loader` configuration, which will generate unique class names for each CSS selector and scope them to the corresponding component.

Now, in the `src/styles` directory, create a `style.css` file with the following content: 
```css
.button {
  background-color: #ff0000;
  color: white;
}

.active {
  background-color: #00ff00;
}
```
This is a regular CSS file, but we will use it as a CSS module by importing it into our JavaScript code.

In the `src` directory, create an `index.js` file with the following content:
```javascript
import styles from './styles/style.css';
console.log(styles.button); // Outputs a unique class name for the .button selector
console.log(styles.active); // Outputs a unique class name for the .active selector
```
This will import the `style.css` file as a CSS module and assign it to the styles variable. We can then access the class names as properties of the styles object. The `css-loader` will transform the class names to be unique and scoped to the component.

Now, in the command line, run the following command to build your project using Webpack: 
```bash
npx webpack --mode development
```
This will create a `bundle.js` file in the dist directory containing the bundled JavaScript and CSS code. 

Next, you will open the generated `bundle.js` file and observe the transformed CSS class names. You will see something like this:
```javascript
// ./src/styles/style.css
__webpack_require__.r(__webpack_exports__);
// extracted by mini-css-extract-plugin
/* harmony default export */ __webpack_exports__["default"] = ({
  "button": "style__button--3f4d5",
  "active": "style__active--2g6f7"
});

// ./src/index.js
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _styles_style_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./styles/style.css */ "./src/styles/style.css");

console.log(_styles_style_css__WEBPACK_IMPORTED_MODULE_0__.default.button); // Outputs a unique class name for the .button selector

console.log(_styles_style_css__WEBPACK_IMPORTED_MODULE_0__.default.active); // Outputs a unique class name for the .active selector
```
You can see that the original class names `.button` and `.active` have been replaced by unique class names such as `style__button--3f4d5` and `style__active--2g6f7`. These class names are generated by the `css-loader` based on the file name, the original class name, and a hash. This ensures that the class names are unique and scoped to the component that imports them.

You can also see that the CSS code has been extracted by the `style-loader` and injected into the `<head>` of the HTML document. This means the styles will be applied to the elements with matching class names.

Now that we know how to set up our project, let's move on to the benefits.

## CSS modules enable local scope by default

One of the common problems with CSS in [large-scale](https://dev.to/hellonehha/css-for-large-projects-cmi) projects is that it has a global scope by default. This means that any style declaration can affect any element on the page, regardless of where it is defined or used. This can lead to unintended side effects, conflicts, and overrides, making the CSS code hard to maintain, read, and debug.

One of the benefits of using CSS modules is that they enable local scope by default. This means that the styles in a CSS module only apply to the component that imports them. They do not affect other components or elements on the page. 

This is achieved by generating unique class names for each component, using a combination of the file name, the local name, and a hash.

For example, if we have a `Button.module.css` file with the following content:
```css
.button {
  background-color: #ff0000;
  color: white;
}
.active {
  background-color: #00ff00;
}
```
The generated class names will look like this:
```css
.Button_button__3Kc8r {
  background-color: blue;
  color: white;
}
.Button_active__1ljlC {
  background-color: green;
}
```
Here, the generated class names `.Button_button__3Kc8r` and `.Button_active__1ljlC` encapsulate the styles defined within the module, providing a modular and isolated approach to styling.

The generated class names will map to HTML or JSX code elements. This will use a special syntax. It imports the CSS module as an object and accesses its properties. 

For example, if we have a `Button.js` React component that uses the CSS module, we can write something like this:
```javascript
import React from 'react';
import styles from './Button.module.css';
function Button(props) {
  return (
    <button
      className={`${styles.button} ${props.active ? styles.active : ''}`}
      onClick={props.onClick}
    >
      {props.children}
    </button>
  );
}
export default Button;
```
Here, the component imports the CSS module for styling. The button’s class name is dynamically determined by the CSS module styles and the `active prop`. If the `active prop` is true, the `styles.active` class is added to the button’s class list. We use the `${}` syntax to embed expressions in strings. This is a feature of JavaScript called [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals).

Now, to show the output of the code, we can use a simple HTML file that imports the React and `ReactDOM` libraries and the `Button.js` component. We can also use some inline styles to center the button on the page. 

For example:
```htmlmixed
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>CSS Modules Example</title>
  <style>
    body {
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
    }
    .button {
      color: white;
      padding: 1rem;
    }
    .active {
      background-color: #00ff00;
    }
  </style>
</head>
<body>
  <div id="root">
    <button class="button active">Click me</button>
  </div>
  <script src="./dist/bundle.js"></script>
</body>
</html>
```
The output of this HTML file will look like this:

![local-scope](https://blog.openreplay.com/images/benefits-of-css-modules/images/image1.jpg)

As you can see, the button component has different styles depending on the `active prop`. The styles are scoped to the component and do not affect other elements on the page. 

## CSS modules support the composition and inheritance of styles 

Another benefit of using CSS modules is that they support [composition](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_compositing_and_blending) and [inheritance](https://developer.mozilla.org/en-US/docs/Web/CSS/Inheritance) of styles. This means we can reuse and extend styles from other modules, without having to write them from scratch or duplicate them. 

We do this using the `composes` keyword. It lets us import and combine styles from one or more modules. 

For example, if we have a `Card.module.css` file with the following content:
```css
.card {
  border: 1px solid black;
  padding: 10px;
  margin: 10px;
}
.title {
  font-weight: bold;
  font-size: 20px;
}
.content {
  font-size: 16px;
}
```
If we want to create a special type of card with a different background and font color, we can use the `composes` keyword to inherit the styles from the `Card.module.css` file and override or add new styles.

We can create a new file called `BlueCard.module.css` with the following content:
```css
.blueCard {
  composes: card from './Card.module.css';
  background-color: blue;
  color: white;
}
```
We use the `composes` property to import styles from the `Card.module.css` module. It allows the `blueCard` class to inherit and apply those styles.

Note that `composes` applies the composed styles before the current styles. Use the current styles if there is a conflict or overlap between styles. They override the composed styles. 

For example, in our `BlueCard.module.css` file, we have overridden the `background-color` and `color` properties of the card class with new values.

The `composes` keyword takes one or more class names from another module, separated by spaces. It can also take a path to the module using the `from` keyword. The composed class names are then added to the current class name, creating a new class name that inherits the styles from both modules.

The generated class name for the `blueCard` class will look like this:
```css
.BlueCard_blueCard__2yf3Z,
.Card_card__1aB8r {
  border: 1px solid black;
  padding: 10px;
  margin: 10px;
  background-color: blue;
  color: white;
}
```
The composed class name is then mapped to the corresponding element in the HTML or JSX code using the same syntax. 

Let’s say we have a `BlueCard.js` React component that uses the CSS module; we can write the following:
```jsx
import React from 'react';
import styles from './BlueCard.module.css';
function BlueCard(props) {
  return (
    <div className={styles.blueCard}>
      <h1 className={styles.title}>{props.title}</h1>
      <p className={styles.content}>{props.content}</p>
    </div>
  );
}
export default BlueCard;
```
The component imports the CSS module styles from the `BlueCard.module.css` file. Inside the component, a `<div>` element is rendered with the class name `styles.blueCard`, which applies the corresponding styles defined in the CSS module. The component also receives `props` as its parameter, including title and content values, which are dynamically rendered within an `<h1>` and `<p>` element.

To show the output of this code, we can use the same HTML file as before. But, we should import the `BlueCard.js` component instead of the `Button.js` component. 

For example:
```htmlmixed
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Composition and Inheritance</title>
  <style>
    body {
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
    }
  </style>
</head>
<body>
  <div id="root"></div>
  <script src="https://unpkg.com/react/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
  <script src="./BlueCard.js"></script>
  <script>
    // Render the blue card component with some props
    ReactDOM.render(
      <BlueCard
        title="CSS Modules"
        content="CSS modules are a cool way to write modular and reusable styles."
      />,
      document.getElementById('root')
    );
  </script>
</body>
</html>
```
The output will look like this:

![composition](https://blog.openreplay.com/images/benefits-of-css-modules/images/image2.jpg)

As you can see, the blue card has inherited and composed styles from the `Card.module.css` file and applied its own styles.


<CTA_Middle_Design />



## CSS modules integrate well with modern web development tools and frameworks 

CSS modules are compatible with most of the popular web development tools and frameworks used today. You can easily integrate CSS modules into your existing workflow and enjoy the benefits of modular and reusable styles without changing your codebase.

In this section, we will explore how CSS modules seamlessly integrate with popular web development tools and frameworks ([Webpack](https://webpack.js.org/), [React](https://legacy.reactjs.org/docs/getting-started.html), and [Vue](https://vuejs.org/)).

### Webpack: 

[Webpack](https://webpack.js.org/) is a module bundler. It bundles many JavaScript files (modules) into a single file. The file can run in the browser. This helps us organize our code into smaller, reusable units. It also optimizes our web application's performance and compatibility.

To use CSS modules with Webpack, we must install and configure two loaders: `style-loader` and` css-loader`. These loaders will let us import CSS files into our JavaScript modules, which we can then inject into the DOM dynamically.

Here is a sample Webpack configuration that uses CSS modules:
```javascript
module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true, // enable CSS modules
            },
          },
        ],
      },
    ],
  },
};
```
In the above code, we have a Webpack configuration file called `webpack.config.js`. It exports an object with various properties defining Webpack's behavior. The `module` property defines rules for handling different types of files. 

In this case, a rule is defined for files with a `.css` extension. The rule specifies that the files should be processed using the `style-loader` and `css-loader`. The `css-loader` is configured with the option modules: true, enabling CSS modules.

The `css-loader` will transform your CSS files into JavaScript modules. These modules export an object containing the mappings of the original class names and the generated ones.

For example, if we have a CSS file like this:
```css!
.button {
  background-color: blue;
  color: white;
  padding: 10px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}
.button:hover {
  background-color: darkblue;
}
```
The `css-loader` will produce a JavaScript module like this:
```javascript
exports.locals = {
  button: 'Button_button__3fO1Z', // the generated class name
};
```
The `style-loader` will inject a `<style>` tag into your HTML document's `<head>` with the generated CSS code.

For example, the `style-loader` will inject something like this:
```htmlmixed
<style>
  .Button_button__3fO1Z {
    background-color: blue;
    color: white;
    padding: 10px;
    border: none;
    border-radius: 5px;
    cursor: pointer;
  }
  .Button_button__3fO1Z:hover {
    background-color: darkblue;
  }
</style>
```
We can show the output of this code using a simple HTML file. The file imports the React and `ReactDOM` libraries and the `Button.js` component. We can also use some inline styles to center the button on the page. 

For example:
```htmlmixed
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>CSS Modules Example</title>
  <style>
    body {
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
    }
  </style>
</head>
<body>
  <div id="root"></div>
  <script src="https://unpkg.com/react/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
  <script src="./Button.js"></script>
  <script>
    // Render the button component with different props
    ReactDOM.render(
      <div>
        <Button onClick={() => alert('Hello')}>Click Me</Button>
        <Button onClick={() => alert('World')} active>Click Me Too</Button>
      </div>,
      document.getElementById('root')
    );
  </script>
</body>
</html>
```
The output of the code will look like this:

![webpack-code](https://blog.openreplay.com/images/benefits-of-css-modules/images/image3.gif)

As you can see, the button component has different styles depending on the `active prop`. The styles are scoped to the component and do not affect other elements on the page.

### React:

To use CSS modules with [React](https://react.dev/), we must import our CSS modules as objects and use them as `className props` for our components.

Here is a simple React component that uses a CSS module:
```jsx
import React from 'react';
import styles from './Button.module.css';
const Button = ({ children, onClick }) => {
  return (
    <button className={styles.button} onClick={onClick}>
      {children}
    </button>
  );
};
export default Button;
```
The styles object will contain the mappings of the original class names and the generated ones produced by the `css-loader`.

Here’s what it will look like:
```javascript
const styles = {
  button: 'Button_button__3fO1Z',
  // other style properties if any
};
export default styles;
```
You can then use the `styles.button` property as the `className` `prop` for your button element. This will apply the corresponding style to it.

The button element will render something like this:
```htmlmixed
<button class="Button_button__3fO1Z" onclick="...">Click me</button>
```
To show the output of this code, we can use a simple HTML file that imports the React and `ReactDOM` libraries and the `Button.js` component. We can also use some inline styles to center the button on the page, just as we did in the previous section. 
```htmlmixed
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>CSS Modules Example</title>
  <style>
    body {
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
    }
  </style>
</head>
<body>
  <div id="root"></div>
  <script src="https://unpkg.com/react/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
  <script src="./Button.js"></script>
  <script>
    // Render the button component with some props
    ReactDOM.render(
      <Button onClick={() => alert('Hello')}>Click Me</Button>,
      document.getElementById('root')
    );
  </script>
</body>
</html>
```
The output will look like this:

![react](https://blog.openreplay.com/images/benefits-of-css-modules/images/image4.gif)

Here, the button component has the same style as the Webpack button component, but it applies it using a different syntax and method.

### Vue:

[Vue](https://vuejs.org/) is a framework that allows us to create reactive and reusable components for our web applications. Vue uses `single-file` components. They are files that contain a component's template, script, and style in one place.

We need to use the `vue-loader` to use CSS modules with Vue to enable CSS modules in our `single-file` components. This will allow us to use the module attribute on our `<style>` tags and access our styles as `$style` in our component. 

Here is a simple Vue component that uses a CSS module:
```htmlmixed
<template>
  <!-- use the $style property as the class binding -->
  <button :class="$style.button" @click="onClick">{{ text }}</button>
</template>
<script>
export default {
  props: {
    text: {
      type: String,
      required: true,
    },
    onClick: {
      type: Function,
      required: true,
    },
  },
};
</script>
<style module>
.button {
  background-color: green;
  color: white;
  padding: 10px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}
.button:hover {
  background-color: darkgreen;
}
</style>
``` 
The `vue-loader` will process our `single-file` component. It will extract the CSS code from the `<style>` tag with the module attribute. It will then make a unique class name for each component. It will also inject the styles into the DOM dynamically.

For example, the `vue-loader` will produce something like this:
```htmlmixed
<style>
  /* the generated class name */
  .Button_button__2yXkG {
    background-color: green;
    color: white;
    padding: 10px;
    border: none;
    border-radius: 5px;
    cursor: pointer;
  }

  .Button_button__2yXkG:hover {
    background-color: darkgreen;
  }
</style>
``` 
The `vue-loader` will add a `$style` property to our component. It will contain the mappings of the original class names and the generated ones.

The `$style` property will look like this:
```javascript
// $style property
const $style = {
  button: 'Button_button__2yXkG', // the generated class name
};
```
We can then use the `$style.button` property as the class for our button. This will apply the corresponding style to it.

The button element will render something like this:
```htmlmixed
<button class="Button_button__2yXkG" onclick="...">Click me</button>
```
To show the output, we can use a simple HTML file that imports the Vue library and the `Button.vue` component. We can also use some inline styles to center the button on the page. 

For example:
```htmlmixed
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>CSS Modules Example</title>
  <style>
    body {
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
    }
  </style>
</head>
<body>
  <div id="root"></div>
  <script src="https://unpkg.com/vue/dist/vue.js"></script>
  <script src="./Button.vue"></script>
  <script>
    // Render the Vue button component with some props
    new Vue({
      el: '#root',
      components: {
        'vue-button': Button,
      },
      template: `
        <vue-button text="Click Me Too" :on-click="() => alert('Hello')" ></vue-button>
      `,
    });
  </script>
</body>
</html>
```
The output will look like this:

![vue](https://blog.openreplay.com/images/benefits-of-css-modules/images/image5.gif)

Here, the Vue button has the same style as the React button, but it applies it using a different syntax and method.

To learn more about the `vue-loader`, check [here](https://blog.logrocket.com/vue-loader-tutorial/).

# Conclusion 

CSS modules are a game-changer for web development. They allow us to create beautiful styles, which are tied to our components, not to the global scope. We can also inherit styles from other modules, making our code more reusable and maintainable. CSS modules work well with the newest tools and frameworks, improving our workflow and developer experience. By using CSS modules, we can improve the quality and performance of our web applications and our productivity and satisfaction as web developers.

If you want to learn more about CSS modules, you can check out the following resources and links: 

- [The official GitHub repository of CSS modules, with documentation, examples, and specifications ](https://github.com/css-modules/css-modules)
- [A series of articles by CSS-Tricks that explain the basics and benefits of CSS modules with practical examples ](https://css-tricks.com/css-modules-part-1-need/)
- [A guide by Create React App that shows how to use CSS modules with React ](https://create-react-app.dev/docs/adding-a-css-modules-stylesheet/)
- [Using CSS modules with Vue](https://vue-loader.vuejs.org/guide/css-modules.html#usage) 
- [A guide by Webpack that shows how to use CSS modules with Webpack](https://webpack.js.org/loaders/css-loader/) 


<CTA_End_Design />