Code Splitting

If you are bundling an application with a lot of routes, users of your application may be downloading a lot of unnecessary content for the initial page render. Using code splitting, you can reduce the initial download size for your application by splitting code that is conditionally loaded into a separate bundle that is only downloaded when it is needed.

Note: This guide assumes that you are using Webpack 2+ to bundle your application.

An app without code splitting#

Let's start out by describing our application's routes without code splitting. We will import each route's component from the files where they are defined.

import Home from './components/Home';
import Contact from './components/Contact';
import ContactMethod from './components/ContactMethod';

const routes = [
  {
    name: 'Home',
    path: '',
    response: () => {
      return {
        body: Home
      };
    }
  },
  {
    name: 'Contact',
    path: 'contact',
    response: () => {
      return {
        body: Contact
      };
    },
    children: [
      {
        name: 'Contact Method',
        path: ':method',
        response: () => {
          return {
            body: ContactMethod
          };
        }
      }
    ]
  }
];

import() in match#

Instead of having static imports, we will use the import() function to import our modules. We will import our components by adding a property to a route's match object. The property name for the function is how we will access the resolved data in the route's response() function.

match functions are called every time a route matches. However, import() calls automatically re-use the results of a previous call, so we do not have to worry about extra network requests.

Here we will name the match function for importing a component body, since it will be set as the response's body property.

match.body() should return a Promise; import(), conveniently, returns a Promise. In our response() function, instead of referencing values imported at the top of the file, we can reference the result of the match.body() function using resolved.body.

import() resolves with a module object. If the component is a default export (export default MyComponent), we can access the component through the imported module object's default property.

const routes = [
  {
    name: 'Home',
    path: '',
    match: {
      body: () => (
        import('./components/Home')
          .then(module => module.default)
      ),
    },
    response: ({ resolved }) => {
      return {
        body: resolved.body
      };
    }
  },
  {
    name: 'Contact',
    path: 'contact',
    match: {
      body: () => (
        import('./components/Contact')
          .then(module => module.default)
      ),
    },
    response: ({ resolved }) => {
      return {
        body: resolved.body
      };
    },
    children: [
      {
        name: 'Contact Method',
        path: ':method',
        match: {
          body: () => (
            import('./components/ContactMethod')
              .then(module => module.default)
          )
        },
        response: ({ resolved }) => {
          return {
            body: resolved.body
          };
        }
      }
    ]
  }
];

Other Approaches#

The approaches taken here are not the only way to do code splitting. Another approach is to skip the match method and do code splitting at other points in your application (e.g. react-loadable).

Whatever path you decide to go, hopefully this has shown you that setting up code splitting with a match function is fairly simple to do. If you are using Webpack and want to reduce your initial bundle size, match functions are a good way to accomplish this.