Configuring React

From the blog:
ajv - The Fastest JSON Schema Validator - Interview with Evgeny Poberezkin

Facebook's React is a popular alternative for developing web applications. Even if you don't use it, it can be valuable to understand how to configure it.

Get Started Fast with create-react-app#

create-react-app encapsulates a lot of best practices related to developing React applications. It's particularly useful if you want to get started with a little project fast with minimal setup.

One of the main attractions of create-react-app is a feature known as ejecting. This means that instead of treating it as a project dependency, you'll get a full webpack setup out of it.

There's a gotcha, though. After you eject, you cannot go back to the dependency-based model, and you will have to maintain the resulting setup yourself.

Setting Up Babel with React#

The Processing with Babel chapter covers the essentials of using Babel with webpack. There's some React specific setup you can perform, though.

Most of React projects rely on a format known as JSX. It is a superset of JavaScript that allows you to mix XMLish syntax with JavaScript. A lot of people find this convenient as they get something that resembles what they know already while they can use the power of JavaScript.

Some React developers prefer to attach type annotations to their code using a language extension known as Flow. The technology fits React well, but it's not restricted to it. TypeScript is another viable alternative. Both work with JSX.

Configuring with Webpack#

Babel allows us to use JSX with React easily. Some people prefer to name their React components containing JSX using the .jsx suffix. Webpack can be configured to work with this convention. The benefit of doing this is that then your editor will be able to pick up the right syntax based on the file name.

Webpack provides a field known as resolve.extensions that can be used for this purpose. If you want to allow imports like import Button from './Button'; while naming the file as Button.jsx, set it up as follows:

webpack.config.js

...

const common = {
  ...
  resolve: {
    extensions: ['.js', '.jsx'],
  },
};

...

The loader configuration is straight-forward as well. Instead of matching only /\.js$/, we can expand it to include .jsx extension through /\.(js|jsx)$/.

In webpack 1 you had to use extensions: ['', '.js', '.jsx'] to match files without an extension too. This isn't needed in webpack 2.

Configuring with Babel#

To enable JSX with Babel, an addition preset is required. Install it:

npm i babel-preset-react --save-dev

You also have to connect the preset with Babel configuration. Here's the rough idea:

.babelrc

{
  "presets": [
    [
      "es2015",
      {
        "modules": false
      }
    ],
    "react"
  ]
}

Rendering a React Application#

To get a simple React application running, you'll need to mount it to a DOM element first. html-webpack-plugin can come in handy here. It can be combined with html-webpack-template or html-webpack-template-pug for more advanced functionality. You can also provide a custom template of your own to it.

Consider the following example:

webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackTemplate = require('html-webpack-template');

const common = {
  ...
  plugins: [
    new HtmlWebpackPlugin({
      template: HtmlWebpackTemplate,
      title: 'Demo app',
      appMountId: 'app', // Generate #app where to mount
      mobile: true, // Scale page on mobile
      inject: false, // html-webpack-template requires this to work
    }),
  ],
};

module.exports = function(env) {
  ...
};

Now that there's a template and a DOM element for where to render, React needs to be told to render there:

app/index.js

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

ReactDOM.render(
  <div>Hello world</div>,
  document.getElementById('app')
);

It would be possible to extend the application from here. Depending on your tastes, you might want to name the file as index.jsx instead, but sticking with index.js can be acceptable too.

Check out the Configuring Hot Module Replacement with React appendix to learn how to set up hot loading for React code with webpack and Babel.

Babel-Based Optimizations for React#

babel-react-optimize implements a variety of React specific optimizations you may want to experiment with.

babel-plugin-transform-react-remove-prop-types is handy if you want to remove propType related code from your production build. It also allows component authors to generated code that's wrapped so that setting environment at DefinePlugin can kick in and give the same effect without the consumers having to use the plugin.

Using react-lite Instead of React for Production#

React is quite heavy library even though the API is quite small considering. There are light alternatives, such as Preact and react-lite. react-lite implements React's API apart from features like propTypes and server side rendering.

You lose out in debugging capabilities, but gain far smaller size. Preact implements a smaller subset of features and it's even smaller than react-lite. Interestingly preact-compat provides support for propTypes and bridges the gap between vanilla React and Preact somewhat.

Using react-lite or Preact in production instead of React can save around 100 kB minified code. Depending on your application, this can be a saving worth pursuing. Fortunately integrating react-lite is simple. It takes only a few lines of configuration to pull off.

To get started, install react-lite:

npm i react-lite --save-dev

On the webpack side, we can use a resolve.alias to point our React imports to react-lite instead. Consider doing this only for your production setup!

resolve: {
  alias: {
    'react': 'react-lite',
    'react-dom': 'react-lite',
  },
},

If you try building your project now, you should notice your bundle is considerably smaller.

