Writing a web app with React and Redux (an implementation of flux architecture) is truly a joy… once it’s set up. Setting it up, on the other hand, is not quite as enjoyable. Let’s look at how to set up webpack and babel to compile your React (and possibly ES6) code, as well as implement hot module reloading. Since all these libraries are new and rapidly changing, getting all of the the packages to play nicely together can be a headache. Here’s how to do it (on November 29, 2015, at 3:13pm PT at least).
Webpack is a “module bundler”. Make sense? Cool, let’s move on.
require statements in your code, which imports code (modules, or files) into other modules (or files) that you can then use in that code. This allows you to write clear, modular code, and separate functionality into different files and directories.
Webpack comes with standard functionality, but requires helper libraries to perform tasks specific to your needs. For example, if you’re writing a React + Redux app, you are likely using JSX (in your React components) and/or ES6 syntax (because many React and Redux docs are written in ES6). Unfortunately, browsers are unable to understand JSX or ES6. So, tough luck!
Hot module reloading
As I mentioned, webpack must traverse all your React code to compile and bundle it into a file that is usable by the browser. But what happens when you make a small change to one of your React components? Do you have to wait for webpack to re-bundle your entire application?
Not with hot module reloading! Hot module reloading (HMR for short) is a plugin that allows webpack to insert any changes “inline” without having to re-bundle the entire app. This saves a lot of time and also makes you feel like a superstar developer. Win win.
Version numbers matter
Like, seriously matter. Like, will-give-you-a-headache-if-you’re-not-careful matter. The reason for this is that a lot of these libraries (React, Redux, Babel) are relatively new and are rapidly changing. Sometimes, these changes are breaking and result in incompatibilities with the other helper libraries you’re using.
For example, Babel recently released v6.0. Unfortunately, v6.0 led to breaking changes with babel-plugin-react-transform, which allows for hot module reloading.
So… yeah. Version numbers matter. I will go through a current set up that works for me… right now. But my general advice is that if you find an example online that is working, just copy those version numbers into your
package.json and run with it.
So what do I need to install?
Here’s a list of what you need:
The most important part of all this is that the HMR libraries are not yet ready for Babel v6.0, so you will need to use v5.x. There are developers working on upgrading the HMR packages, and it should happen soon. If you’re interested, this would be a great open-source contribution to make.
webpack.config.js file is where you specify all of webpack’s configurations for your specific project. Here’s an example config:
devtool option is set so that if there is an error encountered during runtime, the error will specify the line number in the file it originated from, not the line in the bundled file.
entry array specifies the “starting point” for webpack to start compiling your app. Generally, this will be your
client.js file which initializes your app and renders it to the DOM. In the case where you’d like to use hot module reloading, you need to also specify the “hot module entry point”. I believe it will be
'webpack-hot-middleware/client' for every project.
output option specifies the output location of the bundled, compiled file. Generally, this will be the folder that you will be serving to the client, such as a
public folder. When you run
webpack from the command line in your project folder, it will enter your app at the entry point, traverse the module dependencies, compile a bundled JS file, and put it into the
path folder with the
filename you specify. I will explain the
publicPath option in the next section.
plugins options shown above are required for hot module reloading. The
module config is where you specify the specific compiler options you’d like to use for each file type. In this case, we set the
test option to find all
.js files, and then use
babel-loader to act on those files. You can add more
loaders as needed. For example, you will likely want to add a loader for CSS files.
webpack + server file
Here’s how we implement webpack and HMR into our server file:
We are setting up our server to
app.use webpack as middleware with
webpack-hot-middleware. As you can see, the
webpack-dev-middleware references the
publicPath option from our webpack config file. This means that webpack will compile the bundled file and serve it up at the
publicPath for you.
So, let’s say you set up your express server to serve your app at port 3000. You will be testing your app at
http://localhost:3000. Since I have my
publicPath set to be
'/', it will place
http://localhost:3000/. Thus, my
index.html will load
bundle.js like so:
Let’s say we had set
publicPath to be
'/public'. Then, webpack would serve up
bundle.js at http://localhost:3000/public and our
index.html would have to load
bundle.js like so:
Since we’re using Babel to compile our code (from the
babel-loader option in the
webpack.config.js), you’ll need to add a
.babelrc to the root of your project. The
.babelrc file specifies options for how Babel should compile your code. With Babel v5.x your
.babelrc should look like this:
plugins option specifies that we want to use the
babel-plugin-react-transform library to enable the React components to be transformed when compiled. We specify how we want it to be transformed in the
extra option; namely, we want to use the
react-transform-hmr library so that we can hot module reload our components.
Starting your app
Personally, I enjoy using nodemon to serve my apps on my local machine. nodemon “watches” your files and will automatically restart the server upon any changes. However, we don’t want it watching every file, since any changes to the components will be hot module reloaded and will not need to restart the server. Luckily, we can specify files or directories for nodemon to ignore. For example, if you have React components in a directory called “components”, you can start your server by running:
Webpack can be confusing when you first start working with it. It is a great tool that dramatically improves a developer’s workflow and app structure. However, it takes care of a lot of stuff under the hood, and so can seem mysterious at times and hard to customize. Add this to the fact that a lot of the plugins and helper libraries are in constant flux (pun intended), you now have a potentially headache-inducing set up on your hands. Try out the setup in this blog (derived from this simple todo list demo), or try to mimic (not just copy and paste) a set up from another example. Either way, do not give up! Seeing your components update immediately in the browser is an amazing feeling. Using Redux and React is an amazing feeling. Getting it (finally) working in the first place… well, that is a priceless feeling.