How to Use CSS Modules with Create React App
Short answer: you can’t. You gotta ⏏
Update January 18th, 2018: CSS Module support has landed in alpha builds of Create React App v2 🎉.
👉 Here’s how to use CSS Modules with Create React App v2! 👈
Read on if you need to add CSS Modules to an older v1 CRA instance.
Facebook’s Create React App is a great way to get started on a new React project quickly. It supports a lot of sensible and popular defaults. But if you want to add unsupported functionality — a CSS preprocessor step like CSS Modules for example—you’re going to have to eject
and head out on your own.
This article will explain how to use CSS Modules in a Create React App … app.
Starting a new project with Create React App (I’m just going to call it CRA for short) means you no longer have to manage a bunch of node modules and confusing Webpack configs. After installing CRA globally ($ yarn global add create-react-app
) starting a new project is as easy as:
$ create-react-app myApp
$ cd myApp/
$ yarn start
A nice detail is that CRA keeps itself updated so you shouldn’t ever need to manually update the global create-react-app
package.
Go your own way
You get a lot of power from those little commands. Unfortunately, if you want to do anything outside of what CRA provides you’ll have to go your own way.
the `eject` command ⏏
Create React App comes with a handy little command for those of us that have our own opinions about the ingredients of a great React setup: the eject
command:
$ yarn eject
The Create React App README is pretty clear about the pros and cons of ejecting:
Note: this is a one-way operation. Once you
eject
, you can’t go back!If you aren’t satisfied with the build tool and configuration choices, you can
eject
at any time. This command will remove the single build dependency from your project.Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except
eject
will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.You don’t have to ever use
eject
. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
I suggest you read that carefully and decide if you are ready to make the split.
Here’s what happens when you eject
$ yarn eject
Are you sure you want to eject? This action is permanent. [y/N]
> y
Ejecting...Copying files into /Users/user/src/myApp
This replaces your project’s single dependency on react-scripts
with the plethora of packages CRA depends on. It also moves some scripts and configurations into your project. Now you have full control of your project.
Running a Git status on your project after running yarn eject
shows the affected files and directories:
$ git statusChanges not staged for commit:
modified: package.jsonUntracked files:
config/
scripts/
Commit those changes and you are ready to configure CSS Modules.
CSS Modules
I’m not going to tell you how great CSS Modules are. I assume you’re already enlightened. If not read this. It has the best illustrations:
Let’s just hop right in and get CSS Modules installed right?
Installing CSS Modules
Lucky us! It turns out the project generated by ejecting from CRA includes all the node modules we need to get CSS Modules running in Webpack. We don’t need to install any additional dependencies!
Configuring CSS Modules
Find the css-loader
section in your Webpack dev config (config/webpack.config.dev.js:186
at the time of writing) and change the options
portion as so:
// before
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
},
},// after
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
modules: true,
localIdentName: "[name]__[local]___[hash:base64:5]"
},
},
Do the same in your Webpack production config (config/webpack.config.prod.js:191
):
// before
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
minimize: true,
sourceMap: true,
},
},// after
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
modules: true,
minimize: true,
sourceMap: true,
},
},
Note: you can leave the localIdentName
out of your production config and you’ll get the default class name format which is just an ugly [hash:base64]
.
And … that’s it!
If you run yarn start
now you will see that your app is now completely unstyled. Paradoxically that means its working.
Update your classNames to use CSS Modules
Your app is looking so brutalist because now you actually have to use CSS Modules in your components. To get started just open your components, and make a few changes:
- Assign imported stylesheets to a local constant
import styles from './SomeComponent.css'
. - Replaces the
className
strings in your JSX with object indexesclassName={styles.myClass}
. - CSS Modules don’t allow dashes (“-” ) in class names so you will have to update any classes that use a dash. You’ll have to make this change both in the CSS and in the JSX, but a nice bonus is that your class names will be simpler —
.App-header
becomes just.header
for example.
Here’s what the App component src/App.jsx
looks like:
Before CSS Modules:
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';class App extends Component {
render() {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Welcome to React</h2>
</div>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
);
}
}export default App;
After CSS Modules:
import React, { Component } from 'react';
import logo from './logo.svg';
import styles from './App.css';class App extends Component {
render() {
return (
<div className={styles.App}>
<div className={styles.header}>
<img src={logo} className={styles.logo} alt="logo" />
<h2>Welcome to React</h2>
</div>
<p className={styles.intro}>
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
);
}
}export default App;
You’re on your own now
You’re done! No more Create React App. Now you are free to use whatever CSS Preprocessor your want. You’re a grown up now.
If you aren’t ready to eject yet, I know of at least one other approach that allows you to add CSS Module support without ejecting. But this adds another dependency to your project which goes against the point of sticking with CRA.
I hope this little tip was helpful to you. Or at least made you less afraid to eject
. And of course, If you know of a better way to use CSS Modules with Create React App let me know in the comments!
Further reading
- Create React App: https://github.com/facebookincubator/create-react-app
- CSS Modules: https://glenmaddern.com/articles/css-modules
- Webpack: https://webpack.github.io/
July 5th, 2017 — examples updated to use Webpack 3 config syntax (thanks Tom Laker and Annie Pennell)