Skip to content

Instantly share code, notes, and snippets.

@mxstbr
Last active February 16, 2021 18:33
Show Gist options
  • Save mxstbr/85b1c37276162b6ceb33 to your computer and use it in GitHub Desktop.
Save mxstbr/85b1c37276162b6ceb33 to your computer and use it in GitHub Desktop.
Code splitting react-router routes with webpack 2

NOTE: Sokra confirmed I had a misunderstanding, Webpack is still a static build tool and the below won't work. It's still a nice concept though...

With webpack 1, code splitting react-router routes is quite tedious. It requires us to repeat the getComponent function over and over again. (See here for an explanation how it works with webpack 1)

Example:

<Router history={history}>
  <Route
    path="/"
    getComponent={(location, callback) => {
      require.ensure([], function (require) {
        callback(null, require('./HomePage.jsx'));
      });
    }}
  />
  <Route
    path="/about"
    getComponent={(location, callback) => {
      require.ensure([], function (require) {
        callback(null, require('./AboutPage.jsx'));
      });
    }}
  />
</Router>

The getComponent function is almost the same across all routes, that's not very DRY. The problem is that with webpack v1, it needed the requires as static strings because it analyses the code before evaluation, and it needs to somehow determine there which modules should go into seperate chunks.

With webpack 2 though, we're getting System.import and dynamic expressions! (src) This'll make code splitting react-router routes so much easier. Imagine a component like this:

class Page extends React.Component {
  render() {
    <Route
      path={this.props.path}
      getComponent={(location, callback) => {
        System.import(this.props.component)
          .then((component) => {
            callback(null, component);
          });
      }
  }
}

Used like this:

<Router history={history}>
  <Page path="/about" component="./AboutPage.jsx" />
</Router>

And all of these <Page>s are now in seperate chunks! Code splitting react-router routes: easy with webpack 2.

Note: This is just an idea, none of the new code above is tested.

@hufeng
Copy link

hufeng commented Jul 4, 2016

cool

@hufeng
Copy link

hufeng commented Jul 4, 2016

because this issue. webpack/webpack#2188

class Page extends React.Component {
  render() {
    <Route
      path={this.props.path}
      getComponent={(location, callback) => {
        System.import(this.props.component)
          .then((component) => {
            callback(null, component.default || component);
          });
      }
  }
}

@dinesh2609
Copy link

Any idea on how to send props along with component when used with code splitting..

In the above example

@sagiavinash
Copy link

sagiavinash commented Feb 5, 2017

With this Page definition we can completely abstract out normal and onDemand routes

class Page extends React.Component {
  render() {
    if (this.props.isOnDemand) {
      return (
        <Route
          {...this.props}
          getComponent={(location, callback) => {
            System.import(this.props.component).then((component) => {
              callback(null, component.default || component);
            });
          }}
        >{this.props.children}</Route>
      );
    } else {
      return <Route {...this.props} />;
    }
  }
}

@rubencodes
Copy link

Just, for the record: I wasn't able to implement this several reasons:

  1. React-Router@3.0.2 (at least in my testing) was not happy with direct non-Route children 😕
  2. WebPack@2.2.1 wouldn't allow me to System.import a dynamic path—I had to hard code in the paths for it to work (e.g. System.import('/my/path/here'). This is conflicting with it's documentation, which seems to imply you can do this; I think you have to provide at least part of the path for it to make guesses about all the files you would like to include.

So maybe I did something wrong, but I, at least, was not able to get this to work through this really really nice-looking abstraction.

@kamleshchandnani
Copy link

With webpack 2.6 I tried this
import(this.props.chunkname) but this doesn't seems to work, where chunkname = "./container/componentName" but this is not resolved at runtime

@ananddodia
Copy link

ananddodia commented Aug 25, 2017

I implemented code splitting same way. Separate modules js files are build. So webpack build is successfully completed.
Now when i load index page. It loads but when i open new page. New page module is loaded dynamic successful.
But issue it that new js file that loaded, that component is not rendered. It does not show any error also.
<Route path="connections" getComponent={ (location, cb) => { if (__SERVER__) { Promise.all('./containers/Connections/Connections').then(loadRoute(cb)).catch(errorLoading); } else { // System.import('./containers/Connections/Connections').then(loadRoute(cb)).catch(errorLoading); require.ensure([], (require) => cb(null, require('./containers/Connections/Connections').default), 'Directory'); } } } />
this module is not rendered

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment