How to Update StaticRouter in React

This article explains how to update and implement StaticRouter in React applications, specifically focusing on the transition from React Router v5 to v6 and the modern React Router v6.4+ data APIs. You will learn how to update your import paths, adapt to component architecture changes, and transition to the modern StaticRouterProvider for server-side rendering (SSR).

1. Update the Import Path (React Router v6)

In React Router v5, StaticRouter was imported directly from the main react-router-dom package. In React Router v6, this component was moved to a dedicated server entry point to keep client-side bundles smaller.

To update your import, change it from:

// React Router v5 (Deprecated)
import { StaticRouter } from 'react-router-dom';

To the new subpath:

// React Router v6
import { StaticRouter } from 'react-router-dom/server';

2. Update Component Structure and Props

React Router v6 replaced the <Switch> component with <Routes> and changed how children are defined inside routes. Additionally, the context prop behavior has changed.

Here is how to update your server-side rendering setup.

Old React Router v5 Implementation:

import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import App from './App';

const context = {};
const html = renderToString(
  <StaticRouter location={req.url} context={context}>
    <App />
  </StaticRouter>
);

Updated React Router v6 Implementation:

In v6, you still pass the location prop, but ensure your <App /> component uses the new <Routes> and <Route element={<Component />} /> syntax instead of <Switch>.

import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom/server';
import App from './App'; // Ensure App uses v6 <Routes>

const html = renderToString(
  <StaticRouter location={req.url}>
    <App />
  </StaticRouter>
);

3. Upgrading to StaticRouterProvider (React Router v6.4+)

If you are updating your application to use the modern data APIs introduced in React Router v6.4 (such as loaders, actions, and createBrowserRouter), you should migrate from <StaticRouter> to <StaticRouterProvider>.

This update requires using @remix-run/router to handle the server-side request data before rendering.

Step-by-step update:

  1. Create a server-side handler using createStaticHandler and createStaticRouter.
  2. Query the active route data based on the incoming request.
  3. Render the application using StaticRouterProvider.

Here is the implementation:

import { renderToString } from 'react-dom/server';
import {
  createStaticHandler,
  createStaticRouter,
  StaticRouterProvider,
} from 'react-router-dom/server';
import { routes } from './routes'; // Shared routes array

async function handleServerRequest(req, res) {
  const handler = createStaticHandler(routes);
  const context = await handler.query(req);

  // If the query returns a Response (like a redirect), send it immediately
  if (context instanceof Response) {
    return res.redirect(context.status, context.headers.get('Location'));
  }

  const router = createStaticRouter(handler.dataRoutes, context);

  const html = renderToString(
    <StaticRouterProvider router={router} context={context} />
  );

  res.send(`<!DOCTYPE html>${html}`);
}