Disclaimer: This article is in no way meant to belittle the react-css-modules project, or its author. Gajus is a smart dude and does a lot of great work for free for the JavaScript community to enjoy! ❤️ This is merely an evaluation of this particular library.

As with many popular libraries, I'm sure "react-css-modules" had a valid use-case at the time it was created, but at the present, it's drawbacks far outweigh its benefits. This article is meant to be a warning against picking it up without thinking about what you get from it.

tl;dr: Use "css-loader" over "react-css-modules"/"babel-plugin-react-css-modules" because the latter relies on side-effects, adds cognitive overhead (too much magic), causes React errors in your tests, requires complex webpack config, requires an additional dependency, is slower than css-loader, and doesn't work with webpack/babel import aliases.

"PROs"*

Throws error when trying to use an undefined class name.

Only actual pro IMHO.


You don't have to use the styles object whenever constructing a className.

Why is explicitness a bad thing?


Mixing CSS Modules and global CSS classes is easy.

// With react-css-modules
import './styles.css';

<div className="global-class" styleName="local-class">Hi</div>;

// With css-loader
import styles from './styles.css';

<div className=`${styles.localClass} global-class`>Hi</div>;

Yeah, the second example is slightly more involved, but if you're using the fantastic (and tiny) classnames lib, you can simplify the "css-modules" example to:

import styles from './styles.css';
import classnames from 'classnames';

<div className={classnames(styles.localClass, 'global-class')}>Hi</div>;

Don't have to use camelCase CSS class names.

Okay, sure, but the same is true for "css-loader." It has a camelCase option which converts kabab-case'ed class names to camelCase.

CONs

Relies on side-effects

// With css-loader
import styles from './styles.scss';

export default () => <div className={styles.myClass}>Hi</div>;

// With react-css-modules
import './styles.scss';

export default () => <div styleName="myClass">Hi</div>;

Where does "myClass" come from? Why am I not using the ./styles.scss import?


Adds magic and cognitive overhead.

What's the difference between className and styleName? Why are there both?


Causes React errors about unrecognized property name (styleName) for native DOM elements.


Requires pretty convoluted webpack config.

{
  test: /\.(jsx?)$/,
  exclude: /node_modules/,
  use: [{
    loader: 'babel-loader',
    query: {
      plugins: [
        [
          'babel-plugin-react-css-modules',
          {
            context,
            generateScopedName: scopedPattern,
            filetypes: { '.scss': 'postcss-scss' }
          }
        ]
      ]
    }
  }]
}

Requires an additional dependency.

You're already using "css-loader" if you are importing css in your app, which already works for css modules out of the box. Why add another dependency?


Slower than using css-loader directly.

https://github.com/gajus/babel-plugin-react-css-modules#performance


Doesn't work with webpack aliases (or babel-plugin-module-resolver) with no plans to support.


~~Generates random number for style map which causes changes to dist files even when there were no code changes.~~

Fixed in v2.8.0.