Package detail

react-router-server

gabrielbull719MIT4.2.3

Server Side Rendering library for React Router v4

code-splitting, react, react-component, react-router

readme

React Router Server

Build Status Code Climate npm version Gitter

Server Side Rendering library for React Router v4.

Help wanted!

If anyone is interested in taking over this project please let me know.

Table Of Content

  1. About
  2. Installation
  3. Examples
  4. Usage
  5. API
  6. Contributing
  7. License

About

This library allows to fetch states for your components on the server side and mount them on the client side.

It also allows to do code splitting by providing a component that can be used to load modules splitted by Webpack 2.

Installation

npm install react-router-server --save

Examples

Example

A working example using Webpack bundle and preloading is provided here. To try for yourself, you can clone it and run it. This will provide a server accessible at http://localhost:3000.

git clone git@github.com:gabrielbull/react-router-server-complex-example.git
npm install
npm start

Usage

Server Side rendering

To render an app that has code splitting or state fetching, you need to load the modules and states required by your app before rendering. react-dom/server does not offer a function to do that, but you can use the renderToString function provided by this library. This function will return a promise that will return the rendered app once the modules and states are loaded.

import { renderToString } from 'react-router-server';
import App from './path/to/app';

renderToString(<App/>)
  .then(({ html }) => {
    // send html to client side
  });

Code Splitting

The code splitting consist of a component that you can use to load modules splitted by Webpack 2. It also allows you to get information on the modules required to render a page so that you can preload the modules before displaying the page on the client side.

To use code splitting, you will need to import the Module component and provide the System.import call inside the module property of that component. Then, you need to defined a callback function as the children of the component.

import { Module } from 'react-router-server';

<Module module={() => System.import('./Foo')}>
  {module => module ? <module.default/> : <div>Loading...</div>}
</Module>

To preload the modules on the client side, you can use the preload method and pass the modules from the server into that method.

In the following example, __INITIAL_MODULES__ would be provided by the server and rendered in the HTML document as a global variable.

import { preload } from 'react-router-server';
import { render } from 'react-dom';
import App from './path/to/app';

preload(__INITIAL_MODULES__).then(() => render(<App/>, document.getElementById('#my-app')));

You can get the modules from the renderToString function on the server side and extract them from your webpack stats by using the extractModules method. For more information on usage with webpack, check the usage with webpack part of this read me.

import { renderToString, extractModules } from 'react-router-server';
import App from './path/to/app';
import stats from './path/to/stats';

renderToString(<App/>)
  .then(({ html, modules }) => {
    modules = extractModules(modules, stats);
    // send html and modules to client side
  });

To be able to use System.import calls on the server side, you will need to install the babel-plugin-system-import-transformer plugin.

Fetch State

On the server side, you will often need to fetch data before rendering your component and then pass that data to the client side so that the components are in sync.

To fetch data for your components, use the fetchState decorator provided by this library. The fetchState decorator takes two arguments, mapStateToProps and mapActionsToProps. mapStateToProps allows you to map the state to the props of your component while mapActionsToProps allows you to map the done action to the props of your component.

import * as React from 'react';
import { fetchState } from 'react-router-server';

@fetchState(
  state => ({ message: state.message }),
  actions => ({ done: actions.done })
)
class MyComponent extends React.Component {
  componentWillMount() {
    if (!this.props.message) {
      setTimeout(() => {
        this.props.done({ message: 'Hello world!' });
      }, 10);
    }
  }

  render() {
    return (
      <div>{this.props.message}</div>
    );
  }
}

To pass that state from the server to the client, you need to wrap the client app with the ServerStateProvider and pass the state from the server into that component's state property.

In the following example, __INITIAL_STATE__ would be provided by the server and rendered in the HTML document as a global variable.

import { ServerStateProvider } from 'react-router-server';
import App from './path/to/app';

<ServerStateProvider state={__INITIAL_STATE__}>
  <App/>
</ServerStateProvider>

You can get the state from the renderToString function on the server side.

import { renderToString } from 'react-router-server';
import App from './path/to/app';

renderToString(<App/>)
  .then(({ html, state }) => {
    // send html and state to client side
  });

Usage with Webpack

You can extract the required modules per requests when running your server to pass them to the client side. This allows you to preload your modules before running the client side app. To do so, you need to get the stats from Webpack.

There are many ways to do this, but we recommend using the stats-webpack-plugin. Here's a code sample that you can add to your webpack config's plugins section. This will create a stats.json file that you can use to extract the required modules for your app.

[
  new StatsPlugin('stats.json', {
    chunkModules: true,
    exclude: [/node_modules/]
  })
]

To extract the modules, you can use the extractModules function and pass the modules provided by the renderToString as well as the stats generated by webpack. See the code splitting usage part of this documentation to learn more on code splitting.

To be able to use System.import calls on the server side, you will need to install the babel-plugin-system-import-transformer plugin.

Usage with React Router

To use with React Router v4, you can pass the Module component to the Route component of React Router.

import { Route } from 'react-router';
import { Module } from 'react-router-server';

<Route
  exact
  path="/"
  render={matchProps => (
    <Module module={() => System.import('./Foo')}>
      {module => module ? <module.default {...matchProps}/> : <div>Loading...</div>}
    </Module>
  )}
