Load Both CSS and CSS Modules With Webpack

CSS Modules are das bombe, but you're still stuck in a land where the traditional CSS stylesheet exists, you may have to load both. Here's a pattern that could potentially work for you.

Importing CSS

Webpack is a tool for loading many different kinds of assets as modules into JavaScript. This means, for instance, that you can import CSS into your JavaScript. If you import a CSS module in this way, it'll allow you to use that module on a UI component that is built using JavaScript (eg, React):

import css from './my-component.css'
import React from 'react'

const MyComponent = _ => <div className={css.someSelector}></div>

We can reference the css.someSelector from the css variable because it's a CSS module.

Using Webpack, you can import a traditional CSS stylesheet in the same way:

import './my-component.css'
import React from 'react'

const MyComponent = _ => <div className="someSelector"></div>

But the CSS is merely imported but not saved to a variable reference. There's no need. The CSS will be made available, usually via something like style-loader in a stylesheet on the page. Thus a simple className="someSelector" reference will be enough.

Those are the two worlds that we want to support. These two methods are so different that they need to be loaded differently.

Webpack Config

Webpack has a list of loader rules to determine how files get loaded as modules into your code. Each rule is usually matched by file extension. That is, the *.css files get loaded like stylesheets, and the *.js files get loaded like JavaScript code.

Here we will only explore how to load CSS, but we have two totally different constructs for how our styles will be used: traditional stylesheet and CSS module. We need to make another distinction to be able to load them differently. We'll do something akin to making a new file extension by adding a file name suffix to the end of one of these types of files. We'll choose the CSS module file names because CSS modules are the newcomer tech and a bit more specialized in their usage. Here's the webpack.config.js snippet:

{
  module: {
    rules: [
      {
        test: /^(?!.*?\.module).*\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.module\.css$/,
        use: ['style-loader', {
          loader: 'css-loader',
          options: {
            modules: true
          }
        }]
      }
    ]
  }
}

Both *.css and *.module.css files are loaded using the css-loader, but the CSS modules are being loaded with a key extra option enabled: modules: true.

That first entry with the gnarly regex is a "negative lookahead" that matches "*.css files that don't include a .module substring" file names. If you don't include this negation of *.module.css files, your *.module.css files will go through both loader rules, causing an error. Depending on your setup, another option might be to use the excludes option from Webpack.

Now both are loadable using the same tool in the same config. All we need to do now is stay consistent in our file naming.

Are there other strategies you've taken to get the same support?