Dev

By Carlos Santana on
Reading time: 5 minutes

Webpack-ytfX9.png

In the last post, Implementing React with Webpack 4 we learned how to implement React using Webpack 4 and we split the Webpack configuration. So far we are able to build our bundle and run the application as a static page. In this post, we will add the Webpack Dev Server to run the React application in an actual server and restart the server every time we make a change. Also, we are going to implement CSS preprocessors such as Sass, Stylus, and Less.

Installation

First, we need to install the following packages:

npm install webpack-dev-server css-loader mini-css-extract-plugin style-loader

If you want to use Sass, you need to install:

npm install sass-loader node-sass

If you prefer Stylus, then you need to install:

npm install stylus-loader stylus

Or if you like Less, then install:

npm install less-loader less

Implementing Webpack Dev Server

Now that you installed Webpack Dev Server let's configure our package.json, first let's add a new script to start the application.

{
  "name": "my-webpack-project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "dependencies": {
    "@babel/core": "^7.2.2",
    "@babel/preset-env": "^7.3.1",
    "@babel/preset-react": "^7.0.0",
    "@babel/register": "^7.0.0",
    "babel-loader": "^8.0.5",
    "css-loader": "^2.1.0",
    "html-webpack-plugin": "^3.2.0",
    "mini-css-extract-plugin": "^0.5.0",
    "node-sass": "^4.11.0",
    "react": "^16.7.0",
    "react-dom": "^16.7.0",
    "sass-loader": "^7.1.0",
    "style-loader": "^0.23.1",
    "webpack": "^4.29.0",
    "webpack-cli": "^3.2.1",
    "webpack-dev-server": "^3.1.14"
  },
  "scripts": {
    "start": "webpack-dev-server --mode development --open",
    "build": "webpack --mode production",
    "build:development": "webpack --mode development"
  },
  "keywords": [],
  "author": "Carlos Santana",
  "license": "MIT"
}
File: package.json

As you know, the --mode flag specifies the mode we want to use (the default is production), and the --open flag opens the browser when we start the application. Now you can run the application with the command: npm start

WebpackDevServerHome-UeoEu.png

Great, now our application is running on port 8080, which is the default port of webpack-dev-server. If you want to change it, you can use the --port flag to specify which port you want to use:

  "scripts": {
    "start": "webpack-dev-server --mode development --open --port 3000",
    "build": "webpack --mode production",
    "build:development": "webpack --mode development"
  },
File: package.json

The cool thing about webpack-dev-server is that if you update any component, you will see the change reflected immediately. For example, if you modify the Home component like this: 

import React from 'react';

const Home = () => <h1>Updated Home</h1>;

export default Home;
File: src/components/Home/index.jsx 

You can see the reflected change in the same page without refreshing the page manually:

WebpackDevServerHomeUpdated-2Ft5Z.png

Let's add Sass, Stylus, or Less to our project to have some styles in the application. You have to edit the file located at webpack/configuration/module.js and add style-loader, css-loader, and the loader we want for Sass (sass-loader), stylus (stylus-loader), or less (less-loader):

export default {
  rules: [
    {
      test: /.(js|jsx)$/,
      exclude: /node_modules/,
      use: 'babel-loader'
    },
    {
      test: /.scss$/, // Can be: .scss or .styl or .less
      use: [
        {
          loader: 'style-loader'
        },
        {
          loader: 'css-loader',
          options: {
            modules: true,
            localIdentName: '[name]_[local]_[hash:base64]',
            sourceMap: true
          }
        },
        {
          loader: 'sass-loader' // sass-loader or stylus-loader or less-loader
        }
      ]
    }
  ]
};
File: webpack/configuration/module.js

If you choose Sass, then you have to create the Home.scss file and add some basic styles: 

$color: green;

.Home {
  color: $color;
}
File: src/components/Home/Home.scss

Now in your Home component, you have to import the Sass file like this: 

