on
React CSS Modules Evaluation
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.
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.
"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 tocamelCase
.
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
andstyleName
? 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.
- 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.