Setting up CSS and Sass with Webpack!!
Tags: webdev, react, webpack, tutorial
Published: 2021-04-29 03:07:57

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](https://dev.to/deepanjangh/react-setup-with-webpack-for-beginners-2a8k). 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](https://www.npmjs.com/package/css-loader) interprets `@import` and `url()` 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 to `style-loader`.
* [style-loader](https://www.npmjs.com/package/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.

```javascript
...
   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:

```javascript
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.

```css
.container {
    height: 100vh;
    background-color: #E7E3EB;
}

.header {
    padding: 15px 20px;
    background-color: #6200EE;
}

h1 {
    color: #FFF;
}
```

*App.jsx*

```javascript
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.

![Screenshot 2021-04-28 at 6.51.19 PM](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bt1ps2ajaijyxao83pp1.png)
 
## 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](https://www.npmjs.com/package/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. 

```javascript
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](https://www.npmjs.com/package/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 require `node-sass`.
* [sass-loader](https://www.npmjs.com/package/sass-loader) loads a Sass/SCSS file and compiles it to CSS.

Now let's update the *webpack.config.js*.

```javascript
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*

```css
.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.

```javascript
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](https://fonts.google.com/specimen/Open+Sans) and moved them in a folder *(src/Assets/Fonts/)*. Now create a *font.scss* file and add the font face inside 

```css
@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*
```css
@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.

```javascript
...
   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*

```javascript
...
   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*

```javascript
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*

```css
@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.

![Screenshot 2021-04-29 at 2.06.17 AM](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ovpidfwd6qemmmjw4lyn.png)

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](https://www.buymeacoffee.com/deepanjan) 😃

Happy coding! 🧑🏻‍💻