Arpith Siromoney 💬

Isomorphic React in Three Simple Steps

An isomorphic react app has three basic parts:

  1. You generate a html file using react-dom/server’s reactDOM.renderToString()
  2. This is then served along with the client javascript file —
  3. — which is pretty much the same as the server-side code, except that it uses reactDOM.render()

The code we’re interested in on the server is:

app.use(function *() {
  var splitURL = this.url.split('/');
  splitURL.shift();
  var username = splitURL.shift();
  var id = splitURL.shift();
  var user = yield fetchUser(username).then((user) => {
    if ((id) && (user.posts.indexOf(id) > 0)) {
      user.posts.splice(user.posts.indexOf(id), 1);
      user.posts.unshift(id);
    }
    var promiseArr = user.posts.map(id => fetchPost(username, id));
    return Promise.all(promiseArr).then((data) => {
      user.posts = data;
      return user;
    });
  });
  this.body = generateHTML(user);
});

We’re still serving static files off the public directory, which brings us to the client code:

var splitPathname = window.location.pathname.split('/');
splitPathname.shift();
let username = splitPathname.shift();
let id = splitPathname.shift();
function load(username, id) {
  return fetchUser(username).then((user) => {
    if ((id) && (user.posts.indexOf(id) > 0)) {
      user.posts.splice(user.posts.indexOf(id), 1);
      user.posts.unshift(id);
    }
    var promiseArr = user.posts.map(id => fetchPost(username, id));
    Promise.all(promiseArr).then((data) => {
      user.posts = data;
      let mountNode = document.getElementById(“react-mount”);
      React.render(React.createElement(views.User, user), mountNode);
    });
  });
}

Pretty similar, huh? The file is bundled using webpack and put in the public directory using this webpack.config.js:

var path = require('path');
var webpack = require('webpack'); 
module.exports = {
  entry: [ 
    './main.js'
  ],
  output: { 
    path: path.join(__dirname, 'public'),
    filename: 'main.js',
  },
  module: {
    loaders: [{loader: ‘uglify’}, {
      exclude: /node_modules/,
      loader: 'babel' 
    }]
  },
  plugins: [ 
    new webpack.ProvidePlugin({
      ‘fetch’: ‘imports?this=>global!exports?global.fetch!whatwg-fetch’ 
    })
  ]
};

Note that we’re using whatwg-fetch to support the fetch api on older browsers. We can run webpack using an npm post install script in our package.json:

"scripts": { 
  "start": "node index.js",
  "postinstall": "webpack -p"
}

To recap, we have

  1. Server code that generates html and
  2. Serves both the html and the client code
  3. Which is pretty much the same as the server code

And that’s about it! Congratulations, you’ve made an isomorphic react app!