Webpack and React is awesome

Note! This article has aged quite a bit. Though it still has valid points, I would suggest reading some of my laster articles on Webpack to get up to speed.

Previously I have written a couple of articles about how to set up a workflow with React JS. The solution provided was browserify with gulp. If React JS is not your thing I think you still will get a lot of value out of this article, so please read on.

To build a workflow for React JS with browserify and gulp you need a lot of dependencies and you have to write quite a bit of code. You might appreciate the flexibility gulp brings, but as the requirements of bundling your project grows, your workflow implementation becomes very complex. Nevertheless there are some core concepts you want to bring in to your React JS workflow:

Depending on your project you will of course have less or more requirements, but this is what I believe to be the core of it. What we are going to talk about in this article is how WebPack can help you very easily set up a workflow to handle these core concepts and some pretty amazing stuff on top of that. How does lazy loading your code sound? Or hot swapping React JS components without refreshing the browser? Loading CSS, less and sass with require? What about loading images that automatically inlines below a specific file size? These are some of the things we are going to talk about. If you want to get your hands dirty as you read, please do so, but it is definitely not required. You can also find an example workflow over at this repo.

WebPack

Maybe like me you have also heard about WebPack. What motivated me to write this article was not my discovery of WebPack, but the presentation by Pete Hunt. He goes straight to the point of how WebPack differs and what makes it awesome. This is not the impression you get from looking at the WebPack site or reading its documentation. There is a webpack-howto that gives you a very simple and easy introduction to WebPack and we are actually going to go through the concepts mentioned there, though in more detail. We are also going to take it a bit further. We are going to use webpack-dev-server to introduce hot swapping of not only css, but also your JavaScript. That is pretty cool. Okay, so lets get going.

Installing and setting up webpack and webpack-dev-server

First of all you will need to install both webpack and webpack-dev-server, using NPM. npm install webpack and npm install webpack-dev-server. This will allow you to run the command webpack and webpack-dev-server directly in your terminal. You will be using webpack to deploy your code and webpack-dev-server to run your workflow. We will first look at the workflow.

Let us just briefly talk about the script we have added here: "webpack-dev-server --devtool eval --progress --colors". When you run npm run dev in your terminal this is what will be executed. It runs webpack-dev-server with some arguments. –devtool eval will add source urls to your code, which will make sure that any errors point to the right file and line. It is really important that you do it like this. Sourcemapping can be achieved with –d, but that is a lot slower. Use that for production if you want sourcemapping. –progress and –colors will just improve the feedback you get in the terminal when running your workflow. –content-base build points to where you have your custom index.html located.

Running the workflow

Let us create an HTML file that our webpack-dev-server can serve when we fire up the workflow. Add an index.html file to the build folder and put something like this in it:

When we now run the following in our terminal, npm run dev, and go to localhost:8080 we will of course not see anything, but our console logs out: “Loaded the app component”. If you do any changes to the files WebPack will rebundle your app automatically. So making a change and refreshing the browser will show your changes. Okay, so now we have the basics down. Let us install React JS and see how we would implement a transformer from JSX to JavaScript.

WebPack loaders

You will have to install the loaders we need for the workflow, and the first one is jsx-loader. Run npm install jsx-loader --save in your terminal and head into the webpack.config.js file and we will do a small configuration:

What we are basically doing here is telling WebPack that whenever we try to require something that ends with .js it should run the contents of that file through the jsx-loader. Thats it! No need for the JSX comment at top of file or the jsx extension. If you prefer creating .jsx files you just change to /.jsx$/. Now lets install React JS and create our first component, npm install react. And in our AppComponent.js file we insert:

Since we changed the configuration we need to stop the webpack-dev-server and start it again with npm run dev. When you refresh the browser, you see “Hello world!” on the screen. But we have met an issue here. When we do a change to a file it takes up to 300 ms to rebundle our application, and that with only two application files. In my book that is way too slow for a workflow.

Handling vendors

When you create a bundling process in your workflow you want it to be as fast as possible. In Browserify you would solve this by creating two different bundles, one for vendors and one for your application. The vendors bundle is only bundled once and your application bundle is being watched for changes and rebundled when that occurs. This strategy does not work with WebPack though, because WebPack can not have two completely separate bundles and allow one of them to require from the other. That does not mean there is no solution. It took me some time to figure this one out, but the solution is quite simple.

We are going to change things around a bit. Instead of depending on npm for our browser vendors, we are going to use bower. So go ahead and install bower, npm install bower -g and then React JS, bower install react --save. Now we have react installed in a bower_components folder instead. To add this file to your project, create the following configuration:

So what we did here was bascially tell WebPack when require(‘react’) is resolved in the code, it will use the react.min.js file located in bower_components. We also made sure that WebPack does not parse the file, because it is not necessary. You will actually get a warning if not using noParse. The reason is that WebPack more effectively compress a vendor by source, then a preminified file. We are talking about a few kilobytes, so it is up to you what is more important. Fast rebundling or an absolute minimum file size. The rebundle speed has now dropped from 250-300 ms to 50-100 ms. That is a huge difference and when working with UI it will be the difference of “instant browser reload” and “waiting for reload”.

