How to isolate component styles in React using CSS modules
Tags: react, beginners, css, architecture
Published: 2020-07-11 13:50:14


React is a library for building front-end applications by creating components and composing them into a complex user interface. A component is a Javascript function/class, usually written in .jsx files, that manages its state, DOM structure, behaviors, and user interactivity. The component style is defined either as a separate style (.css/.scss) file or directly in the Javascript file using a concept named css-in-js. 

When using a separate style file, by default, all styles are global, as demonstrated later in this post, and you need to work harder to prevent collisions between components styles. This post suggests ways to encapsulate components styles and making them unique per component easily.

# Css-in-js or separated styles file?
Writing styles in a separate file is trivial as you write standard css definitions. Usually, you create a .css/.scss file with the same name of the component and then import it in the component Javascript file, making it part of the deployed package. Alternatively, you can use a library that follows the css-in-js principles and directly writes the styles inside the Javascript file using JSON objects. [CSS-in-js](https://en.wikipedia.org/wiki/CSS-in-JS) is not a particular library; it represents a group of libraries following the same ideas. 

Both ways are popular; each has its benefits and drawbacks. When using either, you write the styles next to the component definition, so probably expect that the component styles will affect only that component. Effectively at runtime, all the components' styles are added to the page head together, so there is no separation between components styles, and collision is inevitable.

CSS-in-js prevents collisions by generating unique selectors for each component. With separated .css/.scss file, it is the responsibility of the library you choose to prevent the collision, if anything. 

Tip: When switching between css definition to JSON representation, you can assist with an online converter like [css to React](https://staxmanade.com/CssToReact/).

# Building a box component with styles
The screenshot below shows two boxes, both with <span style='color:green'>green background</span>, even though the first one should have a <span style='color:red'>red background</span>.

<figure>
  <img onclick="window.open('https://sakalim.com/media/202007-styles/media/without.jpg', '_blank')" src="https://sakalim.com/media/202007-styles/media/without.jpg" alt="">
  <figcaption>Two green boxes instead of a red box and a green box</figcaption>
</figure>

Here is the code that leads to that unfortunate outcome.

### GreenBox.css (the styles file)
```css
.box {
    background: green;
}
```

### GreenBox.jsx (the Javascript file)
```javascript
import React from 'react';
import './GreenBox.css';

export function GreenBox() {
  return <div className="box">
    Green box
  </div>
}
```

### RedBox.css (the styles file)
```css
.box {
    background: red;
}
```

### RedBox.jsx (the Javascript file)
```javascript
import React from 'react';
import './RedBox.css';

export function RedBox() {
  return <div className="box">
    Red box
  </div>
}
```

## The problem
We defined two different components; each has its styles. Both have a css selector named `.box` with a different background color. When compiling the code, the bundler, a tool that generates the deployable files, groups all the styles together and, at runtime, inject them to the page header. Effectively at runtime, they are not bound to the original component, and other rules apply to determine which style to show when. In our case, the computed style of `.box` will show <span style='color:green'>green background</span> for both components. A computed style is a set of all the values of all the css properties relevant to the element after resolving all the duplication and performing the computation of the relevant properties.

> If you wonder why the browser prioritized the green background over the red one, it happens because the order of CSS styles matters. You can read more in article [Precedence in CSS](https://css-tricks.com/precedence-css-order-css-matters/) published at css-tricks.

<figure>
  <img onclick="window.open('https://sakalim.com/media/202007-styles/media/browser-computed.jpg', '_blank')" src="https://sakalim.com/media/202007-styles/media/browser-computed.jpg" alt="">
  <figcaption>The computed style of the box classname as a result of showing both components in the page</figcaption>
</figure>

## How to avoid that problem
There are popular methodologies, like [BEM](http://getbem.com/introduction/), [ABEM](https://css-tricks.com/abem-useful-adaptation-bem/), [Atomic](https://github.com/nemophrost/atomic-css), [SMACSS](http://smacss.com/), that provide conventions to prevent name collisions in the browser.  

Using conventions is excellent in general, but with modern libraries, there are some great alternatives, and you will probably prefer enforcing it at the API level to automate the process and leveraging authoring tools for component-based applications. 
 
To automate it instead of counting on conventions, We will use css module [css modules](https://github.com/css-modules/css-modules), a library that will make sure each component is using unique selectors at runtime


## Prepare your project to support css modules

CSS modules can be used in any Javascript project regardless of the devop tools you used to setup your project. But with "Create React App", a powerful toolchain to build React applications, it is even easier as "Create React App" comes with [css modules already activated](https://create-react-app.dev/docs/adding-a-css-modules-stylesheet/) and ready to be used.


If you are using "Create React App", go ahead to the next section, otherwise you can check out the [CSS modules](https://github.com/css-modules/css-modules#examples) examples section in their documentation site.

I must admit that it is not a straightforward process to set up css modules manually; I'm not sure why but the css modules documentation doesn't provide an easy guide to follow. I suggest you check if the tool you are using already supports css modules. Consider other alternatives or google for a guide that shows you how to tweak your bundler configuration.

## How to fix our boxes components using css modules
> This section focuses on using css modules with the "Create React App" tool.

To use css modules with any component in your project, you need to rename the style file by adding `.module` before the file extension. Refactoring the example from before will result in the desired outcome.

<figure>
  <img onclick="window.open('https://sakalim.com/media/202007-styles/media/with.jpg', '_blank')" src="https://sakalim.com/media/202007-styles/media/with.jpg" alt="">
  <figcaption>Two boxes that are using the same class name which becomes unique at runtime</figcaption>
</figure>

<p style="font-size:0.9em"><a href="https://codesandbox.io/embed/post-how-to-build-your-react-application-with-style-bxzuy?autoresize=1&fontsize=14&hidenavigation=1&theme=dark" target="_blank">(Run example in CodeSandbox)</a></p>

### GreenBox.module.css 

Change the file name, add `.module` to the name. In the file content, you can use any css class name you want. 
 
```css
.box {
    background: green;
}
```

### GreenBox.jsx

Import the `.module.css` file with default module `classes`. Then, use `classes` when setting the child `className` property. 

```javascript
import React from 'react';
import classes from './GreenBox.module.css'; // <--- 1. import classes from the module css file

export function GreenBox() {
  return <div className={classes.box}> {/* <--- 2. use classes file extension */}
    Green box
  </div>
}
```

### RedBox.module.css

Change also this file name, add `.module` to the name. In the file content, you can use any css class name you want. 

```css
.box {
    background: red;
}
```

### RedBox.jsx

Import the `.module.css` file with default module `classes`. Then, use `classes` when setting the child `className` property. 

```javascript
import React from 'react';
import classes from './RedBox.module.css'; // <--- 1. import classes from the module css file

export function RedBox() {
  return <div className={classes.box}> {/* <--- 2. use classes file extension */}
    Red box
  </div>
}
```

## Problem solved

Your components are now using css modules, which guarantee that you will never have name collision between components again. The image below shows the class names at runtime with and without [CSS modules](https://github.com/css-modules/css-modules).

![](https://sakalim.com/media/202007-styles/media/image3.png)

# Tips when using CSS modules

### Tip #1 - don't worry about awkward class names formatting

In your javascript files, all class names are converted to lower case to keep the Javascript convention for constants. For example, class name `.home-view` is converted to `homeView` and can be used as `classes.homeView`.

### Tip #2 - pick simple names

You are encouraged to use simple class names, at runtime each name will become unique but will still include the original name to simplify code debugging.

### Tip #3 - css modules library is not tight to "Create React App"

As mentioned earlier, [css modules](https://github.com/css-modules/css-modules) are not part of the "Create React App" tool and can be used by any project regardless of the tools you are using. If you set up CSS modules on your own, make sure you enable important css modules plugins like global preprocessor.

### Tip #4 - css modules library supports nested class names

When using The `.scss` file, you can continue using nested classes. The CRA tool is smart enough to handle nested classes.

#### GreenBox.module.css

```css
.box {
    background: green;
    .text {
        font-size: 30px;
    }
}
```

#### GreenBox.jsx

```javascript
import React from 'react';
import classes from './GreenBox.module.css';

export function GreenBox() {
  return <div className={classes.box}>
    <div className={classes.text}>Green box </div>
    </div>
}
```

In the example above, You can nested class names directly from `classes` like `classes.text`.

### Tip #5 - avoid using global class names with your own components

If you have a module that is using a class name from a global CSS file, it might indicate that you are missing a component.

Imagine you have a black box class name that blocks interactions with elements below it. Instead of just using a global class `.black-box` and calling it from multiple components; you should have a component named `BlackBox` with a matching CSS module. This way, you keep your code more straightforward and easier to maintain.

Below is a partial example demonstrating the `Box` component using the global class name `black-box` that is defined in a global CSS file.

```javascript
return (
  <div className={classes.box}>
    <div>Some important content</div>
    { isBlocked && <div className="black-box" /> }
  </div>
)
```

Below is an example of a `BlackBox` component that will handle this feature.

```javascript
return (
  <div className={classes.mapView}>
    <div>Some important content</div>
    <BlackBox visible={isBlocked} />
  </div>
)
```

### Tip #6 - css module library lets you override global class names in css file
You can declare global selectors both in .css files and .scss files. Using global selectors is useful if you want to override styles of third party components.

In your css file, write the following to declare a global selector that will not be handled by the CSS module compiler.

```css
:global {
    .global-class-name {
        color: green;
    }
}
```

### Tip #7 - css module library lets you override global class names in scss file
Setting global class names in scss files is even more robust as you can limit a global selector to affect only nested component children.

```css
.box {
    :global {
        .global-class-name {
            color: green;
        }
    }
}
```

To read more about global selectors, see their
[documentation](https://github.com/css-modules/css-modules#usage-with-preprocessors).

# Final words

In this post, I demonstrated a way to avoid style collision between components when using the "Create React App", with minimal effort by leveraging the power of css modules.

I have used it in many React projects that didn't require advanced styling support. Still, I recommend trying out css-in-jss next time you start a new project and play with it before settling with css styles file. It might surprise you.


<span>Photo by <a href="https://unsplash.com/@amandavickcreative?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Amanda Vick</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span>