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.

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';

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

import() in resolve

Instead of having static imports, we will use the dynamic import function to import our modules. We will import our components using a route's resolve object.

A route's resolve function is called every time it matches. However, import calls automatically re-use the results of a previous call, so we do not have to worry about extra network requests.

A route's resolve function should return a Promise; import, conveniently, returns a Promise. In our respond function, instead of referencing values imported at the top of the file, we can reference the result of the resolve function using the resolved property passed to the respond function.

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.

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

Other Approaches

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

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