I decided to write this post, because I didn’t find any blog post, article, or a tutorial that will sum up for developers what the issues they are going to face when they build a universal application.
If you googled a tutorial on how to build a universal react application, you will find a lot, but most of them are just covering the surface, hello-world examples. But as you go further, or even you decided to use one of the universal frameworks/boilerplate, you will start to face some issues and difficulties.

P.S: This post is not a tutorial on building a universal application, and in fact, it is assuming that you already know what universal app is, or what are its benefits, and why people use it, and neither it is a React tutorial, and I assume you already know React, and webpack.


Universal challenges

All challenges related to universal application can be categorised in one of these categories:

  • Universal rendering.
  • Universal routing.
  • Universal data retrieval.
  • Build and development process.
  • Sharing libraries between server and client.

Universal Rendering

Let us start with the basic of a React application and describe the rendering process in React and in Single Page Application in general.
A typical React application will run on the client side (on the browser) and does its rendering of the web pages on the client side. A typical React application consists of many components, and in a “sane” React application, there will be only one root component that will contains all other components as its children.
The entry point for a React app, will be rendering that root component when loading the page, and the code will be similar to this:

import ReactDOM from 'react-dom';
import RootApp from './components/RootApp';
ReactDOM.render(<RootApp />, document.getElementById('root_placeholder'));

In a universal application, everything on the client stays the same, and we add another rendering of the application on the server.
How to do that?: Using React’s    renderToString  
React is universal-friendly library, and has the ability to render on the server using its function: renderToString.
The code in NodeJs application will be similar to this:

import { renderToString } from 'react-dom/server';
import RootApp from './components/RootApp';
export default function serverRenderer() {
    return (req, res, next) => {
        res.status(200).send(`
            <!doctype html>
            <html> 
              <head> <title>App</title> </head>  
              <body>
                <div id="root_placeholder">
                    ${renderToString(<RootApp />)}
                </div>
                <script src="/client.js"></script>
              </body>
            </html>
        `); 
    };
}


So, does client rendering work as well?

After we added the server rendering, the client code stays the same, which means the client code stills has the code to do rendering when the page loaded.
But, because React is universal-aware, React on the client will checkes if the rendering did happen already, and in case it happened then it will skip it.
So, the rendering process will happen only once.
Which lead us to some interesting fact: It is easy to switch off Server rendering.

Switch off Server Side Rendering?

So, by just having a small “if” statement before doing server rendering, we can switch off totally the server rendering and switch back to client rendering as a normal non-universal React application.

....
var htmlBody = isItUniversal ? renderToString(<RootApp/>) : '';
function serverRenderer() {
    return (req, res, next) => {
        res.status(200).send(`
            <!doctype html>
            <html> 
              <head> <title>App</title> </head>  
              <body>
                <div id="root_placeholder">
                    ${htmlBody}
                </div>
                <script src="/client.js"></script>
              </body>
            </html>
        `); 
    };
}


It is common practice in universal boilerplate/frameworks to see they provide a way for developers to switch that server-side rendering.

Rendering with Redux

As we know in a typical Redux-enabled React application, rendering is happening by populating the Redux store, and React components will subscribe to that redux store, and render as HTML.
Redux is like React a universal-friendly library.
The scenario is to populate the redux store on the server, and do the rendering on the server.
React on the client side will know that the component is already rendered, so it will skip rendering.

But, because redux and react-redux needs the redux store to sync with components states, so there is a trick that has to be done. We have to pass to the redux on the client side, the whole redux state after populating the store.
Applications will do that by passing the state into the client javascript code through the &ltscript> html tag, and they pass it as a global javascript variable like this:

function renderFullPage(html, preloadedState) {
  return `
  <!doctype html>
  <html>
    <head>  <title>Redux Universal Example</title>  </head>
    <body>
      <div id="root_placeholder">${htmlBody}</div>
      <script>
        // WARNING: See the following for security issues around embedding JSON in HTML:
        // http://redux.js.org/docs/recipes/ServerRendering.html#security-considerations
        window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState).replace(/</g, '\\u003c')}
      </script>
      <script src="/client.js"></script>
    </body>
  </html>
  `
}


Universal Routing

