Serverside CSS Modules with babel

August 22, 2016 0 Comments css-modules, babel

I had a requirement to use CSS modules with serverside rendering, it took me a while to figure out so I thought I'd write the process down.

In order to set css modules, we need babel to turn our generic classnames (.container) into localised class names like this .style__container___fK43b. There is a module called babel plugin css modules transform to help us with this task.

npm install -D babel-plugin-css-modules-transform

You can set this module up in your .babelrc file by adding it to the plugins array.

{
    "plugins": [
        ["css-modules-transform"]: {
            "generateScopedName": "[name]__[local]___[hash:base64:5]
        }
    ]
}

I've added this to my .babelrc plugins array, and I've added a custom generateScopedName attribute which will add the filename, the local classname and a hash of the contents to create the new localised classname.

You can specify extensions that it should look out for, .css is the obvious one but what if you want to use a preprocesser like SCSS or Stylus?

I'll show you how to set up the config to preprocess Stylus files so they can be used by the CSS modules transform.

{
    "plugins": [
        [["css-modules-transform", {
            "generateScopedName": "[name]__[local]___[hash:base64:5]",
            "extensions": [".styl"],
            "preprocessCss": "./stylus-require-hook.js",
            "context": process.cwd()
        }] 
    ],
    "ignore": ["./stylus-require-hook.js"]
}

IMPORTANT - Set your context to whatever your webpack context is set to so that babel can generate the exact same classnames as webpack.

I've added .styl to the extensions attribute so it knows to look for my stylus files. And I've added a preprocessCss attribute which points to a file I've called stylus-require-hook.js. I've also added this file to the ignore list of babel because I don't want to babel it.

The stylus require hook is needed to preprocess the Stylus into CSS so it can be used by the CSS modules transform. Its a simple hook which takes raw stylus code and uses the stylus module to convert it to css.

/* stylus-require-hook.js */
const stylus = require('stylus');

module.exports = (styl, filename) =>  
    stylus(styl)
        .set('filename', filename)
        .render();

That's it, you're now set up for babel to process your css classnames into localised css classnames allowing you to use CSS modules in your project. On the client side, if you're using Webpack, you can set up the css-loader to hash the classnames in the same way so the browser can then pick up your css module on the client side and use it.