React code splitting made easy ✂️.
JavaScript
Switch branches/tags
Nothing to show
Latest commit 029d43e Jun 23, 2017 @neoziro neoziro docs: improve readme
Permalink
Failed to load latest commit information.
src tests: fix tests Jun 23, 2017
.babelrc First version Jun 23, 2017
.eslintignore First version Jun 23, 2017
.eslintrc.json First version Jun 23, 2017
.gitignore First version Jun 23, 2017
.mversionrc First version Jun 23, 2017
.npmignore First version Jun 23, 2017
.nvmrc First version Jun 23, 2017
.travis.yml First version Jun 23, 2017
LICENSE First version Jun 23, 2017
README.md docs: improve readme Jun 23, 2017
package.json tests: fix tests Jun 23, 2017
yarn.lock First version Jun 23, 2017

README.md

loadable-components

Build Status codecov

React code splitting made easy. Reduce your bundle size without stress ✂️.

npm install loadable-components

Webpack permits us to use easily split our code using dynamic import syntax. loadable-components makes it possible to use that awesome feature with React components. It is compatible with react-router and server side rendering. The API is designed to be as simple as possible to avoid useless complexity and boilerplate.

You want a real size demo? Check it out on https://www.smooth-code.com/, it's open source https://github.com/smooth-code/website.

Getting started

// Routes.js
export const Home = loadable(() => import('./Home'))
export const About = loadable(() => import('./About'))
export const Contact = loadable(() => import('./Contact'))
// App.js
import React from 'react'
import { Route } from 'react-router'
import * as Routes from './Routes'

export default () =>
  <div>
    <Route exact path="/" component={Routes.Home} />
    <Route path="/about" component={Routes.About} />
    <Route path="/contact" component={Routes.Contact} />
  </div>

Custom loading

It is possible to add a custom loading component, by default it will render nothing:

export const Home = loadable(() => import('./Home'), {
  LoadingComponent: (props) => <div>Loading...</div>,
})

Error handling

You can configure the component rendered when an error occurs during loading, by default it will render nothing:

export const Home = loadable(() => import('./Home'), {
  ErrorComponent: ({ error, props }) => <div>Oups an error occurs.</div>,
})

Prefetching

To enhance the user you can fetch routes before they are requested by the user.

Prefetch on route loading

import React from 'react'
import { Contact } from './Routes'

Contact.load()

export default () => <div>Hello</div>

Prefetch on hover

import React from 'react'
import { Contact } from './Routes'

export default () =>
  <div>
    <Link
    <Link to="/contact" onHover={Contact.load}>Contact</Link>
  </div>

Server-side rendering

First create a Routes.js containing all your loadable routes:

// Routes.js
import loadable from 'loadable-components'

export const Home = loadable(() => import('client/Home'))

You can use them in your application:

// App.js
import React from 'react'
import { Home } from './Routes'

const App = () =>
  <div>
    <Route exact path="/" component={Home} />
  </div>

Then bootstrap your application client-side using loadComponents:

// main.js
import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter } from 'react-router-dom'
import { loadComponents } from 'loadable-components'
import App from './App'

// Load all components needed before starting rendering
loadComponents().then(() => {
  ReactDOM.render(
    <BrowserRouter>
      <App />
    </BrowserRouter>,
    document.getElementById('main'),
  )
})

The only thing you have to do on the server is to call getLoadableState() and inserting the loadable state in your html:

// server.js
import React from 'react'
import { renderToString } from 'react-dom/server'
import { StaticRouter } from 'react-router'
import { getLoadableState } from 'loadable-components/server'
import App from './App'

let context = {}

const app = (
  <StaticRouter location={...} context={context}>
    <App />
  </StaticRouter>
)

// Extract loadable state from application tree
getLoadableState(app).then(loadableState => {
  const html = renderToString(<YourApp />)
  // Insert style tag into page
  const page = `
    <!doctype html>
    <html>
    <head></head>
    <body>
      <div id="main">${html}</div>
      ${loadableState.getScriptTag()}
    </body>
    </html>
  `
})

Configuring Babel

Dynamic import syntax is natively supported by Webpack but not by node. That's why you have to configure Babel differently for server and client:

To have a different configuration for client and server, you can use Babel env option.

API Reference

loadable

This is the default export. It's a factory used to create a loadable component. Props are passed to the loaded component.

Arguments

  1. getComponent (Function): Function to load component asynchronously.
  2. options (Object): Facultative options to configure component behavior.

options

  1. ErrorComponent (ReactComponent): Component rendered when an error occurs, take two props: error and props.
  2. LoadingComponent (ReactComponent): Component rendered during loading, take the same props from loadable component.
import loadable from 'loadable-components'

const MyLoadableComponent = loadable(() => import('./MyComponent'), {
  ErrorComponent: ({ error }) => <div>{error.message}</div>,
  LoadingComponent: () => <div>Loading...</div>,
})

loadComponents

This method is only required if you use server-side rendering. It loads components used in the page that has been rendered server-side.

import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter } from 'react-router-dom'
import { loadComponents } from 'loadable-components'
import App from './App'

// Load all components needed before starting rendering
loadComponents().then(() => {
  ReactDOM.render(
    <BrowserRouter>
      <App />
    </BrowserRouter>,
    document.getElementById('main'),
  )
})

getLoadableState

This method is only required if you use server-side rendering. It loads components recursively and extract a loadable state from a React tree.

import React from 'react'
import { renderToString } from 'react-dom/server'
import { StaticRouter } from 'react-router'
import { getLoadableState } from 'loadable-components/server'
import App from './App'

const app = (
  <StaticRouter>
    <App />
  </StaticRouter>
)

// Extract loadable state from application tree
getLoadableState(app).then(loadableState => {
  const html = renderToString(<YourApp />)
  // Insert style tag into page
  const page = `
    <!doctype html>
    <html>
    <head></head>
    <body>
      <div id="main">${html}</div>
      ${loadableState.getScriptTag()}
    </body>
    </html>
  `
})

A loadable state has two methods to extract state:

  • loadableState.getScriptTag(): Returns a string representing a script tag.
  • loadableState.getScriptElement(): Returns a React element.

Interoperability

You can implement a loadable component by your own. To do it you have to add LOADABLE Symbol to your component:

import React from 'react'
import { LOADABLE } from 'loadable-components'

class ComponentWithTranslations extends React.Component {
  // Required
  static componentId = 'custom-loadable'
  static async load = () => {
    const response = await fetch('/translations.json')
    const translations = await response.json()
    ComponentWithTranslations.translations = translations
    return translations
  }

  state = { translations: ComponentWithTranslations.translations }

  componentWillMount() {
    ComponentWithTranslations[LOADABLE].load()
    .then(translations => this.setState({ translations }))
  }

  render() {
    const { translations = { hello = 'hello' } } = this.props;

    return <div>{hello}</div>
  }
}

ComponentWithTranslations[LOADABLE] = () => ({
  componentId: 'custom-loadable',
  load: async () => {
    const response = await fetch('/translations.json')
    const translations = await response.json()
    ComponentWithTranslations.translations = translations
  }
})

Inspirations

MIT