Similar setup works for Preact too. In that case you would point to preact-compat instead. See preact-boilerplate for the exact setup and more information.

Inferno is yet another alternative. The setup is the same and you can find inferno-compat with a similar idea. I discuss these alternatives in more detail at my slide set known as React Compatible Alternatives.

If you stick with vanilla React, you can still optimize it for production usage. See the Setting Environment Variables chapter to see how to achieve this. The same trick works with preact-compat as well.

Exposing React Performance Utilities to Browser#

React provides a set of powerful performance related utilities for figuring out how your application performs. Enabling them takes some setup. After the setup is done, you can access them through your browser console.

To get started, install the needed dependencies:

npm i expose-loader react-addons-perf --save-dev

Next, we need to expose React to the console through the expose-loader. The idea is that we'll bind the React performance utilities to that during development. Here's the webpack loader configuration for exposing React as a global:

{
  test: require.resolve('react'),
  use: 'expose-loader?React',
},

After this, you should be able to access React through a console. To make it possible to access the performance utilities, we need to do one more step. Add the following to the entry point of your application to enable React.Perf during development:

if(process.env.NODE_ENV !== 'production') {
  React.Perf = require('react-addons-perf');
}

If you check out the browser console now, you should be able to access the performance-related API through React.Perf. The utilities allow you to understand better what's taking time and to squeeze the last bits of performance out of your application. The Elements tab in Chrome can be useful as well. You can see how React operates on the DOM as it flashes.

It can be a good idea to install React Developer Tools to Chrome for even more information. This allows you to inspect props and state of your application.

Optimizing Rebundling Speed During Development#

We can optimize React's rebundling times during development by pointing the development setup to a minified version of React. The gotcha is that we will lose propType-based validation. But if speed is more important, this technique may be worth a go. You can hide it behind an environment flag if you want type checking.

In order to achieve what we want, we can use webpack's module.noParse option. It accepts a RegExp or an array of RegExps. We can also pass full paths to it to keep our lives simple.

In addition to telling webpack not to parse the minified file we want to use, we also need to point react to it. This can be achieved using a feature known as resolve.alias just like we did with react-lite above.

We can encapsulate the basic idea within a function like this:

webpack.parts.js

...

exports.dontParse = function(options) {
  const alias = {};
  alias[options.name] = options.path;

  return {
    module: {
      noParse: [
        options.path,
      ],
    },
    resolve: {
      alias: alias,
    },
  };
};

The function can be used like this through webpack-merge:

webpack.config.js

...

merge([
  common,
dontParse({ name: 'react', path: path.join( __dirname, 'node_modules', 'react', 'dist', 'react.min.js', ), }),
... ]); ...

If you try developing your application now, it should be at least a little bit faster to rebuild. The technique can be useful for production usage as well as you avoid some processing then.

module.noParse also accepts a regular expression. If we wanted to ignore all *.min.js files for instance, we could set it to /\.min\.js/. That can be a more generic way to solve the problem in some cases.
Note that aliasing works also with loaders through resolveLoader.alias.
Not all modules support module.noParse, the files included by deps array should have no call to require, define or similar, or you will get an error when the app runs: Uncaught ReferenceError: require is not defined.

Code Splitting with React#

The splitting pattern discussed in the Code Splitting chapter can be wrapped into a React component. Airbnb uses the following solution as described by Joe Lencioni:

import React from 'react';

...

// Somewhere in code
<AsyncComponent loader={() => import('./SomeComponent')} />

...

// React wrapper for loading
class AsyncComponent extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      Component: null,
    };
  }

  componentDidMount() {
    // Load the component now
    this.props.loader().then(Component => {
      this.setState({ Component });
    });
  }

  render() {
    const { Component } = this.state;
    const { Placeholder } = this.props;

    if (Component) {
      return <Component {...this.props} />;
    }

    return <Placeholder>
  }
}

AsyncComponent.propTypes = {
  // A loader is a function that should return a Promise.
  loader: PropTypes.func.isRequired,

  // A placeholder to render while waiting completion.
  Placeholder: PropTypes.node.isRequired
};

Maintaining Components#

One way to structure React projects is to push components to directories which expose their code through a index.js file. Often that's just boilerplate code which you have to add for webpack to resolve correctly. component-directory-webpack-plugin has been designed to alleviate this problem and it allows you to skip index.js while performing lookups based on a naming convention.

A separate tool known as create-index is a different way to solve the same problem. It literally generates those boilerplate index.js files to your project and keeps them up to date.

Conclusion#

There are a lot of aspects to keep in mind when configuring webpack to work with React. Fortunately, this is something you don't have to perform often. Once you have a solid basic setup fitting your needs together, it will take you far.

Previous chapterAuthoring Packages
Next chapterAppendices

This book is available through Leanpub. By purchasing the book you support the development of further content. A part of profit (~30%) goes to Tobias Koppers, the author of Webpack.

Need help?