Server Rendering with React Router

Arpith Siromoney
2 min readDec 19, 2015

--

I build apps and websites, and lately I’ve been building isomorphic/universal websites with React. This is how you can use React Router to render on the server with Koa.

The basics are straightforward:

  1. Your Koa app uses React Router’s match()
  2. match() takes your routes and the url, and a callback
  3. The callback will either receive an error or a redirect location or render props

If you’ve got an error or a redirect you can use Koa’s this.throw and this.redirect, and if you’ve got nothing you can throw a 404. On the other hand, if you’ve got renderProps you can then pass React Router’s <RouterContext {…renderProps} /> to react-dom/server’s renderToString function.

require('babel-core/register');
var ReactDOMServer = require('react-dom/server');
var ReactRouter = require('react-router');
var koa = require('koa');
var render = require('koa-ejs');
var path = require('path');
var RouterContext = require('./RouterContext');
var App = require('./components/App');
var app = koa();
render(app, {root: path.join(__dirname, 'view')});
app.use(function *() {
const routes = {
path: '/',
component: App
};

var reactString;
ReactRouter.match({
routes: routes,
location: this.url
}, (error, redirectLocation, renderProps) => {
if (error) {
this.throw(error.message, 500);
} else if (redirectLocation) {
this.redirect(redirectLocation.pathname + redirectLocation.search);
} else if (renderProps) {
reactString = ReactDOMServer.renderToString(RouterContext(renderProps));
} else {
this.throw('Not Found', 404);
}
});

yield this.render('layout', {react: reactString});
});var port = process.env.PORT || 3000;
app.listen(port);
});

A couple of things thrown in there:

  1. I’m using Babel’s require hook to support the jsx in my React component
  2. This doesn’t transpile the file it’s in, so I’m using plain routes
  3. This is also why I’ve placed <RouterContext /> in a separate file
  4. Finally, I’m using koa-ejs to provide the HTML around the react string.

My RouterContext.jsx looks like:

var React = require('react');
var RoutingContext = require('react-router').RoutingContext;
module.exports = (renderProps) => <RoutingContext {…renderProps} />;

This is because the current version of React Router provides RoutingContext which has since been renamed to RouterContext (which is what the docs talk about).

That’s about it! Let me know your thoughts and suggestions!

--

--