Tutorial: Webpack setup using React, React-Router and Redux for developing Front End applications. Includes Hot Module Replacement and much more.

In this post I will show you how to build and understand a webpack config file for developing applications with react, reactrouter and redux. I also provide a boilerplate to start developing right away.

If you already know how to set up webpack and wish to learn more on building and understanding react apps, see this post. It includes setting up hot module replacement in react components.

The boilerplate contains:

  • a working example of a filterable table which you can play around with.
  • ES6ES7 Support with Babel
  • Redux dev tools to help you keep track of the app’s state
  • hot module replacement support so you can change modules (defined in webpack config) without having to reload the browser
  • a webpack production config so you can build the app and make it ready for production
  • Sass support, just import your styles wherever you need them
  • eslint to keep your js readable
  • tests (coming soon, I swears)
  • much more…
Here's a Link to the boilerplate so you can download and follow along.

Why Webpack

I have been checking out webpack a lot lately. For those of you that don't know what it is, webpack is a tool that helps you deal with dependencies in your js, like browserify does, and generate a bundle out of them. It doesn't stop at that though, through it's loaders, its able to compile SASS or LESS to css as well, and a whole bunch of other things. As I get deeper in to the react ecosystem it seems most people use it instead of browserify. After some investigation it's easy to see why: webpack brings in stuff right out of the box and can not just replace browserify but also whatever task automator you are using, in my case gulp. Additionally, its hot module replacement and browser error logging are some of the extras that I can't live without anymore. More on them later. Edit: Webpack in the context of this post is used for stand-alone apps (no connection to the back end with templates like mustache). Check out this post for use of webpack for connected set ups.

React, Redux, React-Router

I'm assuming you know what these 3 are. I could spend several posts talking on them alone so let's just get a quick recap on what they are:
  • React: "A javascript library for building user interfaces". Reacts excels at easing development of your UI once you get in to it. It forces you to think of your app in components and sub-components that react whenever the state changes.
  • Redux: "Predictable state container for JavaScript apps". Redux is a library that helps you manage your app's state. It derives from various things, one of them the Flux architecture. Redux forces you to use pure functions to calculate the new state whenever an action happens, so : (oldState, action) => newState
  • React-Router: A router library for react, it connects nicely to react and redux to help your app support routes.
[caption id="attachment_870" align="aligncenter" width="958"]The boilerplate contains this working example of a filterable list The boilerplate contains this working example of a filterable list that uses React, React-Router and Redux[/caption]

Webpack config

Let's start out with the heart of our development: webpack's config file. If you downloaded the boilerplate from the link I gave you earlier, you might notice there's two webpack config files, one is for development, the other one is run once before each deploy to a production server. The only difference is that the production version will minify/uglify your assets so they don't take as much space. Here's the whole webpack config file:
'use strict';

var path = require('path');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    devtool: 'eval-source-map',
    entry: [
        'webpack-dev-server/client?http://localhost:3000',
        'webpack/hot/only-dev-server',
        'react-hot-loader/patch',
        path.join(__dirname, 'app/index.js')
    ],
    output: {
        path: path.join(__dirname, '/dist/'),
        filename: '[name].js',
        publicPath: '/'
    },
    plugins: [
        new HtmlWebpackPlugin({
          template: 'app/index.tpl.html',
          inject: 'body',
          filename: 'index.html'
        }),
        new webpack.optimize.OccurenceOrderPlugin(),
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoErrorsPlugin(),
        new webpack.DefinePlugin({
          'process.env.NODE_ENV': JSON.stringify('development')
        })
    ],
    module: {
        loaders: [
            {
                test: /\.js?$/,
                exclude: /node_modules/,
                loader: 'babel'
            }, 
            {
                test: /\.json?$/,
                loader: 'json'
            },
            {
                test: /\.scss$/,
                loader: 'style!css!sass?modules&localIdentName=[name]---[local]---[hash:base64:5]'
            },
            { test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url-loader?limit=10000&minetype=application/font-woff" },
            { test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "file-loader" }
        ]
    }
};
Eww, big config file. I know. But let's go through it.
'use strict';

