The SDET Playbook

← All questions

How do you mitigate test flakiness caused by asynchronous client-side state hydration in React/Next.js?

Asked Sep 28, 2026Viewed 1 time

1 Answer

Sign in to answer and to vote.

  • 0
    The SDET PlaybookSep 28, 2026

    With server rendering, the HTML arrives before React has attached its event handlers. A test can see the button and click it before hydration finishes, and the click does nothing. Playwright's actionability checks can't catch this, because the button is already visible, stable and enabled.

    Fixes, from most to least preferable:

    1. Assert on the result, not just the click. After click(), wait for what the click should do: await expect(page.getByRole('dialog')).toBeVisible(). If hydration swallowed the click, the assertion fails clearly instead of a later step failing mysteriously.
    2. Render interactive controls disabled until they work. If the app renders a form's submit button disabled on the server and enables it after hydration, Playwright's "enabled" check does the waiting for you, and real users on slow devices get the same protection.
    3. Expose an explicit ready signal. The app can set an attribute once it has hydrated (for example data-hydrated="true" on <body>, set in an effect), and the test waits for it with await expect(page.locator('body')).toHaveAttribute('data-hydrated', 'true'). This is a hook your team adds; neither React nor Next.js provides it.
    4. Wait for the data the screen needs. When a control depends on a client-side fetch, wait for that response (page.waitForResponse, set up before the action that triggers it) or, better, for the element that shows the loaded data.

    Avoid waitForTimeout and networkidle as fixes: both hide the race without removing it.

    Sources: React: hydrateRoot, Playwright auto-waiting, Playwright best practices