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:

  1. Render the component to a string (simulating the server).
  2. Set the document.body.innerHTML to that string.
  3. Call hydrateRoot to simulate the client taking over.
  4. Spy on console.error to 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.