Server-Side-Rendering with react-router and Meteor

Server Side Rendering (SSR) is the ultimate step to unify Single Page Applications and classic (server generated HTML) websites.

partial SSR on Facebook.com Facebook immediatly sends HTML but frequent updated contents (realtime) or the less important ones are updated at a later time.

What is SSR in brief?

Server side generated HTML: the page is fully or partially rendered in the server. The benefit is the Search Engine Optimization (SEO) and an immediate user feedback with less "loading.." and spinning wheels. It can be a React generated DOM server-side and it doesn't need javascript to be loaded to be visible.

Embedded state: it is also defined as "dehydrate/rehydrate state", it means the first request gives you also the current state of the HTML rendered as JSON data. In this way the client-side has a start point to resume without waiting the initial data state from another request to the server (or a ready subscription in Meteor).

isomorphic routing: you need a router and the routes on the server and on the client, because after the first full request the further requests will be managed by the router (client-side) and only limited parts of the page will be re-rendered from a request to another one.

The simplest SSR with react-router and Meteor

reactrouter:react-router-ssr has almost everything you need to get started.

meteor add reactrouter:react-router-ssr  

It will also include:

  • reactrouter:react-router version 1.0.*-beta, the most used router with react components

  • reactrouter:react-router-ssr: a wrapper around react-router to enable isomorphic features in Meteor. It integrates dehytratate/rehydrate a state from meteorhacks-fast-render.

  • react and react-meteor-data: react and the mixin to manage the reactive data inside a react component.

All the example will be inside a file components.jsx inside your meteor project which will be loaded on the client and on the server.

Here the full source code react-router-ssr-demo and the live demo: http://react-router-ssr-demo.meteor.com/

// components.jsx
App = React.createClass({

  getInitialState() {
    ItemsSub = Meteor.subscribe("items", () => {
      this.setState({isReady: true});
    });

    return {
      isReady: false,
    };
  },

  render() {
    return (
      <div>  
        <header>Header { (this.state.isReady) ? "(..sub ready, live data now!)" : null }</header>

        {this.props.children}

        <footer>Footer</footer>
      </div>
    );
  }
});
...

App is the layout component and it subscribes to the data used by it's child components Home and Another. Those components will be loaded by the router in-place replacing {this.props.children}. If you what to explicitly separate the "React components" from "Meteor-React components" look at the meteor-flux-leaderboard example, it uses Meteor + Flux Alt store + React.

/// Isomorphic Router
...
const {Route, Router} = ReactRouter;

Meteor.startup( function() {  
  AppRoutes = (
    <Router  >
      <Route component={App}>
        <Route path="/" component={Home} />
        <Route path="another" component={Another} />
      </Route>
    </Router>
  );

  ReactRouterSSR.Run(AppRoutes);
});

react-router works on the client and on the server but the routes need to be loaded differently ReactRouterSSR.run does diffent things in client and server.

Notes

  • if you use react-router and you don't want a full page reload you need to use the component <Link to="/path"> instead of <a href="">.
  • if the subscription isn't ready the content is displayed anyway via SSR HTML, if you want, you can use the parent component to display a warning to the user.
  • FlowRouter-SSR is an alternative Meteor-specific router approch and it also manage the <title> and other metatags on the server-side.
  • If you are interested in SSR with Meteor check this bugs: #40
  • if you are interested in SSR with React without Meteor look at these other examples: fluxity-starter-auth, isomorphic500, react-isomorphic-starterkit, isomorphic-hot-loader.

I hope you will find the right way :)

corso javascript