A small pro tip here is to create a method to add vendors. This makes it very easy to add, change and remove vendors:

Specifying chunks

Currently we have one chunk in our WebPack bundle that includes our application and react. The chunk is loaded with the javascript file bundle.js. Larger applications will probably need to split the bundle into more chunks, which means more files. WebPack has a fantastic feature we are going to look at shortly, but for now, let us put all our vendors into a specific vendors chunk.

Restart the workflow and everything works as expected. So why would you want to do this? It is very unlikely that your vendors will change, but you will probably do bug fixes etc. on your application. So when a user visits the application the first time both vendors.js and bundle.js will be cached. If bundle.js is updated due to a bug fix, vendors.js will still be cached and only bundle.js will be downloaded again.

Multiple lazy loaded entries

Okay, lets dive into one of the really cool parts of WebPack. Lets say you are building a single page application that has a home-page and an admin-page. When users arrive at the home-page you do not want to download all the JavaScript and CSS of the admin-page. That is very easy to achieve. Let me show you with a minimal configuration:

If you follow along on the network tab of the web console you will see network requests fire when clicking the hyperlinks. So now we have optimized our project by not downloading admin specific code if you only visit the home page. If you would go to admin and then back to home it would not trigger a new download, as the chunk is already downloaded. WebPack by default loads the chunks from the root url (‘/‘), but you can define the public path in your config where the bundles should be loaded.

In this configuration React JS was included in the bundle.js file, but you could have created a specific vendors chunk like we did earlier. It all depends on how you want to load the files.

Optimizing with a common chunk

So what if both our Home.js file and our Admin.js required a third file, Shared.js. That would actually result in the contents of Shared.js to appear in both Home.js and Admin.js, which is definitely not what we want. We want WebPack to put modules used by multiple entry points into a common chunk. So let us do that:

We create a CommonsChunkPlugin and pass three arguments. The first argument refers to what entry point we want to put the shared code. The default entry point name is “main”, which points to [‘./app/main.js’]. The second argument would be the name of the file, that holds the shared code, but since we already have an output for our main entry point (bundle.js) we leave it at null. The third argument tells webpack to look for common modules in our lazely required modules. There is actually a fourth argument here too which is a number. The number states how many of these lazy modules that needs to share a module before it is put into our main bundle.

An important note here is that the modules (main.js, Home.js or Admin.js) themselves does not have to require the common module, any dependency within a dependency will count. An example of this would be if Home.js required a module named InnerHome.js and InnerHome.js required our Shared.js module. The Shared.js module would still be included in main bundle.

Live reload

Live reload is a common thing in a workflow, but we are going to take it a step further with WebPack. But lets first setup a normal live reload. webpack-dev-server supports this out of the box, all we have to do is put it in our config, fire it up in the right mode and add another script tag. Let us continue on the multiple entry point example:

Fire up the server again with npm run dev and whenever you do a file change the browser will refresh. Now, you could actually omit this script insertion in the index.html file and go to localhost:8080/webpack-dev-server instead. At the top of the browser you will see a rebundle status and your application is put into an iframe. Personally I do not like this as it changes the environment of the application (iframe), but also visually breaks your app with the bundle indication at the top. But is very cool indeed :-)

Lets do the hot swap!

I will not go into detail about the general hot swapping in WebPack, but I will show you something quite awesome with React JS. Let us use the current example and update the main.js file:

So as stated in the comment whenever we now do a change to a component the application will actually rerender itself without refreshing the page. There are lots of possibilities with this hot swapping and you have to create your own strategy for it. This is an example of using routing and rerendering with React JS whenever any module has changed.

Loading CSS

So another very cool thing about WebPack is how you can require CSS files directly in your javascript. To allow for this we simply need to install a style-loader and a css-loader, npm install style-loader --save and npm install css-loader --save. We insert the loaders into our config:

Out of the box any changes to the css/less will update without refreshing the page. But the really cool thing is something that happens “under the hood”. All url() and @import statements will be handled as a require expression. So how does that benefit us? Lets look at images and fonts.

Images and fonts

A typical css file that uses fonts and images. Now, webfonts is a science in itself and heavily depends on your target browsers. In font loading examples we normally see 4 different formats added. woff, ttf, svg and eot. But that is hard to optimize, because only one of those formats will actually be used. Converting the font files to inline base64 string would cause all 4 to be included, increasing the file size unnecessarily.

Looking at caniuse.com we see that modern browsers support woff and svg. So depending on your project I would suggest sticking to only one of those two formats. Doing so will let us optimize it. Let us look at the code so that you see what I mean:

So now you see why multiple fonts would be a bad idea as you would inline content that would never be used. No matter, it is important to be careful with inlining as it will take longer to download the CSS and if changes to the CSS is made it will need to bring down also your inlined images and fonts, which would not be necessary if they were external files.

Deploying your application

To produce a production ready application we have to add a new script to our package.json file:

Summary

WebPack is just an awesome tool. It takes application bundling to a whole new level. It is really too bad that documentation is lacking. Hopefully this article gave you some insight into what makes WebPack great. Have a look at this simple example that lets you enjoy all the functionality mentioned in this article. Also take a look at this webpack react cookbook @bebraw and I am working on: webpack-react-cookbook.