import React from 'react';
import styles from './Home.scss';
// For Stylus
// import styles from './Home.styl';
// For Less
// import styles from './Home.less';

const Home = () => <h1 className={styles.Home}>Updated Home</h1>;

export default Home;
File: src/components/Home/index.jsx

Each import line (the commented ones) is for a different preprocessor. Use the line you want and remove the others. Sass will generate this style:

UpdatedHomeWDS-LdDVb.png

If you want to test Stylus, then just create the Home.styl file and change the configuration in Webpack.

$color = blue

.Home 
  color: $color
File: src/components/Home/Home.styl

Or if you prefer to test Less, then create Home.less.

@color = yellow;

.Home {
  color: @color;
}
File: src/components/Home/Home.less

Behind the scenes

Probably you're curious about how the styles are being rendered. If you inspect the site, you will see something like this: 

HomeHTML-H9GmN.png

As you can see, the <link> tag is being injected dynamically with a temporal URL that contains our compiled CSS, and then the class name is something like "Home_Home_hMHTH2..." this is because of the Webpack configuration localIdentName: "[name]_[local]_[hash:base64]". With this configuration, we are creating isolated styles, which means that we will never affect any other class if we use the same name.

Mini CSS Extract Plugin

Right now we are injecting dynamically our styles, but normally for the production environment, we want to extract all the CSS code and minify it into a style.css file, for this purpose we can use mini-css-extract-plugin which you already installed.

The Mini CSS Extract Plugin implementation is really easy, basically, we need to modify our Webpack plugins file like this:

import HtmlWebPackPlugin from 'html-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';

const isProduction = process.env.NODE_ENV === 'production'

const plugins = [
  new HtmlWebPackPlugin({
    title: 'DEV Education',
    template: './src/index.html',
    filename: './index.html'
  })
];

if (isProduction) {
  plugins.push(
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[id].css'
    }),
  );
}

export default plugins;
File: webpack/configuration/plugins.js

Then you also need to modify your Webpack rules:

import MiniCssExtractPlugin from 'mini-css-extract-plugin';

const isProduction = process.env.NODE_ENV === 'production';

const rules = [
  {
    test: /.(js|jsx)$/,
    exclude: /node_modules/,
    use: 'babel-loader'
  }
];

if (isProduction) {
  rules.push({
    test: /.scss$/,
    use: [
      MiniCssExtractPlugin.loader,
      {
        loader: 'css-loader',
        options: {
          modules: true,
          localIdentName: '[name]_[local]_[hash:base64]',
          sourceMap: true
        }
      },
      'sass-loader',
    ]
  });
} else {
  rules.push({
    test: /.scss$/, // Can be: .scss or .styl or .less
    use: [
      {
        loader: 'style-loader'
      },
      {
        loader: 'css-loader',
        options: {
          modules: true,
          localIdentName: '[name]_[local]_[hash:base64]',
          sourceMap: true
        }
      },
      {
        loader: 'sass-loader' // sass-loader or stylus-loader or less-loader
      }
    ]
  })
}

export default {
  rules
};
File: webpack/configuration/module.js

As you can see we are using Mini CSS Extract Plugin just for production. For any other environment, we use style-loader, css-loader and sass-loader directly as before. That's why I love splitting the Webpack configuration into smaller files, in this way we avoid huge configuration files and help us to be more organized. The last thing you need to do is to add the NODE_ENV=production to your build script in the package.json.

  "scripts": {
    "start": "webpack-dev-server --mode development --open",
    "build": "NODE_ENV=production webpack --mode production",
    "build:development": "webpack --mode development"
  }
File: package.json

Then if you run npm run build you should see the main.css file into your dist folder:

Only members can see all the codes
You can Login or Sign Up

File: dist/main.css

If you liked this post and you want to learn more about Webpack, React and more then you should consider get my React Cookbook.

avatarLeave a comment

Your comment

Only members can comment. You can Login or Sign Up