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! 🚀
// 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"
}
}