Server Side Rendering - React

By Ali Farooq on 2nd February, 2021
If you have been working with React, you are probably familiar with client side rendering (CSR) where the browser downloads a minified HTML page which contains some javascript. The javascript then populates then inflates the HTML page with content. While this is very beneficial, this is not great for web crawlers (which means it is not great for SEO!). Search engine crawlers are getting smarter everyday however they will always have an easier time crawling pre-rendered content and if you are working with React. If your content is static, then you are going to have a very easy time doing that and you can set up static site generation. However, like most sites, if you have dynamic content then you probably need server side rendering (SSR). There are alternates such as incremental static regeneration available however this blog post will focus primarily on server side rendering.
In this blog post, we will discuss:
  • What is server side rendering (SSR)?
  • Why SSR?
  • How to implement SSR with React?
What is server side rendering (SSR)?
Server side rendering refers to the HTML of your web page being generated on the server side. The diagram below help you understand the difference between CSR and SSR.
Why SSR?
Performance
Server side rendering generally outperforms client side rendering in initial load times. There are still countless cases where CSR is necessary e.g. facebook wouldn’t want to server their entire feed with SSR. The initial load would probably be with server side rendering and additional content can be populate with CSR. So in most real world scenarios, you would be using a combination of SSR and CSR.
SEO
While Google, Bing and most other search engines have started crawling async javascript applications, your content is still not as likely to be crawled when compared to a page that has its HTML already generated. Server side rendering will always be more reliable and since search engines only allocate a limited time slice to each site to crawl content, your content is more likely to be crawled if search engine does not have to run additional javascript to get to your content.
Social sharing
Facebook, twitter etc. allow web pages to set snippets of what your site would look like when shared on these platforms. Other applications such as Slack, Linkedin etc. also make use of these tags to show snippets. Social platforms will not run your javascript to generate these snippers so SSR plays an important role here.
How to implement SSR with React?
A React application, without SSR, can be deployed on any static host such as AWS S3. However, with SSR enabled, static storages are no longer enough. As the name suggest, you now need a server. There are several options to implement SSR however we are going to divide them into two broad categories: Set up SSR manually using something like ExpressJS OR use a framework such as NextJS, GatsbyJS etc.
Setting up SSR using ExpressJS
‣ Start by installing "express" in your application.
npm install express
OR
yarn install express
‣ Create server/index.js
‣ Your server/index.js file should have the following code:
import path from 'path';
import fs from 'fs';

import React from 'react';
import express from 'express';
import ReactDOMServer from 'react-dom/server';

import App from '../src/App';

const PORT = process.env.PORT || 3006;
const app = express();


app.get('/', (req, res) => {
  const app = ReactDOMServer.renderToString(<App />);

  const indexFile = path.resolve('./build/index.html');
  fs.readFile(indexFile, 'utf8', (err, data) => {
    if (err) {
      console.error('Something went wrong:', err);
      return res.status(500).send('Oops, better luck next time!');
    }

    return res.send(
      data.replace('<div id="root"></div>', `<div id="root">${app}</div>`)
    );
  });
});

app.use(express.static('./build'));

app.listen(PORT, () => {
  console.log(`Server is listening on port ${PORT}`);
});
This file is doing two important things:
1. It uses ReactDOMServer and renderToString to render the app to a static HTML string.
2. It serves the content in build folder as static content.
‣ Next, install webpack and babel
npm install webpack@4.42.0 webpack-cli@3.3.12 webpack-node-externals@1.7.2 @babel/core@7.10.4 babel-loader@8.1.0 @babel/preset-env@7.10.4 @babel/preset-react@7.10.4 --save-dev
OR
yarn add webpack@4.42.0 webpack-cli@3.3.12 webpack-node-externals@1.7.2 @babel/core@7.10.4 babel-loader@8.1.0 @babel/preset-env@7.10.4 @babel/preset-react@7.10.4 --dev
‣ Then, we will setup our babel configuration by setting up .babelrc.json in our root.
{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react"
  ]
}
‣ Next, we will add our webpack server configuration by setting up webpack.server.js:
const path = require('path');
const nodeExternals = require('webpack-node-externals');

module.exports = {
  entry: './server/index.js',

  target: 'node',

  externals: [nodeExternals()],

  output: {
    path: path.resolve('server-build'),
    filename: 'index.js'
  },

  module: {
    rules: [
      {
        test: /\.js$/,
        use: 'babel-loader'
      }
    ]
  }
};
‣ Now, let's add some scripts to your package.json to simplify commands that we need to run frequently:
"scripts": {
  "dev:build-server": "NODE_ENV=development webpack --config webpack.server.js --mode=development -w",
  "dev:start": "nodemon ./server-build/index.js",
  "dev": "npm-run-all --parallel build dev:*",
  // ...
},
‣ The last thing that we need to do is install some packages to watch for changes and run parallel builds:
npm install nodemon@2.0.4 npm-run-all@4.1.5 --save-dev
OR
yarn add nodemon@2.0.4 npm-run-all@4.1.5 --dev
And that's it!
If you are looking to set up server side rendering on your application for SEO or for any of the other reasons mentioned above, feel free to reach out to us and book a consultation session with Alfabolt.