Isomorphic Server Variables

Isomorphic Single Page Applications (Isomorphic SPA) are often used since they combine the advantages of the single page application and the server-rendered pages:

Data in isomorphic apps can be retrieved like in any other SPA, using REST calls, FLUX, Relay, etc. However, in some cases it can be nice to have certain short-hand variables available to the scripts, for instance for settings, debugging, etc. They need to be provided in a different fashion when rendering on the server, and on the client. In the browser it would be natural to use the global object, while this is not an option on the server.

Here we describe an approach to using such variables.

Starter Kit Implementation

In the Universal Relay Boilerplate adding isomorphic variables is easy. Edit the file webapp\scripts\isomorphicVars.js. In the return section add your variable(s). In the example below the only variable set is version which equals the value of process.env.npm_package_version on the server:

export function isomorphicVars( )
{
  if( typeof( window ) == 'undefined' )
  {
    // Running on server.
    return {
      // Modify the code below to set the variables >>>
      version: process.env.npm_package_version,
      // <<< Modify the code above to set the variables
    }
  }
  else
    return window.isomorphicVars;
}

After the variables have been added they can be used in components after including the isomorphicVars.js file. For instance on the Home Screen we do

....
import {isomorphicVars} from '../scripts/isomorphicVars';

....

class Home_Screen extends React.Component
{
  render( )
  {
    var isoVars = isomorphicVars( );
    ....
    <CardHeader
      title="Isomorphic Material-UI Relay starter kit"
      subtitle={ "Version " + isoVars.version }
    />
    ....
  }
  ....
}

How it works

On the server

Every time isomorphicVars( ) is executed on the server, typeof( window ) will be 'undefined', so the dictionary will be created and returned. Notice that if functions are used to retrieve the values, they should be reasonably light weight, especially if the variables are used in multiple components.

On the client

When the initial load of the page triggers server rendering in renderOnServer.js, the isomorphic variables are being retrieved, and serialized to JSON. Then they are passed to the renderOnServer.ejs template:

....
import {isomorphicVars} from './scripts/isomorphicVars';

....

function render( data )
{
  ....
  // Load up isomorphic vars here, for server rendering
  let isoVars = JSON.stringify( isomorphicVars( ) );

  const reactOutput = ReactDOMServer.renderToString(
      <IsomorphicRouter.RouterContext {...renderProps} />
  );

  res.render( path.resolve( __dirname, '..', 'webapp', 'renderOnServer.ejs' ), {
      preloadedData: JSON.stringify(data),
      assetsPath: assetsPath,
      reactOutput,
      isomorphicVars: isoVars
  } );
}
....

In the template they are provided as a global object:

<script>window.isomorphicVars = <%- isomorphicVars %></script>

Later, when isomorphicVars( ) is executed on the client, it simply returns the value of the serialized JSON object, now part of the page script, with the values.