Server Side React Rendering

In order to understand server-side rendering in React, I created the most basic example using Express, Babel, and Parcel.

Let’s get started.

Our app’s structure is going to be like this:

react-ssr-example/
├─ src/
│ ├─ app.jsx
├─ client.js
├─ server.js
├─ package.json

At first, we are going to download the react, react-dom, express, parcel, and nodemon packages.

We also need to install @babel/preset-react to support .jsx on Node and @babel/register to compile .jsx on runtime since .jsx files are not natively supported by Node.

client.js has react imported and we hydrate the #root element where we render our app. hydrateRoot lets you display React components inside a browser DOM node whose HTML content was previously generated by react-dom/server.

server.js now has a Babel config file at the top. The rest is a basic Express app. createElement creates a React element from a component. renderToString renders a React tree to an HTML string.

Finally, you can run npm run build and then npm start and have a React SSR App up and running! 🚀

Server Side React Example

// client.js

const React = require("react");
const { hydrateRoot } = require("react-dom/client");
const ReactApp = require("./src/app.jsx");

hydrateRoot(document.getElementById("root"), <ReactApp />);

// server.js

// compile on runtime
require("@babel/register")({
  presets: ["@babel/preset-react"],
});

const { createElement } = require("react");
const { renderToString } = require("react-dom/server");
const express = require("express");

const ReactApp = require("./src/app.jsx");

const app = express();
// parcel builds files to the 'dist' folder by default
app.use(express.static("dist"));

app.get("/", (request, response) => {
  const html = renderToString(createElement(ReactApp));

  response.end(
    `<!DOCTYPE html>
      <html>
      <head></head>
      <body>
        <div id="root">${html}</div>
        <script src="client.js"></script>
      </body>
      </html>`,
  );
});

const PORT = 3000;
app.listen(PORT, () => console.log(`App listening at port:${PORT}`));

// src/app.jsx

const React = require("react");

const App = () => {
  const [count, setCount] = React.useState(0);

  return (
    <>
      <h1>Hello from the simplest React SSR Demo!</h1>
      <button onClick={() => setCount(count + 1)}>{count}</button>
    </>
  );
};

module.exports = App;

// package.json

{
  "name": "server-side-react-rendering",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "nodemon --ext js,jsx server.js",
    "build": "parcel build client.js"
  },
  "dependencies": {
    "express": "^4.17.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@babel/preset-react": "^7.18.6",
    "@babel/register": "^7.18.9",
    "nodemon": "^2.0.20",
    "parcel": "^2.8.3"
  }
}
Back to Home