How to Implement Server-Side Rendering in React
Server-Side Rendering (SSR) in React improves application load times, enhances user experience, and boosts search engine optimization (SEO) by rendering components into HTML on the server before sending them to the browser. This article provides a direct, step-by-step guide on how to manually implement SSR in a React application using Node.js, Express, and React’s built-in server APIs. You will learn the core architecture of SSR, how to render components on the server, and how to hydrate them on the client.
The Core Concept of React SSR
In a standard Client-Side Rendered (CSR) React app, the server sends a blank HTML file and a JavaScript bundle. The browser downloads the JS and renders the UI.
With SSR, the process shifts: 1. Server Rendering: The server executes the React code, renders the component tree into static HTML, and sends this pre-rendered HTML to the browser. 2. Immediate Paint: The user immediately sees the visual content of the page. 3. Hydration: The browser downloads the JavaScript bundle and attaches event listeners to the existing HTML, making the page fully interactive.
Step 1: Set Up the Client-Side Hydration
To transition from static HTML to an interactive React application,
you must “hydrate” the HTML on the client. Instead of using
createRoot, use hydrateRoot from
react-dom/client.
Create your client entry point (client.js):
import React from 'react';
import { hydrateRoot } from 'react-dom/client';
import App from './App';
hydrateRoot(document.getElementById('root'), <App />);Step 2: Create the Express Server
To render React on the server, you need a Node.js server environment. Express is commonly used for this purpose.
Using the renderToString function from
react-dom/server, you can compile your main React component
into a static HTML string.
Create your server file (server.js):
import express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';
import App from './App';
const app = express();
// Serve static assets (like your compiled client-side JavaScript bundle)
app.use(express.static('public'));
app.get('*', (req, res) => {
// Render the React component to an HTML string
const appMarkup = renderToString(<App />);
// Inject the rendered markup into a full HTML template
const html = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>React SSR App</title>
</head>
<body>
<div id="root">${appMarkup}</div>
<script src="/bundle.js"></script>
</body>
</html>
`;
// Send the complete HTML page to the client
res.send(html);
});
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});Step 3: Configure Build Tools (Babel and Webpack)
Node.js does not natively understand JSX or ES modules out of the box. You must configure Webpack and Babel to compile both the server-side and client-side code.
- Babel Setup: Configure
@babel/preset-envand@babel/preset-reactto transpile JSX and modern JavaScript. - Webpack Client Config: Bundles
client.jsand outputs it to thepublic/bundle.jsfile so the browser can download it. - Webpack Server Config: Bundles
server.jsusingtarget: 'node'andwebpack-node-externalsto ensure Node-specific modules are not bundled.
Step 4: Run the Application
Once your build tools are configured, compile your assets and start the Node server:
- Run Webpack to build the client and server bundles.
- Start the Express server by running
node dist/server.js.
When a user visits your site, the server will instantly return the
pre-rendered HTML containing your React components, followed by the
activation of event listeners once bundle.js loads.
Alternative: Production-Ready Frameworks
While building a custom SSR setup is highly educational, maintaining Webpack configurations, code-splitting, and data-fetching synchronization between the server and client can become complex. For production environments, industry-standard frameworks automate this entire process:
- Next.js: The most popular React framework, offering built-in SSR, Static Site Generation (SSG), and automatic code splitting.
- Remix: A modern React framework focused on web standards, robust data loading, and seamless SSR capabilities.