var path = require('path');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
Here, we are getting our dependencies. path is a module that helps us manipulate file paths. webpack is of course the webpack module and HtmlWebpackPlugin is a plugin which helps us auto-generate html, you will see it's use later.
module.exports = {
    devtool: 'eval-source-map',
    entry: [
        'webpack-dev-server/client?http://localhost:3000',
        'webpack/hot/only-dev-server',
        'react-hot-loader/patch',
        path.join(__dirname, 'app/index.js')
    ],
    output: {
        path: path.join(__dirname, '/dist/'),
        filename: '[name].js',
        publicPath: '/'
    },
This is the start of the config. Inside we find the property devtool set to eval-source-map. This is related to the debugging, more info here. In the entry property you define what the entry file is going to be. In this case it's app/index.js. Webpack will use this file to gather all the dependencies from the app. Entry also accepts an array of entry files. We also pass in 2 entries related to the webpack-dev-server, more on it later. Finally there's a line for react-hot-loader. I will be talking about RHL in the following article, so stay tuned. In short, what it does is help us hot reload react components. The output property contains where you want the outputted files to go. In this case we say put everything inside of the 'dist' folder. the name of the file returned by webpack will be given dynamically.
plugins: [
        new HtmlWebpackPlugin({
          template: 'app/index.tpl.html',
          inject: 'body',
          filename: 'index.html'
        }),
        new webpack.optimize.OccurenceOrderPlugin(),
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoErrorsPlugin(),
        new webpack.DefinePlugin({
          'process.env.NODE_ENV': JSON.stringify('development')
        })
    ],
plugins are modules that can be created by anyone which can tap into webpack's build process. Without them webpack wouldn't be as interesting. This is where the power of the community can be seen. We use quite a few plugins for our build process, most of them come right out of the box with webpack. Let's see what they are doing:

HtmlWebpackPlugin

Simplifies creation of HTML files to serve your webpack bundles
This plugin handles the creation of HTML files for you. The plugin will generate an HTML5 file that includes all your webpack bundles in the body using script tags. It takes a template file as input which is located at 'app/index.tpl.html' and outputs it into our dist folder. Why is this useful? Because you don't need to write the script tags yourself. Any dependency that your html needs to have is going to be added by this plugin. It's also useful for when we make use of generated filenames that include a hash which changes in every build. This is used for busting the client's browser cache. Without this plugin we would have to manually enter the new hash into the HTML for each build...

OccurrenceOrderPlugin

I'll be honest and say I'm not 100% sure as to what this plugin does, but it is recommended by the docs as it helps optimise your files. I think it gives shorter ID's to more reoccurring modules in your code.
Assign the module and chunk ids by occurrence count. Ids that are used often get lower (shorter) ids. This make ids predictable, reduces to total file size and is recommended.

HotModuleReplacementPlugin

This baby is the best plugin ever for developing web apps. It gives you the ability to change code, save it, and see the change appear almost instantly in your browser, without having to reload it. Best of all, it keeps your app's state as it was before the change, so no need to reproduce all the steps!

NoErrorsPlugin

This is helpful when you use webpack in the CLI because it doesn't stop the process when there is an error.
If you are using the CLI, the webpack process will not exit with an error code by enabling this plugin.

DefinePlugin

This plugin helps us pass variables from webpack to our js files. We can for example pass what environment we are in (dev or prod) so that our js files act accordingly. This way we can turn off development tools if we are in a production environment.

Loaders

The last part of the config file pertains to loaders:
module: {
        loaders: [
            {
                test: /\.js?$/,
                exclude: /node_modules/,
                loader: 'babel'
            }, 
            {
                test: /\.json?$/,
                loader: 'json'
            },
            {
                test: /\.scss$/,
                loader: 'style!css!sass?modules&localIdentName=[name]---[local]---[hash:base64:5]'
            },
            { test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url-loader?limit=10000&minetype=application/font-woff" },
            { test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "file-loader" }
        ]
    }
Loaders are what basically replace other task runners like gulp or grunt. They handle the assets by passing each through a pipeline of transformers. Take for example the sass files, they are passed through the following transformations: sass, css, style.
Loaders allow you to preprocess files as you require() or “load” them. Loaders are kind of like “tasks” are in other build tools, and provide a powerful way to handle frontend build steps. Loaders can transform files from a different language like, CoffeeScript to JavaScript, or inline images as data URLs. Loaders even allow you to do things like require() css files right in your JavaScript! -webpack docs
In our case, it's not CoffeeScript but Babel, which lets us use ES6 features like arrow functions, the 'class' keyword, spead operatorspromises, async/await and all the other good stuff!

Webpack Dev Server

If you check the root of your project you will find a node server called 'server'. This server is only useful for development. Without this server we wouldn't be able to actually run webpack.
The webpack-dev-server is a little node.js Express server, which uses the webpack-dev-middleware to serve a webpack bundle. It also has a little runtime which is connected to the server via Socket.IO. The server emits information about the compilation state to the client, which reacts to those events. You can choose between different modes, depending on your needs. - webpack docs
Things to note:
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var config = require('./webpack.config');
The server requires webpack, webpack-dev-server and the actual config file we defined earlier.
new WebpackDevServer(webpack(config), {
    publicPath: config.output.publicPath,
    hot: true,
    historyApiFallback: true,
    stats: {
        colors: true
    }
}).listen(3000, 'localhost', function (err) {
    if (err) {
        console.log(err);
    }

  console.log('Listening at localhost:3000');
});
The wepack-dev-server contains everything you need to actually run the app so we only need to instantiate it. As first argument we pass it in the webpack object itself, initialized with the config file we look at earlier. The second argument contains configuration pertaining to the server itself. Important here is to check 'hot' to 'true', so we can do hot module replacement.

Run it

So how do you run this boilerplate? It's quite easy really. All you need to do is download the project from github. In your terminal at the root of the project you do npm install and once everything installs, you do npm start Read the next post on how the app itself works


Comments

17 responses to “Tutorial: Webpack setup using React, React-Router and Redux for developing Front End applications. Includes Hot Module Replacement and much more.”

  1. jasan_s Avatar
    jasan_s

    In the project on github HMR does not work

    1. Jean-Pierre Sierens Avatar
      Jean-Pierre Sierens

      Thanks for reporting!

      Gave a temporary solution in the issue you opened:
      https://github.com/jpsierens/webpack-react-redux-react-router/issues/1

      Will investigate how to properly fix this if possible, it seems react-router is the problem

  2. Jean-Pierre Sierens Avatar
    Jean-Pierre Sierens

    Fixed the project, HMR now works. Changed to React Hot Loader 3

  3. is there any example for multiple entries or mostly known as code-splitting

    1. Jean-Pierre Sierens Avatar
      Jean-Pierre Sierens

      Sorry, right now I haven’t looked at code-splitting. I want to in the future though.

  4. Tiago Pina Avatar
    Tiago Pina

    Couldn’t we use – import webpack from ‘webpack’ instead of using require?

    1. Jean-Pierre Sierens Avatar
      Jean-Pierre Sierens

      The webpack config files are ran in nodejs which doesn’t yet support the ‘import’ syntax. You could use babel on the back end though if you really want it.

  5. This is a great kick starter! It is not too complicated, just what most projects need to get going.

    1. Jean-Pierre Sierens Avatar
      Jean-Pierre Sierens

      Glad you liked it!

  6. Doug Wright Avatar
    Doug Wright

    Amen for this. The first webpack blog to actually help me get my app running. Thanks!

    1. Jean-Pierre Sierens Avatar
      Jean-Pierre Sierens

      Np, glad it helped you!

  7. Andrey Kholkin Avatar
    Andrey Kholkin

    Hey man, thanks for this! But just one question… how do i get the dist version to run? index.html just renders a blank page…

    1. Jorge Stamatio Avatar
      Jorge Stamatio

      Also blank page… Did you find out how to run the dist?

      1. jackdurango Avatar
        jackdurango

        Same here. Blank screen and nothing in console

  8. IndigoRhan Avatar
    IndigoRhan

    It’s a nice guide. However, I wish you included a list of all the actual node dependencies.

  9. Atul Kumar PK Avatar
    Atul Kumar PK

    My question is when I run npm install then which file is being compiled at the very beginning? server.js or webpack.conf of webpack.production.conf ? When this conf files are called?

Leave a Reply

Your email address will not be published. Required fields are marked *