React Server Side Rendering with Express

Photo by Dlanor S on Unsplash

React Server Side Rendering provides the ability to render your React pages from server such as ExpressJs. React SSR provide various benefits which includes SEO Friendly and faster initial page load. React has an amazing documentation on ReactDOMServer.


Some of the commands you see here are based on Terminal and I will try my best to include for Command Prompt (Windows)

Let’s begin from scratch

We are going to build this React App from scratch as this will allow us to have more customization on our React App which you will understand later. Don’t get me wrong Facebook make an great tool to provide a head start to React App creation and you don’t have to worry about the rest. Read more here.

I am using NPM here, however if you prefer you can use Yarn. Gant Laborde provided a NPM vs Yarn Cheat Sheet

Create a directory for your code and from the directory execute

npm init

To create our server, we are going to use ExpressJs. Therefore we are going to include express into our modules

npm install express

We will be using ES6, therefore Babel is needed to transpile our code. We need these dependencies for development only and not for production release

npm install --save-dev @babel/cli @babel/core @babel/node @babel/plugin-proposal-class-properties @babel/plugin-transform-runtime @babel/polyfill @babel/preset-env

There are a bunch of dependencies we have here. And they are


Create a directory under the root directory and name it “src” and create a new file and name it server.js

mkdir src
touch ./src/server.js #for terminal
echo $null >> /src/server.js #for windows

This is a standard ExpressJs code nothing special here. Compression middleware by ExpressJs is used to allow static content to be served with compression. Make sure compression is part of you package by running npm install compression

app.use(express.static(‘public’)); tell ExpressJs we are serving all static content via the public folder

As your React SSR app grows you will need to organize your route for easy maintainability. With that, we create our route “/firstssr” which will reference to the route that we are going to create “ssr”


We will create a directory under “src” called routes

mkdir ./src/routes #for terminal
mkdir /src/routes #for windows

Next create a new file and name it ssr.js

touch ./src/routes/ssr.js

Throughout this tutorial we will add the SSR logic here. For now we will keep it simple.

import express from "express";const router = express.Router();router.get("/", async (req, res) => {res.status(201).send("Hello World");});export default router;

The above code simply allow us to browse to http://localhost:3030/firstssr and return a text “Hello World”

Running the App

We are going to run the app for the first time. Before that we need to include some script into our package.json. Include the below under “scripts”

"dev": "nodemon --exec babel-node src/server.js"

I used nodemon, if you do not have nodemon, feel free to install this package globally using npm install -g nodemon

We can now start our application using npm run dev .

Wait a minute, there is an error.

The reason for this error is due to a missing babel configuration as babel does not recognize import. We need to add a babel configuration file. To do so from the root directory create a file, name it .babelrc and add the below

{"presets": [["@babel/preset-env", { "targets": { "node": "current" } }]],"plugins": ["@babel/plugin-proposal-class-properties"]}

Remember we added a couple for bebel dependencies earlier. And one of them was @babel/preset-env and we are going to target it based on the current node version. We also added a plugin to allow babel to transpile classes which will be needed later when we start to include our React component.

Let’s run npm run dev . This time it executed without an issue.

Open your browser and browse to http://localhost:3030/firstssr and you should get the same result as the below

Building the React Page

We are going to build a very basic React page with a button that will calculate the length of the name. Create a folder and name it “component” under the root folder or “src” and create a jsx file

mkdir ./src/components
touch ./src/components/app.jsx

Getting the React Component Rendered Server Side

The fun starts here. We will now go back to our SSR route that we created (src/routes/ssr.js).

React creates DOM and will append to the HTML. This is exactly what we are going to do now. We first need a HTML that will allow React’s DOM to be rendered onto.

The HTML can be from a remote server or a physical file.

Replace the existing router.get

router.get("/", async (req, res) => {const theHtml = `<html><head><title>My First SSR</title></head><body><h1>My First Server Side Render</h1><div id="reactele">{{{reactele}}}</div><script src="/app.js" charset="utf-8"></script><script src="/vendor.js" charset="utf-8"></script></body></html>`;res.send(theHtml);});

Let me explain what is happening here

  • We are using Handlebar as the template engine, alternatively you can you other view engine such as EJS or nunjucks
  • {{{reactele}}} — This is a handlebar syntax of the variable “reactele”. The content DOM rendered by React will be replaced here. The 3 braces is to escape the HTML values
  • The 2 additional script — app.js is the main React javascript that will be rendered and vendor.js is simply any vendor script that we are going to use such as react. I will cover this later under the Webpack configuration

When we run our application with npm run dev we will get our HTML.

Next we will render our React DOM. We need 3 dependency to make this happen.

npm install react react-dom handlebars

const hbsTemplate = hbs.compile(theHTML); is trying to compile the HTML we create into a Handlebar Template.

const reactComp = renderToString(<App />); is where React Server Side rendering works. If you console log the “reactComp” you will see a bunch of HTML generated.

const htmlToSend = hbsTemplate({reactele: reactComp }); will be replacing the rendered React DOM into the handlebar variable {{{reactele}}}

We got an error “Unexpected token at (23:35)” and we need to include @babel/preset-react the into “.babelrc” under the presets setting. Read more here. Make sure this module is added as our dev dependency.

npm install --save-dev @babel/preset-react

After doing this, we can now run our application and the result is as below

You will notice, nothing happen when you click the button. If you have React Dev Tool installed, when you click the button it says “This page doesn’t appear to be using React”. Why is this so? We are rendering our React Component right? I will explain in the next section.

Hydrating our React Component

React is a client side javascript. It renders DOM and hydrate the component to get it working. Because we only render our React component as HTML string and therefore, the current state of our application is just plain HTML.

A standard client side React application, we typically would have an entry point script and the code would be something similar to the below

ReactDOM.render(<yourreactcompoent />, document.getElementByID('root');

To hydrate the component, we will create a new file under components and name it index.js.

import React from "react";import { hydrate } from "react-dom";import App from "./app";hydrate(<App />, document.getElementById("reactele"));

Remember when we generate our HTML we have a div called as “reactele”. This is where we will hydrate our React component.

We are not done yet. The last step is to configure the webpack. Webpack will help us to build the entry point javascript and deployed it into public folder.

Webpack Configuration

Create a new file in the root folder of our app and named it “webpack.config.js”.

Here we have 2 entry point.

  1. vendor — These will be the script that are from 3rd party such as React and they rarely change
  2. app — This is your React entry point that we just created on the previous section

We included babel-loader which will allow us to transpile our javascript with webpack

As build more complex app, you will be adding more routes with multiple entry point. Why is this so? By doing this, you can reduce the size of your app.js and only load the needed React Component and not those component that is not needed. You could also version and chunk your javascript with the webpack.

With the configuration done, we need Webpack to bundle our javascripts. Add a new script (webpack) in package.json

"scripts": {"dev": "nodemon --exec babel-node src/server.js","webpack": "webpack -wd"},

Before running our application, make sure you have included the webpack and babel-loader dependency by executing npm install --save-dev webpack webpack-cli babel-loader . Webpack-cli is used to pack the scripts from terminal/command.

Running the App

Finally, we are going run our application. We need to run 2 command and they are npm run webpack and npm run dev . You should run these command in different terminal or command prompt window. Once done browse to http://localhost:3030/firstssr and the result

Congratulation! This is your first React Server Side Rendering!

For the full source code, please go to my github React-SSR. If you have any question, feedback or need help, feel free to contact me via linkedIn.

Full Stack Developer, Javascript, .Net.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store