Skip to content

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.

How to load Styles in WebPack

Mar 13, 2019 • 8 Minute Read

Introduction

One of the biggest issues faced by web developers with CSS is the fact that CSS is global. Each CSS class is exposed globally and it is quite easy to unintentionally break some other part of your web app while updating or adding CSS for some new functionality. When using frameworks like React, it becomes even more difficult to manage CSS over a period of time.

This is where CSS Modules come into play. It allows us to write scoped CSS similar to a JavaScript variable. We can write CSS for our particular component and be assured that it would not be leaked to any other component.

CSS Modules are a cool feature and integrate very well with React applications. In this guide, we'll take the example of a React app and then add CSS modules. We'll also have a look at the Webpack plugins which would be used to achieve this.

Working with Modular CSS

There are two ways in which we can work with modular CSS inside of any React application.

  1. By using CSS as JavaScript objects
  2. By using CSS Modules

CSS as JavaScript Objects

It is much easier to write CSS as JavaScript objects, especially when working with ReactJS, as you do not have to deviate from JavaScript code to write your CSS. CSS classes look similar and are named in camel cased versions of the property name.

Also, CSS written as JavaScript objects can be used directly inside of React components by using the style attribute on components.

Take the example below;

      // lets write some CSS inside of our js
    const myButton = {
        backgroundColor: '#999999',
		margin: '10px',
        padding: '10px',
        border: '2px solid blue',
        width: '100%'
    }
    export default { myButton }

    //Importing style and using inside JSX
    import styles from './mystyles.css.js'

    const button = () => {
        return <button style={styles.myButton}>My Button</button>
    }
    

The code above is an example of using CSS as a JavaScript object. Also, this method allows us to have styles which are unique to each element.

The other thing that we can do when defining our CSS as a JavaScript object is to use variables as the values for the style property. In this way it can change depending on some conditions:

      // lets write some CSS inside of our js
    var myRedBgVar = {
        backgroundColor: 'red'
    }
    var myButton = {
        backgroundColor: myRedBgVar.backgroundColor,
        margin: '10px',
        padding: '10px',
        border: '2px solid blue',
        width: '100%'
    }
    export default { myButton };

    //Importing style and using inside JSX
    import styles from './mystyles.css.js'

    const button = () => {
        return <button style={styles.myButton}>My Button</div>
    }
    

One of the advantages of using CSS as JavaScript objects is that no additional setup is required to make this work. Since it is just simple JavaScript, we are good to go. Also, while using CSS in JavaScript can be extensible, it does come with its own limitations in certain areas.

CSS Modules

CSS modules are not very different from the approach we followed above. However, by using CSS modules, we can write our normal CSS styles and then import that into our component.

Below is an example (CSS code):

      // lets add some CSS in myStyle.css
    .button {
      background-color: blue;
      margin: '10px',
      padding: '10px',
      border: '2px solid blue',
      width: '100%'
    }
    

And here is the javascript code:

      // Importing and using in a jsx
    import myStyles from './myStyle.css'
    import React from 'react';

    export default class Button extends React.Component {
        render(){
            return (
                <button className={myStyles.button}>My Button</button>
            )
        }
    }
    

In the example above, it can be seen that the CSS code is actual CSS and not a JavaScript object. However, this by itself will not work and we would need to add/configure our loader to make this work. Also, the beauty of this approach is that the actual class name in the generated CSS file won’t be .button, as specified in the above CSS, but it would instead look like .button-[some-hash]. By adding hash to each class name, we can be sure that each class declaration would be unique. Also, since the hash is based on the contents, even if two classes clash, it is because they have similar styles.

Webpack Loaders

A webpack loader is essentially a plugin that can be used to apply any transformations or for manipulating files before they get added in a bundle.

There are two main loaders which we need to use:

  1. css-loader - Used to parse our CSS file and apply various transformations to it. It also has a CSS Modules mode that can take our CSS and hash the classes.
  2. style-loader - This would take the output string, which gets generated above, and put the same data inside the <style> tag/element.

We would now need to add another configuration for our.css files. Here we would first configure the style-loader and then css-loader as shown below:

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

The first configuration for style-loader does not need any extra configuration, so we should be good with that. Then we need to configure css-loader. Here, the important config is the "query" object, which defines two properties:

modules - Setting this as "true" turns on the CSS modules mode localIdentName - '[name][local]_[hash:base64:5]' defines how the structure of the generated CSS class should look. We don’t have to worry much about this. We only need to know that this would map to the generated output.

Running Webpack

We are almost done with our Webpack configuration and can now run Webpack. You can just run "npm start" to start up your Webpack dev server and have our CSS modules converted and get it running in the browser.

Also, if you are using the dev server, you’ll observe that the CSS is automatically updated when changing without needing a hard refresh in the browser, which can be quite useful during development.

Cleaning Up the Webpack Configuration

One odd thing about the Webpack configuration used above is that we have to configure loaders for our .css twice - once for style loader and then again for the css loader. It would be cleaner to group them into one. However, once we configure multiple loaders, we cannot pass in the "query" object as we did above. Instead, we need to use Webpack’s string configuration. If we did that, our configuration would look like below:

      {
    test: /\.css$/,
    loader: 'style-loader!css-loader?modules=true&localIdentName=[name]__[local]___[hash:base64:5]'
}
    

However, this seems pretty confusing and much harder to understand. Thankfully we have "webpack-combine-loaders" which enables us to use the "query" object syntax to configure our loader, but without having to repeat test: /.css$/ line. Using this module, our configuration would look like:

      {
  test: /\.css$/,
  loader: combineLoaders([
    {
      loader: 'style-loader'
    }, {
      loader: 'css-loader',
      query: {
        modules: true,
        localIdentName: '[name]__[local]___[hash:base64:5]'
      }
    }
  ])
}]
    

This is much cleaner and it is very clear that we are using both style-loader and css-loader on the same file type (.css).

Installing Package Dependencies

We'll need to add the packages used above to our list of dependencies in package.json. Run the following commands to do the same:

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

Also, if you plan to add support for SCSS files, feel free to run the following commands as well: npm install --save-dev sass-loader npm install --save-dev node-sass

Conclusion

As seen above, CSS Modules are a very nice way to organize our CSS code in any component based system. In the above example, we have shown this with React application, but none of the code is actually React specific. This method can be used easily with any other frameworks without any additional complexity.