How to configure CSS and CSS modules in webpack
You’ll learn the following in this post
- How to configure basic CSS in a webpack project using
style-loader
andcss-loader
- How to extract the CSS to its own
style.css
file - How to configure CSS modules with webpack
- How to configure both CSS and CSS modules
How to configure CSS with webpack
Let’s start configuring CSS. I assume you already have a webpack project set up. If you don’t, check out createapp.dev to create your own custom webpack boilerplate.
Create the CSS file and reference it
Before we configure CSS support in the webpack setup, let’s first add a CSS file and use it.
The first thing we are going to do is to add a styles.css
file in the project. Let’s put it in the src
folder.
body {
color: white;
background-color: black;
}
This CSS file creates a black background and white text color. This makes it clear to see if it works or not - if it works the whole page will be black!
Next thing to do is to import it. We’ll do this from a JavaScript file because by default webpack puts the CSS inside the bundle which means we have to reference it from another JavaScript file (we’ll look into how to extract the CSS to its own file later in this tutorial).
Put this in your index.js
file:
import "./styles.css"
Configure webpack css-loader and style-loader
To be able to use CSS in your webpack app, you need to set up a new loader. Out-of-the-box, webpack only understands Javascript and JSON. With a loader, you can translate another type of file to a format that webpack understands and can work with.
There are many webpack loaders and each loader has a specific purpose. You need two loaders to support CSS in your app: css-loader
and style-loader
. Let’s look at how we can configure css-loader
and style-loader
in webpack.
You set up a loader with the module
keyword in your webpack.config.js
.
This is how you configure CSS in your webpack.config.js
:
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}
]
}
The test
keyword tells webpack what kind of files should use this loader. The use
keyword tells webpack which loaders should be run for these files.
As you can see in the config, you need to use two loaders, style-loader
and css-loader
. You also need to install them as NPM dependencies:
npm install --save-dev style-loader css-loader
What does css-loader and style-loader do?
Loaders are just pure JavaScript functions: they take some data as input and do something to that data and returns a transformed version of the data. When you use two loaders in webpack then it takes the output of the first and sends it as input to the second. In our example it takes the CSS file and runs it through css-loader
then it takes the output and runs it as input to the style-loader
Let’s look at what the loaders do.
css-loader
reads the CSS from the CSS file and returns the CSS with the import
and url(...)
resolved correctly. What does that mean? Let’s look at an example. Let’s say you have a url
in the CSS referencing another resource, like an image:
.topbanner {
background: url("topbanner.svg") #00d no-repeat fixed;
}
In this example, we reference topbanner.svg
from the CSS. When css-loader
sees this line, it tells webpack to load this file using an appropriate loader. For this to work you also need to configure a SVG loader, because the file is a SVG file:
module: {
rules: [
// CSS loader here
{
test: /\.svg$/,
use: "file-loader",
},
]
}
To load SVG files we use webpack file-loader.
If you wouldn’t have configured css-loader
you would get an cryptic error like this:
ERROR in ./src/styles.css 1:0
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> .topbanner {
| background: url("zip.svg") #00D no-repeat fixed;
| }
@ ./src/styles.css 1:14-39
@ ./src/index.js
You would get this error even though you had configured a loader for SVG files. Avoid weird erros like that by always using css-loader
for your CSS configuration.
The next loader you configured was the style-loader
. This loader adds the CSS to the DOM so that the styles are active and visible on the page. This is needed because the CSS is put into the bundle.js
file - there is no separate styles.css
file.
How to extract the CSS to its own styles.css file
It’s possible to output a separate styles.css
file by using the mini-css-extract-plugin instead of using style-loader
like we previously did.
The first thing you need to do is to install the dependency
npm install --save-dev mini-css-extract-plugin
Then you need to import the plugin at the top of the webpack.config.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
Next thing is to enable the plugin in the plugin section of your webpack.config.js
file:
plugins: [
new MiniCssExtractPlugin(),
],
And last but not least, you replace the style-loader
with MiniCssExtractPlugin.loader
:
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader, // instead of style-loader
'css-loader'
]
}
Now when you run webpack, it will output main.css
file in the dist folder that you can reference from your index.html
file. (remove the import "./styles.css";
line from index.js
if you added that)
How to configure CSS modules in webpack
Now that you know how to configure pure CSS, let’s look into how to configure CSS modules in webpack.
But first what is CSS modules? From the github repo:
"A CSS Module is a CSS file in which all class names and animation names are scoped locally by default."
CSS is global - the classes you define can be used anywhere. But CSS modules are scoped to the component where they are used.
CSS-tricks.com has a good introduction to CSS Modules that is useful if you want to learn more. In this post, we’ll be focusing on learning how to configure it.
Configuring CSS modules is quite similar to configuring CSS.
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
modules: true
}
}
]
}
you still use the css-loader
and the style-loader
. No extra dependencies are needed. But the difference to configuring CSS is that you add two options to css-loader
.
It’s the modules: true
that tells css-loader
to enable CSS modules.
importLoaders: 1
means that it also applies CSS modules on @import
ed resources.
Using both CSS Modules and global CSS at the same time
So the way to enable CSS modules is to pass some options to the css-loader
. But what if we want to use both CSS and CSS modules. Let’s say you have a layout.css
file which is truely global CSS. Or you use a third-party component that is dependant on global CSS. It’s possible to do it!
We need a convention to use for deciding which files are global CSS and which are CSS modules. The way I like to do it is to use CSS modules only for files ending in *.module.css
, for example modal.module.css
, and all other *.css
files are global.
To configure this we’ll add two loaders to our webpack config: one for CSS and one for CSS modules and we’ll use the include
and exclude
keywords for separating the two. include
and exclude
takes a regexp that decided if the file should be used or not. The complete setup looks like this:
module: {
rules: [
{
test: /\.css$/,
use: [
"style-loader",
{
loader: "css-loader",
options: {
importLoaders: 1,
modules: true,
},
},
],
include: /\.module\.css$/,
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
exclude: /\.module\.css$/,
},
]
}
Now you can use both CSS and CSS modules in your project!
Follow me on Twitter to get real time updates with tips, insights, and things I build in the frontend ecosystem.