Webpack - React starter kit

November 1, 2015

Webpack is a module bundler. It takes modules with dependencies and emits static assets representing those modules.

If you rely on a build tool with your development team (and you definitely should), most probably you are relying on Make, Grunt or Gulp. But when it come to bundling assets and dependencies for the browser, and you want to rely on javascript modules (either following the commonJS, AMD, or ES6 module pattern), simply concatenating JavaScript files won’t be that easy anymore. Luckily we have tools that help us in this direction: Browserify, Webpack and JSPM. In this post, we’ll go over setting up Webpack for developing with react using Babel to transpile ES6 and JSX code.

If all of that still confusing to you, read surviveJS’s article ‘Webpack compared’ to understand Webpack better by seeing it explained into a historical context.

If you were just looking for a quick and minimal starter setup, go ahead and fork my webpack-react starter kit, otherwise continue reading as I’ll walk you trough that setup.


A simple two pages app scenario

Let’s imagine that inside an /app folder we have two javascript files:

  • Profile.js
  • Home.js

    Those two apps don’t do much, apart rendering the respective header on the page using react-dom. Both have react and react-dom as dependencies ,and both use ES6 and/or JSX syntax.

Home.js:

import React from 'react';
import ReactDOM from 'react-dom';

var Home = React.createClass({
  render () {
    return <h1>Home</h1>
  }
});

ReactDOM.render(<Home/>, document.getElementById('app'));

and Profile.js

var React = require('react');
var ReactDOM = require('react-dom');

var { h1 } = React.DOM;

var Profile = React.createClass({
  render () {
    return h1({}, "Profile");
  }
});

ReactDOM.render(<Profile/>, document.getElementById('app'));

Note: Just for the sake of illustrating some example variations, Home.js rely on a JSX syntax while Profile.js not. Home.js rely on the ES6 module pattern while Profile.js uses the CommonJS one.


Defining npm dependencies

Before being able to configure webpack we want to make sure that we have everything we need. We therefore define our dependencies and dev-dependenciesInside package.json:

"dependencies": {
  "react": "^0.14.1",
  "react-dom": "^0.14.1"
},
"devDependencies": {
  "babel-core": "^6.0.14",
  "babel-loader": "^6.0.0",
  "babel-preset-es2015": "^6.0.14",
  "babel-preset-react": "^6.0.14",
  "webpack": "^1.12.2"
}

Configuring webpack

Webpack looks for a file named webpack.config.js in the root of our project. In our case we could have something like:

var webpack = require('webpack');

var plugins = [
  new webpack.optimize.CommonsChunkPlugin('public/shared.js'),
];

module.exports = {
  entry: {
    Home: './app/Home.js',
    Profile: './app/Profile.js'
  },

  output: {
    filename: 'public/[name].js'
  },

  plugins: plugins,

  module: {
    loaders: [
      {test: /\.js$/, loader: "babel", query: {presets:['react', 'es2015']}}
    ]
  }
};

Let’s walk through it:

var webpack = require('webpack');

We require all the modules we may need to build our configuration file.

var plugins = [
  new webpack.optimize.CommonsChunkPlugin('public/shared.js'),
];

The CommonsChunkPlugin allows us to build all the shared common javascript modules into a separate single js file. In this case, we’ll want to output a file named shared.js inside the public directory. At this moment webpack still won’t do anything with it, as the only thing that it will look for is what come within its exports, in other words, the webpack.config.js is nothing more then a js module itself:

module.exports = {
  ...
}

We expose the configuration following the export module pattern

entry: {
  Home: './app/Home.js',
  Profile: './app/Profile.js'
},

output: {
  filename: 'public/[name].js'
},

We export a reference to the entries that we want to bundle telling webpack to compile a different file for each of them in the public directory.

plugins: plugins,

Exporting the plugin array we declared earlier.

module: {
  loaders: [
    {test: /\.js$/, loader: "babel", query: {presets:['react', 'es2015']}}
  ]
}

We make sure to tell webpack to use the babel-loader for any .js file to correctly transipile our files. As we rely on the latest Babel 6 that come with presets plugin, we want to make sure to pass the ones we need as a query object. Learn more about Babel 6.

If we did everything right, we could now run webpack in the terminal (make sure you installed webpack globally first)

$ npm i -g webpack
$ webpack

and we should find 3 new files builded for us in the public directory:

  • shared.js
  • Home.js
  • Profile.js

We can now require those final assets from their respective HTML files (home.html and profile.html):

home.html

<!doctype html>
<html>
<meta charset="utf-8">
<title>Home</title>
<div id="app"></div>
<script src="./shared.js"></script>
<script src="./Home.js"></script>

profile.html

<!doctype html>
<html>
<meta charset="utf-8">
<title>Profile</title>
<div id="app"></div>
<script src="./shared.js"></script>
<script src="./Profile.js"></script>

Some webpack extras.

While you can run webpack with the –watch flag so that webpack watches all dependencies and recompile on change, you can go one step further and install the WEBPACK DEV SERVER that will run a little express server for you serving the webpack bundle.

Enabling some extra plugins to optimize code for production:

var plugins = [
  new webpack.optimize.CommonsChunkPlugin('public/shared.js'),
  new webpack.DefinePlugin({
    'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
  })
];

if (process.env.NODE_ENV === 'production') {
  plugins.push(new webpack.optimize.DedupePlugin());
  plugins.push(new webpack.optimize.UglifyJsPlugin());
}

By adding the Dedupe and UglifyJs plugins we can optimize our bundles from:

  • public/Home.js 773 bytes
  • public/Profile.js 551 bytes
  • public/shared.js 675 kB


to

  • public/Home.js 325 bytes
  • public/Profile.js 207 bytes
  • public/shared.js 133 kB


Read more about optimizations with webpack ***

Final thoughts and resources.

Webpack is an awesome tool that may not solve anything, but it does solve the difficult problem of bundling. Coupled with react and babel makes it a very good development tool.

To help you kickstart your development with react using Webpack fork the webpack-react starter kit

comments powered by Disqus