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 🦄 ), 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';
export default () => <div className="global-class" styleName="local-class"></div>
with "css-loader"
import styles from './styles.css';
export default () => <div className=`${styles.localClass} global-class`></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';
export default () => <div className={classnames(styles.localClass, 'global-class')}></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
.
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.