One of the most fundamental parts of frontend development is styling, in this post, we will see how to use styles with webpack. This post is a continuation of my earlier post where I have explained How to set up react project with webpack and babel. So if you have never set up a react project with webpack then I'll suggest you read my previous post and then come back to this post or happen to have a react project already set up and wonder how to work with styles then you are in the right place.
In this post, we will see how to set up both CSS and a CSS pre-processor like Sass with webpack one by one.
As we know webpack is a module bundler and it is responsible for analyzing the files and bundles all your need to run the application in a single JavaScript output file which we commonly call bundle.js. Webpack by default only understands javascript and in order to make webpack understand other resources like .css, .scss, etc we need the help of loaders to compile it. Let see how to do it.
Setting up CSS with webpack
At first, let's install some dev dependencies.
npm i --save-dev css-loader style-loader
- The definition says css-loader interprets
@import
andurl()
like import/require() and will resolve them. What do we mean by this?css-loader
will take all the CSS from the CSS file and generate it to a single string and will pass this tostyle-loader
. - style-loader will take this string and will embed it in the style tag in index.html.
Now let's add the configuration in webpack.config.js. Add this line inside rules in the module.
...
module: {
rules: [
{
test: /\.(css)$/,
use: ['style-loader','css-loader']
}
]
}
...
Here the test
property will tell the webpack to use
style-loader
and css-loader
for all the .css files.
(Note: The order in which webpack apply loaders is from last to first, so as said earlier the css-loader
will generate the output string which will be used by the style-loader
.)
The overall content of webpack.config.js will be:
const path = require('path');
module.exports = {
mode: "development",
entry: path.resolve(__dirname, './src/index.js'),
devtool: "eval-source-map",
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
},
{
test: /\.(css)$/,
use: ['style-loader','css-loader']
}
]
},
resolve: {
extensions: ['*', '.js', '.jsx']
},
output: {
path: path.resolve(__dirname, './public'),
filename: 'bundle.js',
},
devServer: {
contentBase: path.resolve(__dirname, './public'),
hot: true
},
};
Next, we will add an app.css file with the following content.
.container {
height: 100vh;
background-color: #E7E3EB;
}
.header {
padding: 15px 20px;
background-color: #6200EE;
}
h1 {
color: #FFF;
}
App.jsx
import React from 'react';
import './app.css';
const App = () => {
return (
<div className="container">
<div className="header">
<h1>Welcome to React application</h1>
</div>
</div>
)
}
export default App
and now run npm start
and you'll see the output in your browser.
Configuration for your production environment.
If you have a webpack configuration for production then you'll need a different configuration for using CSS. At first, install mini-css-extract-plugin
npm i --save-dev mini-css-extract-plugin
and now extract the miniCssExtractPlugin and replace the style-loader
with MiniCssExtractPlugin.loader
and add the MiniCssExtractPlugin
in plugin.
MiniCssExtractPlugin
extracts CSS and create a CSS file per JS file.
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: "production",
entry: path.resolve(__dirname, './src/index.js'),
devtool: "source-map",
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
},
{
test: /\.(css)$/,
use: [MiniCssExtractPlugin.loader,'css-loader']
}
]
},
resolve: {
extensions: ['*', '.js', '.jsx']
},
plugins: [new MiniCssExtractPlugin()],
output: {
path: path.resolve(__dirname, './public'),
filename: 'bundle.js',
},
};
Now, run npm run build
and you will see the external files generated in your public folder like main.css and main.css.map. If you want to check whether your build folder has everything as expected you can check it on by running it on the local web server, run this command on your command line.
npx http-server public
This will provide you a URL that you can visit in a browser.
Setting up Sass with webpack
If you happen to prefer Sass more than CSS like me then you need to install some packages to set up Sass with webpack.
npm i --save-dev sass-loader node-sass
-
node-sass provides binding Node.js to LibSass (The C version of Sass).
sass-loader
document says "The sass-loader requires you to install either Node Sass or Dart Sass on your own. This allows you to control the versions of all your dependencies, and to choose which Sass implementation to use." Essentially this loader has internal dependencies which requirenode-sass
. - sass-loader loads a Sass/SCSS file and compiles it to CSS.
Now let's update the webpack.config.js.
const path = require('path');
module.exports = {
mode: "development",
entry: path.resolve(__dirname, './src/index.js'),
devtool: "eval-source-map",
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
},
{
test: /\.(s(a|c)ss)$/,
use: ['style-loader','css-loader', 'sass-loader']
}
]
},
resolve: {
extensions: ['*', '.js', '.jsx']
},
output: {
path: path.resolve(__dirname, './public'),
filename: 'bundle.js',
},
devServer: {
contentBase: path.resolve(__dirname, './public'),
hot: true
},
};
We just need to add the sass-loader
ahead of css-loader
, so now first, the .scss file compiles back to CSS and after that process remains the same as explained above.
Next, let's change the app.css file to app.scss and update the file with our favorite sass features.
app.scss
.container {
height: 100vh;
background-color: #E7E3EB;
.header {
padding: 15px 20px;
background-color: #6200EE;
h1 {
color: #FFF;
}
}
}
Now run npm start
the result will be the same as above but now we have written Sass in place of CSS.
Sass configuration for production environment
Configuration for production remains pretty same as we did for CSS setup, we will be using the same mini-css-extract-plugin
to extract our separate CSS file, it is just that we need to add sass-loader
ahead of css-loader
as we did earlier for our development configuration. The updated file will look like this.
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: "production",
entry: path.resolve(__dirname, './src/index.js'),
devtool: "source-map",
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
},
{
test: /\.(s(a|c)ss)$/,
use: [MiniCssExtractPlugin.loader,'css-loader', 'sass-loader']
}
]
},
resolve: {
extensions: ['*', '.js', '.jsx']
},
plugins: [new MiniCssExtractPlugin()],
output: {
path: path.resolve(__dirname, './public'),
filename: 'bundle.js',
},
};
Now build your project again by running npm run build
and check the output by running in the local webserver.
Extras
Congratulations!! 🎉 You have now successfully set up CSS and Sass with webpack in your react project but your project needs a bit more than styles to make it more attractive and user friendly like images and fonts. I am will give a short tutorial on how to set them up with webpack.
Let's add a dev dependency to support these file formats.
npm i --save-dev url-loader
Font Setup
First, download fonts, for this tutorial purpose I have downloaded Open Sans Italic from google fonts and moved them in a folder (src/Assets/Fonts/). Now create a font.scss file and add the font face inside
@font-face {
font-family: 'Open Sans Italic';
src: url('./Assets/Fonts/OpenSansItalic.eot');
src: url('./Assets/Fonts/OpenSansItalic.eot?#iefix') format('embedded-opentype'),
url('./Assets/Fonts/OpenSansItalic.woff') format('woff'),
url('./Assets/Fonts/OpenSansItalic.ttf') format('truetype'),
url('./Assets/Fonts/OpenSansItalic.svg#OpenSansItalic') format('svg');
font-weight: normal;
font-style: italic;
}
and let's import the fonts file inside app.scss and apply the fonts to <h1>
tag.
app.scss
@import url(./font.scss);
.container {
height: 100vh;
background-color: #E7E3EB;
.header {
padding: 15px 20px;
background-color: #6200EE;
h1 {
color: #FFF;
font-family: 'Open Sans Italic';
}
}
}
Next, update the webpack.config.js to support all the file formats.
...
rules: [
....
{
test: /\.(woff|woff2|eot|ttf|svg)$/,
use: {
loader: 'url-loader',
},
},
....
]
...
Now, run npm start
and now your font will be updated.
Image setup
To use images with webpack we just need to add the image file extension which we need inside the test of url-loader
.
Updated webpack.config.js
...
rules: [
....
{
test: /\.(woff|woff2|eot|ttf|svg|jpg|png)$/,
use: {
loader: 'url-loader',
},
},
....
]
...
Now, let's update the app.jsx and app.scss.
App.jsx
import React from 'react';
import './app.scss';
const App = () => {
return (
<div className="container">
<div className="header">
<h1>Welcome to React application</h1>
</div>
<div className="react-logo"></div>
</div>
)
}
export default App
app.scss
@import url(./font.scss);
.container {
height: 100vh;
background-color: #E7E3EB;
.header {
padding: 15px 20px;
background-color: #6200EE;
h1 {
color: #FFF;
font-family: 'Open Sans Italic';
}
}
.react-logo {
background: url('./Assets/Images/react-logo.png') no-repeat center;
height: 500px;
width: 500px;
background-size: contain;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
Now let's run the npm start
again and see the output in the browser.
As we can see our image has been added and the fonts have been updated.
There are plenty of options for all the loaders we have used in this tutorial, I suggest you read the documents and use them according to your project needs or you can just explore them also.
Don't forget to give a ❤️ if you liked it and thanks for reading and if you want to support me then you can just buy me a coffee 😃
Happy coding! 🧑🏻💻
Top comments (5)
Great tutorial! Heads up, your link for 'How to set up react project with webpack and babel' is pointing to google.com
Best tutorial!
Thanks a lot for this article.
I just didn't understand this regex:
\.(s(a|c)ss)$
So I used mine:
\.(c|sa|sc)ss$
.Please, fix the link at the beginning. It leads to the google.
Done! Thanks for pointing out 🙂