PostCSS with CSS Modules and React
Tags: postcss, react, css, webpack
Published: 2018-06-14 14:50:18

---
title: PostCSS with CSS Modules and React
published: true
description: 
tags: postcss, react, css, webpack
---

![](https://thepracticaldev.s3.amazonaws.com/i/fso5smcm6exzy8eqylmr.png)

## Updates

05/07/20 - The `localIdentName` property for css-loader is now a sub-property of the `modules` property.

02/10/18 - Looks like Create React App now adds support for [CSS Modules](https://facebook.github.io/create-react-app/docs/adding-a-css-modules-stylesheet)

## Quick Intro

[CSS Modules](https://github.com/css-modules/css-modules) are meant as a way to locally scope class and animation names to help prevent some of the downfalls of the default global scope that normal CSS is based on.

[PostCSS](https://github.com/postcss/postcss) provides a way to transform styles using JavaScript plugins.

Can we make them work together? Why would we want to? Let’s answer the second question first.

## The Why

![](https://thepracticaldev.s3.amazonaws.com/i/66z689a6md3ltvtl7eep.gif)

PostCSS has a deep, deep ecosystem, and of particular interest to me is the [postcss-preset-env](https://preset-env.cssdb.org/) plugin. Using this plugin gets you access to the newest CSS features, and their polyfills, today. This role used to be filled by [cssnext](http://cssnext.io/) but this was recently [deprecated](https://moox.io/blog/deprecating-cssnext/), so what better time to explore postcss-preset-env?

An early issue I’ve had using CSS Modules has been its ‘variables’ syntax. While they definitely [exist](https://github.com/css-modules/css-modules/blob/master/docs/values-variables.md), I’m really not sold on the syntax, and CSS Variables are already in the [spec](https://www.w3.org/TR/css-variables-1/) so why are we re-inventing this particular wheel? I don’t think I’m alone in this feeling, other people seem to be [asking](https://github.com/gatsbyjs/gatsby/issues/4933) how to use CSS Modules with the standard CSS Variables, so here we are.

Couple this with [some](https://preset-env.cssdb.org/features#media-query-ranges) [fairly](https://preset-env.cssdb.org/features#matches-pseudo-class) [sweet](https://preset-env.cssdb.org/features#nesting-rules) [future](https://preset-env.cssdb.org/features#grid-layout) CSS functionality, and we’ve got reason enough to tie PostCSS together with CSS Modules.

## The How

![](https://thepracticaldev.s3.amazonaws.com/i/gfq94zvlrv90au0nms6f.gif)

Alright lets get to it. I hope you’re ready for your *node_modules* directory to grow a fair bit, we’ve got some installing to do!

First up, getting CSS Modules to work at all in the context of your React application.

## CSS Modules

Let’s get [babel-plugin-react-css-modules](https://github.com/gajus/babel-plugin-react-css-modules) (is there a longer npm package name?) installed. It has a runtime component, so it actually belongs in your *dependencies* rather than your *devDependencies*. Install it like this:

    npm install babel-plugin-react-css-modules --save

Make sure your *.babelrc* or whatever equivalent you are using to configure Babel includes the new plugin:

    plugins: [‘react-css-modules’]

And now we need to configure [Webpack](https://webpack.js.org/) to tell it how to load in CSS files. We’ll need [style-loader](https://github.com/webpack-contrib/style-loader) and [css-loader](https://github.com/webpack-contrib/css-loader) for this one. These are regular *devDependencies* so make sure you install them as such.

    npm install css-loader style-loader --save-dev

Now lets tell Webpack to use these loaders for CSS files. Add the following to your webpack.config.js

    {
      test: /\.css$/,
      use: [
        { loader: 'style-loader' },
        {
          loader: 'css-loader',
          options: {
            modules: {
              localIdentName: '[path]___[name]__[local]___[hash:base64:5]',
            },
          },
        },
      ],
    }

What’s up with that *localIdentName*? Good question! When you switch on the CSS Modules spec, css-loader will munge your css classes according to this ident. This means you can have two *.button* classes in your codebase and they wont conflict.

However this means that when you add a class name to your React component you’d need to know what css-loader is going to transform your class names into, right? Well that’s where babel-plugin-react-css-modules comes in. It’ll do the same munging of class names as css-loader, we just have to make sure they are configured to use the same method of munging.

The default value for this option in css-loader is different to babel-plugin-react-css-modules, so specifying it to be *[path]___[name]__[local]___[hash:base64:5]* fixes that.

Great, now in your React component you should be able to import the CSS file directly:

*App.css*

    .app {
      border: 1px solid red;
    }

*App.jsx*

    import React from 'react';

    import './App.css';

    const App = () => (
      <div styleName="app">
        Hello, world!
      </div>
    );

    export default App;

The *styleName* property is babel-plugin-react-css-modules’s replacement for *className*, but you get used to it pretty quickly.

Assuming everything has worked, you’ll have class names that look like word soup:

![](https://thepracticaldev.s3.amazonaws.com/i/ppngjkin15wctr63ijok.png)

## PostCSS

And now for the fun stuff. Lots of modules to install, so lets get started:

    npm install postcss postcss-import postcss-loader postcss-preset-env postcss-url --save-dev

We will need to change our *webpack.config.js* to make sure the postcss-loader gets used:

    {
      test: /\.css$/,
      use: [
        { loader: 'style-loader' },
        {
          loader: 'css-loader',
          options: {
            importLoaders: 1,
            modules: {
              localIdentName: '[path]___[name]__[local]___[hash:base64:5]',
            },
          },
        },
        { loader: 'postcss-loader' }
      ],
    }

And now we need a new *postcss.config.js* file

    module.exports = {
      plugins: [
        require('postcss-import'),
        require('postcss-url'),
        require('postcss-preset-env')({
          browsers: 'last 2 versions',
          stage: 0,
        }),
      ],
    };

Now we can try it out! Make a new *colors.css* file

    :root {
      --errorRed: #e03030;
    }

and change App.css to use it:

    @import "../colors.css";

    .app {
      border: 1px solid var(--errorRed);
    }

How’s that style block looking?

![](https://thepracticaldev.s3.amazonaws.com/i/ulk9oh4j1eadfls4kxon.png)

Nice! You get the *var()* rule for browsers that support it, or the fallback for those that don’t.

## Wrapping Up

Getting the right combination of modules to make this work was the real challenge on this one, the configuration itself was fairly easy.

If this doesn’t work for you, something is missing, or you think I’ve gone about this the wrong way, I’d love to hear from you in the comments.