How to Test Server Side Rendering in React
Testing Server-Side Rendering (SSR) in React is crucial for ensuring that your application renders correctly on the server before hydration occurs on the client. This article provides a straightforward guide on how to test React SSR, covering the essential tools, strategies for verifying server-generated HTML, and techniques for testing hydration consistency to prevent common UI mismatches.
Unit Testing Server-Rendered HTML
The first step in testing SSR is verifying that your React components
produce the correct HTML on the server. You can achieve this by using
renderToString or renderToStaticMarkup from
the react-dom/server package within your standard testing
framework (such as Jest or Vitest).
import React from 'react';
import { renderToString } from 'react-dom/server';
import MyComponent from './MyComponent';
describe('MyComponent SSR', () => {
it('renders correct HTML on the server', () => {
const html = renderToString(<MyComponent title="Hello World" />);
expect(html).toContain('<h1>Hello World</h1>');
expect(html).toContain('<p>Welcome to our server-rendered app.</p>');
});
});This approach allows you to assert that critical SEO content, meta tags, and initial states are present in the raw markup sent to the browser before any JavaScript runs.
Testing Hydration and Client-Side Takeover
Hydration is the process where React attaches event listeners to the server-rendered HTML. If the server-rendered HTML and the client-rendered HTML do not match, React will throw a hydration mismatch warning. Testing hydration helps prevent these visual bugs and performance penalties.
To test hydration using React Testing Library and Jest/Vitest:
- Render the component to a string (simulating the server).
- Set the
document.body.innerHTMLto that string. - Call
hydrateRootto simulate the client taking over. - Spy on
console.errorto catch hydration warnings.
import React from 'react';
import { hydrateRoot } from 'react-dom/client';
import { renderToString } from 'react-dom/server';
import MyComponent from './MyComponent';
describe('MyComponent Hydration', () => {
it('hydrates without errors or mismatches', () => {
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
// 1. Simulate Server Render
const serverHtml = renderToString(<MyComponent />);
// 2. Set up DOM environment
const container = document.createElement('div');
container.innerHTML = serverHtml;
document.body.appendChild(container);
// 3. Simulate Client Hydration
hydrateRoot(container, <MyComponent />);
// 4. Assert no hydration warnings were thrown
expect(consoleErrorSpy).not.toHaveBeenCalledWith(
expect.stringContaining('Warning: Did not expect server HTML to contain')
);
consoleErrorSpy.mockRestore();
document.body.removeChild(container);
});
});End-to-End Testing for SSR
End-to-End (E2E) testing tools like Playwright or Puppeteer offer the most reliable way to test SSR in a real browser environment. The best strategy is to disable JavaScript in the browser to verify that the application remains functional and visually correct using only the server-sent HTML.
Using Playwright, you can write a test that disables JavaScript and inspects the page:
import { test, expect } from '@playwright/test';
test.use({ javaScriptEnabled: false });
test('should render content with JavaScript disabled', async ({ page }) => {
// Navigate to the SSR page
await page.goto('http://localhost:3000');
// Verify that the server-rendered elements are visible
const header = page.locator('h1');
await expect(header).toHaveText('Welcome to our server-rendered app.');
// Verify SEO metadata is present in the document head
const description = await page.locator('meta[name="description"]').getAttribute('content');
expect(description).toBe('This is an SSR React application.');
});By combining unit tests for raw HTML output, integration tests for hydration warnings, and E2E tests with disabled JavaScript, you can guarantee a robust and SEO-friendly React SSR implementation.