How to Implement StaticRouter in React

This article provides a straightforward guide on how to implement StaticRouter in React for server-side rendering (SSR). You will learn what StaticRouter is, why it is used to render React applications on a server, and how to configure it alongside your client-side routing to achieve seamless page hydration.

What is StaticRouter?

StaticRouter is a router provided by React Router specifically for server-side rendering. Unlike BrowserRouter, which reads the current location directly from the browser’s address bar, StaticRouter is completely stateless. It takes a specific URL provided by your server and uses it to render the correct component tree matching that route.

In React Router v6, StaticRouter is imported from the react-router-dom/server package.

Step 1: Install the Required Dependencies

To use StaticRouter, you need both react-router-dom and the server-side utilities. Install them in your project using npm or yarn:

npm install react-router-dom

Step 2: Create Your App Routes

Define your application components and routing structure. This file will be shared by both the server-side renderer and the client-side bundle.

// App.jsx
import React from 'react';
import { Routes, Route, Link } from 'react-router-dom';

const Home = () => <h2>Home Page</h2>;
const About = () => <h2>About Page</h2>;

export default function App() {
  return (
    <div>
      <nav>
        <Link to="/">Home</Link> | <Link to="/about">About</Link>
      </nav>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </div>
  );
}

Step 3: Implement StaticRouter on the Server

On the Node.js server (such as an Express server), you will intercept incoming requests, pass the requested URL to StaticRouter via the location prop, and render the application to an HTML string using ReactDOMServer.renderToString.

// server.js
import express from 'express';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import { StaticRouter } from 'react-router-dom/server';
import App from './App.jsx';

const app = express();

app.get('*', (req, res) => {
  // Capture the requested URL from the express request object
  const location = req.url;

  // Render the component tree using StaticRouter
  const html = ReactDOMServer.renderToString(
    <StaticRouter location={location}>
      <App />
    </StaticRouter>
  );

  // Send the rendered HTML template back to the client
  res.send(`
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>React SSR</title>
    </head>
    <body>
      <div id="root">${html}</div>
      <script src="/bundle.js"></script>
    </body>
    </html>
  `);
});

app.listen(3000, () => {
  console.log('Server is running on http://localhost:3000');
});

Step 4: Hydrate the Application on the Client

Once the browser receives the static HTML rendered by the server, React needs to attach event listeners to make the page interactive. On the client side, you must wrap the shared <App /> component in a <BrowserRouter> and use React’s hydrateRoot method.

// client.js
import React from 'react';
import { hydrateRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App.jsx';

hydrateRoot(
  document.getElementById('root'),
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

By pairing StaticRouter on the server with BrowserRouter on the client, you successfully implement server-side rendering for a routing-heavy React application.