How to Mock SSR in React
Testing React applications that support Server-Side Rendering (SSR) often requires simulating the server environment within your client-side testing suite. This article explains how to mock SSR in React using Jest and React Testing Library, detailing how to simulate the absence of browser globals, test the hydration process, and mock environment-specific flags to ensure your components function seamlessly on both the server and the client.
Why Mock SSR in React?
During SSR, React components are rendered to HTML on a Node.js server
where browser APIs like window, document, and
localStorage do not exist. If your components access these
APIs directly during the initial render, they will throw errors on the
server. Mocking SSR allows you to: * Catch “window is not defined”
errors before deployment. * Test conditional rendering logic that
depends on the execution environment. * Ensure hydration matches the
server-generated HTML to avoid layout shifts.
Method 1: Mocking the Window Object in Jest
The most direct way to simulate an SSR environment in a Jest (JSDOM)
environment is to temporarily delete or modify the global
window object before executing your tests.
How to delete
window for specific tests:
describe('SSR Component Behavior', () => {
const originalWindow = global.window;
beforeEach(() => {
// Delete the window object to simulate Node.js server environment
// @ts-ignore
delete global.window;
});
afterEach(() => {
// Restore the window object after the test
global.window = originalWindow;
});
it('should render safely without throwing window errors', () => {
// Render your component here
// Verify it handles the undefined window gracefully
});
});Method 2: Simulating Hydration in React Testing Library
Hydration is the process where React attaches event listeners to the static HTML sent by the server. You can mock this process in your tests to ensure your client-side code correctly “picks up” the server-rendered markup.
You can use react-dom/server to generate the initial
HTML, inject it into the JSDOM container, and then hydrate it.
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import { hydrateRoot } from 'react-dom/client';
import { screen } from '@testing-library/react';
function MySSRComponent() {
const [isClient, setIsClient] = React.useState(false);
React.useEffect(() => {
setIsClient(true);
}, []);
return (
<div>
{isClient ? 'Client Rendered' : 'Server Rendered'}
</div>
);
}
test('should hydrate server-rendered HTML correctly', () => {
const container = document.createElement('div');
document.body.appendChild(container);
// 1. Generate server-side HTML
const serverHtml = ReactDOMServer.renderToString(<MySSRComponent />);
container.innerHTML = serverHtml;
// Assert it shows server content initially
expect(container.textContent).toBe('Server Rendered');
// 2. Hydrate on the "client"
hydrateRoot(container, <MySSRComponent />);
// Assert it updates to client content after hydration (useEffect runs)
expect(screen.getByText('Client Rendered')).toBeInTheDocument();
// Cleanup
document.body.removeChild(container);
});Method 3: Mocking Environment Flags
Many React frameworks (like Next.js or Remix) or custom setups use
utility functions or environment variables to determine if the code is
running on the server or client (e.g.,
const isServer = typeof window === 'undefined').
You can mock these helper modules in Jest to force your components to run their “server” code path.
Example utility
(env.js):
export const isServer = () => typeof window === 'undefined';Mocking the utility in Jest:
import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';
import * as env from './env';
jest.mock('./env', () => ({
isServer: jest.fn(),
}));
test('renders server-specific UI', () => {
// Mock isServer to return true
env.isServer.mockReturnValue(true);
render(<MyComponent />);
expect(screen.getByText('Loading on server...')).toBeInTheDocument();
});