Discover how you can use CSS Modules to change the way you work with styles in your React application.
In this tutorial, we will explore how we can use CSS Modules in React to make maintaining our CSS files way easier and more modular.
When building React applications (or any application actually), we need it to look great. For web applications, the standard beautification language is called CSS. Since the age of the dinosaurs, properly structuring CSS styles and arranging them has always been a problem.
In small apps, this is not really a problem, but with applications of larger structure, things are bound to get real hazy. With CSS Modules we can modularize things in React and add some structure to the flow.
Let’s have a real look at the problems we are trying to solve.
CSS has always been easy and flexible, but then this really cool technology doesn’t really lend itself to scalability. With large projects that have extended sections and features, it becomes a real menace to deal with.
At scale these problems become real (scale here referring to a codebase with many developers working on it), but then there are a couple of problems that we have to take into account and make sure we understand before we attempt to solve them.
Some of the problems are:
1. Global ~~warming~~ namespaces.
2. Dead code elimination.
3. Dependencies.
4. Conditionals.
CSS is a language of globals. In most languages, global variables are considered bad and taking into account we have CSS styles mostly specific to our components, it’s obvious that globals are bad (there are cases when using global CSS is okay though).
Global CSS can easily rewrite styling in other classes. A change in the style in one class can ultimately and silently change the UI on multiple components. Generally, no one wants that to happen when they develop apps.
Most times while building apps, we have styling that is redundant or no longer in use. Although they can be small at first, over time it builds up and we have rogue and unused CSS everywhere and it can cause unnecessarily large file sizes.
With CSS modules, we get to only import styles we use. therefore reducing redundancy and the increase in our ability to spot CSS that is not being used.
Working with dependencies in CSS is hazy. Most of the time we are used to just including the entire script directly into the page whether they are being used or not.
Sometimes you need your CSS class to style in a certain way in one section and in a different way in another section. In this case, it is usually better to have the CSS class variable modifiable in the component.
Modular CSS solves each of these problems in a clean structured manner by having our components import only the classes they actually use therefore getting rid of redundancy. This in itself also solves the problems of global namespaces.
When working with modular CSS in React, we can work in a few ways:
– Using CSS as JavaScript Objects.
– Using CSS Modules.
We will focus on CSS modules but shine a little light on the former.
Writing CSS as JavaScript objects are easier in the context of React as it ensures you do not have to stray from JavaScript to write CSS. In JavaScript, CSS classes look similar and CSS classes are named in camel cased versions of the property name.
CSS written as JavaScript objects can be leveraged directly in React components by using the style
attribute on components.
Here is an example:
1// lets have some CSS in js: style.css.js 2 const button = { 3 backgroundColor: '#202020', 4 padding: '12px', 5 borderRadius: '2px', 6 width: '100%' 7 } 8 export default { button } 9 10 //importing and using in a JSX 11 import styles from './style.css.js' 12 13 const button = () => { 14 return <button style={styles.button}>Vanity Button</button> 15 }
The code above is a clear example of using CSS as a JavaScript object and it allows us to have styling that is unique to that element.
Another thing you can do when defining the CSS as a JavaScript object is use variables as the values for the style property. In this way it can change depending on some conditions:
1// lets have some CSS in JS: style.css.js 2 var variables = { 3 backgroundColor: '#202020' 4 } 5 var button = { 6 backgroundColor: variables.backgroundColor, 7 padding: '12px', 8 borderRadius: '2px', 9 width: '100%' 10 } 11 export default { button }; 12 13 // importing and using in a JSX 14 import styles from './style.css.js' 15 16 const button = () => { 17 return <button style={styles.button}>Vanity Button</div> 18 }
One of the major wins with CSS as JavaScript objects is it requires no setup to get it working. Since it’s plain JavaScript you are good to go. Although CSS in JavaScript is extensible it does have its limitations in various areas.
CSS modules are not very different from the method above. However, with CSS modules, we can write our usual CSS code and import that into our component.
Here is an example:
1// lets have some CSS in JS: style.css 2 .button { 3 background-color: #202020; 4 padding: 12px; 5 border-radius: 2px; 6 width: 100px; 7 } 8 9 10 // Importing and using in a jsx 11 import styles from './style.css' 12 import React from 'react'; 13 14 export default class Button extends React.Component { 15 render(){ 16 return ( 17 <button className={styles.button}>Vanity Button</button> 18 ) 19 } 20 }
In the code above, you can see that the CSS is actual CSS and not a JavaScript object. However, this in itself will not work and we will need to configure our loader to make it work.
Most of the configuration for our application to work with CSS modules will be done in the webpack configuration file. So assuming you have set up a React application using create-react-app
, you will need to take control of the configuration of your app using the eject
command:
1$ yarn eject
? With normal apps not created using
create-react-app
you can configure Webpack in a similar way as shown in this article.
After ejecting, we can update our webpack.config.dev.js
file to reflect our setup for CSS modules. In the webpack configuration file, replace the content below:
1{ 2 loader: require.resolve('css-loader'), 3 options: { 4 importLoaders: 1, 5 }, 6 },
with the following configuration:
1{ 2 loader: require.resolve('css-loader'), 3 options: { 4 importLoaders: 1, 5 modules: true, 6 localIdentName: "[name]__[local]___[hash:base64:5]" 7 }, 8 }
With the change, we can now use CSS modules in our project. We have also set up hashing to properly generate names for the classes being used in the modules.
? You can use a shorthand to specify the same loader like so:
loader: 'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]``'
If you are not using create-react-app
, adding the required options and setting up the extractTextPlugin
will be the way to set up CSS Modules:
1import ExtractTextPlugin from 'extract-text-webpack-plugin' 2 3 module.exports = { 4 .... 5 plugins: [ 6 .... 7 new ExtractTextPlugin('style.css') 8 ] 9 }
Now we can reconfigure our loader to find our CSS files and bundle them together:
1loader: ExtractTextPlugin.extract( 2 'style-loader', 3 combineLoaders([ 4 { 5 loader: 'css-loader', 6 query: { 7 modules: true, 8 localIdentName: '[name]__[local]___[hash:base64:5]' 9 } 10 } 11 ]) 12 )
Let’s take things a step further using the react-css-module
. You can install this via the npm registry:
1$ yarn add react-css-modules
With react-css-modules
we can have our code like this now:
1// lets have some CSS: style.css 2 .button{ 3 background-color: #202020; 4 padding: 12px; 5 border-radius: 2px; 6 width: 100px; 7 } 8 9 //importing and using in a jsx 10 import styles from './style.css' 11 import React from 'react'; 12 import CssModules from 'react-css-modules'; 13 14 class Button extends React.Component{ 15 16 render(){ 17 return ( 18 <button styleName='button'>Vanity Button</button> 19 ); 20 } 21 } 22 export default CssModules(Button, styles);
? Using the
react-css-modules
, we don’t have to specify the style as an object, but rather we can specify it as a class name and have the style object added to the entire component using theCssModule
function.
To demonstrate how CSS Modules can be used we will be creating a To-do application (yes I know, again). The demo implements CSS Modules, with every component implementing its own style. You can download the demo on GitHub.
In the config/webpack.config.dev,js
file and the config/webpack.config.prod.js
file, we update the CSS loaders configurations to use the CSS modules:
1use: [ 2 ... 3 { 4 loader: require.resolve('css-loader'), 5 options: { 6 importLoaders: 1, 7 modules: true, 8 localIdentName: "[name]__[local]___[hash:base64:5]" 9 }, 10 } 11 ... 12 ],
Next, we installed the react-css-modules
package by running the command below in our terminal:
1$ npm install react-css-modules
Now we can create a CSS file called todoItem.css
and paste in the following:
1.TodoItem{ 2 width: 100%; 3 min-height: 30px; 4 padding:12px; 5 border-bottom: 1px solid #ededed; 6 } 7 8 .removeOp{ 9 content: "/f00d"; 10 color: red 11 }
And then paste this in our JavaScript file:
1import React from 'react' 2 import propTypes from 'prop-types' 3 import CssModules from 'react-css-modules' 4 import s from './todoitem.css' 5 6 class TodoItem extends React.Component{ 7 render(){ 8 return ( 9 <div styleName="TodoItem"> 10 {this.props.title} 11 <span styleName="removeOp" onClick={()=>this.props.remove(this.props.id)}>click to remove</span> 12 </div> 13 ) 14 } 15 } 16 17 TodoItem.propTypes = { 18 title: propTypes.string.isRequired, 19 id: propTypes.number.isRequired, 20 remove: propTypes.func.isRquired 21 } 22 export default CssModules(TodoItem, s)
Above you see we can easily use the CssModules
package in exporting our components while hooking them to the style implementation. We can use the styleName
property to specify the specific styles we want in the stylesheet.
We have considered how you can use CssModules
to change the way you work with styles in your React application. You can combine this method with a CSS preprocessor like Sass or Less.
We also learned about the methods of including CSS in our React components. While both methods share certain advantages, the implementation differs: one is actual CSS living in CSS files wired with JS modules using imports and hashed class names at build time, the other is actual JS from the start and is composed and attached to elements in the form of inline styles at execution time.
If you have any questions and feedback, please leave them as a comment below.