/>

Usage with Redux

If you are rehydrating state with redux instead of using ServerStateProvider, all you need is access to the done action so the server can wait for async stuff to complete. In that case, you can use the withDone decorator, which is a shorthand for fetchState(null, ({ done }) => ({ done })).

import * as React from 'react';
import { connect } from 'react-redux';
import { withDone } from 'react-router-server';
import { setMessage } from './actions';

@withDone
@connect(state => state.message, { setMessage })
class MyComponent extends React.Component {
  componentWillMount() {
    // using async actions
    const { setMessage, done } = this.props;
    setMessage('Hello world').then(done, done);
  }

  render() {
    return (
      <div>{this.props.message}</div>
    );
  }
}

For more details on usage with redux, check this boilerplate.

API

extractModules

extractModules(modules, stats)

modules: modules provided by the renderToString method.

stats: stats generated by webpack.

fetchState

fetchState(mapStateToProps, mapActionsToProps)

mapStateToProps(state): function to map the state provided by the done action to props in your component;

mapActionsToProps(actions): function to map the actions to props in your component; Currently, only the done action exists and is used when you are finished fetching props.

withDone

Shorthand for fetchState(null, ({ done }) => ({ done }))

Module

The Module component allows to do code splitting. The Module component takes these propeties:

module: a function that returns a System.import call. E.G. () => System.import('./Foo')

children: a function. E.G. {module => module ? <module.default/> : null}

preload

preload(modules)

modules: array of modules passed by the server side to the client side for preloading.

renderToString

Async version of ReactDOM.renderToString.

renderToString(element)

element: The element to render

Returns an object ({ html, state, modules }) with:

html: the rendered HTML

state: the app state provided by fetch state

modules: the app modules provided by code splitting

ServerStateProvider

The ServerStateProvider component is used for providing the server state to the client side. Provided by the state prop.

Contributing

Everyone is welcome to contribute and add more components/documentation whilst following the contributing guidelines.

License

React Router Server is licensed under The MIT License (MIT).

changelog

CHANGELOG

4.2.3 (January 20th, 2018)

  • Adds support for matching webpack imports using arrow functions

4.2.2 (August 1st, 2017)

  • Fixed some issues with HMR

4.2.1 (May 14th, 2017)

  • Added withDone decorator and Redux example

4.2.0 (April 28th, 2017)

  • Made compatible with new versions of React and Webpack

4.1.1 (April 24th, 2017)

  • Fixed issue with fetchState

4.1.0 (April 18th, 2017)

  • Added exception handling in the render methods

4.0.1 (April 17th, 2017)

  • Made compatible with React 15.5
  • Added typescript definitions

4.0.0 (March 22th, 2017)

  • Removed support for react-router beta and cleaned up

2.1.0 (February 12th, 2017)

  • Added support for react-router beta and release

2.0.0 (January 21st, 2017)

  • Fixed issue with render not resolving
  • Added renderToStaticMarkup method

1.1.2 (January 21st, 2017)

  • Added support for Webpack 2.2

1.1.1 (November 7th, 2016)

  • Fixed issue with webpack production mode

1.1.0 (November 6th, 2016)

  • Fixed issue with webpack import with no module id
  • Fixed issue with modules and states not being passed on second render

1.0.5 (November 6th, 2016)

  • Fixed problem with rendering pages that do not have modules or states

1.0.4 (November 6th, 2016)

  • Removed unused files from dist

1.0.3 (November 6th, 2016)

  • Improved build process

1.0.2 (November 6th, 2016)

  • Fixed issue with the preload method that receives non array parameters

1.0.1 (November 5th, 2016)

  • Removed debug code

1.0.0 (November 5th, 2016)

  • Simplified API
  • Replaced importModule by Module component for code splitting
  • Added extractModules to extract modules from webpack stats
  • Renamed preloadModules to preload
  • Removed the custom Match component
  • Removed the importWebpackBundle function

0.5.1 (November 5th, 2016)

  • Fixed bug that would update an unmounted component

0.5.0 (November 3rd, 2016)

  • Added support for Node 7
  • Updated to react-router v4.0.0-alpha.5

0.4.0 (October 11th, 2016)

  • Changed importWebpackBundle to return the whole module instead of only the default export

0.3.0 (October 8th, 2016)

  • Updated to react-router v4.0.0-alpha.4

0.2.2 (October 8th, 2016)

  • Fixed bug that would instantiate new components when not necessary

0.2.1 (October 6th, 2016)

  • Fixed issue with webpack bundle that would import the wrong module

0.2.0 (October 5th, 2016)

  • Added webpack bundle support
  • Changed import to be a callback on the importModule function

0.1.4 (October 4th, 2016)

  • Renamed fetchProps to fetchState
  • Fixed bug with calling done when re-rendering

0.1.3 (October 4th, 2016)

  • Fixed issue with render not resolving

0.1.2 (October 3rd, 2016)

  • Added unit tests
  • Improved example

0.1.1 (October 2nd, 2016)

  • Fixed issue with importModule method

0.1.0 (October 2nd, 2016)

  • Initial release