Ideally, we should define routes and routing in one place only, and both client and server read them from the same place.
Again, with the help of React-Router which is a universal-friendly library we can achieve a universal (client/server) routing.
On the server (Speaking of NodeJs application), We can achieve this, by delegating the routing to the routes defined on the client using react-router format.
Keep in mind that in a “sane” react application, the root component is the routing component, which define a wrapper around the routes that match React components.
So, it is usual in universal application, that the server routing code will have only one and only one general routing handler like this:

app.get('*', (req, res) => {
…..
….
renderToString(
  <Router location={req.url} context={context}>
    <Route>
    ....
  </Router>
);
…
}


Handling server side code with one routing handler

If in ideal universal application, we have only one general route handler, then how to run server side code that is depending on which current route?
react-router has a function called    match   which will help us detecting the current route on the server side, and then we can run the related server code. The code will be similar to this:

import React from 'react';
import { renderToString } from 'react-dom/server';
// and these to match the url to routes and then render
import { match, RouterContext } from 'react-router';

// we define our routes into the following module, and we import it on both server and client
import routes from './modules/routes';

// One and only one route handler on the sersverworks

app.get('*', (req, res) => {
  // match is a react-router function that will help us detect current route
  match({ routes: routes, location: req.url }, (err, redirect, props) => {
    // here where we detect the routes and location, to find out the current location
    // and run the server code accordingly:
  })
})


Development Challenges

Development environment and developer setup is effected by the universal application, and we are going to list here what the issues are:

Hot Module Replacement

In a typical client side application, React developers usually use Webpack-dev-server with HMR (Hot module replacement), to give a smooth development experience with on-the-fly build/refresh to test the application as they write code.

In a full-stack application, it is not usual to use webpack-dev-server, because webpack-dev-server is an express-based web server by itself, so developers switch to use a library called “webpack-dev-middleware”, with “webpack-hot-middleware”.
For universal application, there is another library: “webpack-hot-server-middleware” that is universal-friendly.

How to use it?

const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotServerMiddleware = require('webpack-hot-server-middleware');
const config = require('./webpack.config.js');
const app = express();
const compiler = webpack(config);

app.use(webpackDevMiddleware(compiler, {
  serverSideRender: true
}));
app.use(webpackHotServerMiddleware(compiler));

app.listen(6060);


Webpack Hot Server Middleware expects your Webpack config to export an array of configurations, one for your client bundle and one for your server bundle, e.g.

// webpack.config.js 
module.exports = [ 
  { name: 'client', target: 'web', entry: './client.js' ... }, 
  { name: 'server', target: 'node', entry: './server.js' ... } 
];


CSS & CSS Modules:

CSS Modules are commonly used css library in react applications, because it fits the react components architecture.
in css-module we import css file into the javascript file as follows:

// this code inside javascript file
import styles from ‘xxx.css’

There is no problem for production, because the build process (usually a webpack process) will recognize those css files, and build a final css file if using extract-text-webpack-plugin, or inject it into the final html if using style-loader.
But during active development (and with HMR), it is the server that is doing the css rendering stuff (remember it is the server that is rendering the page).
Node has to learn about css, and this where the library css-modules-require-hook plays a role.
it is a Node require hook that allows Node to require and deal with css file. And you can configure it to deal files with SASS and SCSS extensions as well.

Redux Dev Tools:

If you are using Redux Devtools, and redux devtools extensions, then you need to do extra work to make it works with redux server side rendering. You need to use remote-redux-dev-tools. It is more complex to describe it in this post, so just read it how to use it in the website, and I might write a future blog about it.

Build process, and code sharing between server and client

The build process for a universal application, will generate a final client side javascript code, that is compressed and maybe uglified, and generates css code, that either embedded into one HTML page, or in a separate files download, depending if we use webpack’s style-loader, or extract-text-webpack-plugin.
On the server side, the build process won’t usually compress the final code, but it might transpile it from babel (if we used babel on the code).

There is a big trick about universal applications that you should be aware of:

If , for any reason, a nodejs related code was called from the client, or if any of the client side code required some nodejs or node-based library, then the webpack will package that library , and many other node-related stuff into the final browser code, generating a huge compressed javascript file.

I am not going to describe how that happen, but I will point you to this interesting article that describes this problem in details. To avoid this problem, it is better have two separate build config, one for the server, and one for the client, as we saw an example before (refer to the code above).
Or you might use a better webpack, called universal webpack