A recurring problem when doing SSR are so-called hydration mismatches.

Hydration mismatch

What hydration means is explained at What is Hydration?

A hydration mismatch is when the content rendered to HTML on the server isn't the same as the content rendered in the browser.

Hydration mismatches can induce performance degradations and bugs and should therefore be avoided.

Vue

Upon a hydration mismatch, Vue throws following errors in the browser:

[Vue warn]: Hydration text mismatch:
 - Client: "some content"
 - Server: "some other content"
   at <SomeComponent>
   at <App>
Hydration completed but contains mismatches.

React

Upon a hydration mismatch, React throws following errors in the browser:

Warning: Text content did not match. Server: "some content" Client: "some other content"
An error occurred during hydration. The server HTML was replaced with client content in <SomeComponent>.
Text content does not match server-rendered HTML.
Hydration failed because the initial UI does not match what was rendered on the server.
There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.

Example

This component causes a hydration mismatch because the milliseconds rendered to HTML (e.g. <span>123</span>) won't match the milliseconds rendered in the browser (e.g. <span>167</span>).

<span>{ new Date().getMilliseconds() }</span>

Instead, to prevent the hydration mismatch:

export { onBeforeRender }
 
async function onBeforeRender() {
  const milliseconds = new Date().getMilliseconds()
  return {
    pageContext: {
      milliseconds
    }
  }
}
<span>{ pageContext.milliseconds }</span>

The pageContext.milliseconds value is set exactly once, which means that the milliseconds value is the same when rendered to HTML on the server-side and when hydrated in the browser.

Common causes & solutions

Common causes:

  • Rendered content is actually different. Make sure your components render the same content when rendered to HTML on the server-side and when rendered/hydrated on the client-side.

    See example above.

  • Proxies: Make sure your proxies don't apply problematic HTML transformations. For example, most HTML minifiers cause hydration mismatches and have to be disabled.

    If you use Cloudflare, you have to disable Cloudflare's automatic HTML minifier.

  • Browser cache: Your browser's cache may load cached & outdated JavaScript, which may cause hydration mismatches if a component is outdated on the client-side and renders something else than the up-to-date component on the server-side. Clear your browser cache and try again.

    In principle, this shouldn't happen since Vite always busts the browser cache whenever a file is changed. If you get issues with hydration mismatches related to browser caching, then let us know and we'll look into it.

With React:

  • Old React versions are buggy around stitching text segments leading to Warning: Text content did not match. Update to react@18.2.0 or above.

Suppress Hydration Mismatch

For situations where mismatches are inevitable or difficult to avoid, you can silence the hydration mismatch warning.

⚠️
Don’t overuse it: for less potential bugs and for (slightly or significant) better performance, it's best to avoid hydration mismatches if you can.

React

Use the suppressHydrationWarning attribute:

function SomeComponent() {
  return (
    <span suppressHydrationWarning={true}>
      Current Date: {new Date().toLocaleDateString()}
    </span>
  )
}

See:

Vue

Use the data-allow-mismatch attribute:

<div data-allow-mismatch="text">{{ data.toLocaleString() }}</div>

See: