The React SSR Rehydrate Markup Matching Issue

The problem is described at length here.

The short version is this: When rendering on the server and calling rehydrate on the client, react expects the markup generated on the server and the render-pass on the client to produce the same results.

This can be problematic if the server lacks certain information that you have available on the client, and needs to use a fallback value, while on the client-side you use the correct value. Common examples:

  • The server does not know if the user is logged in, but the client does.
  • The server does not know the device size, but on the client you render different markup for different devices.

What happens if the markup does not match? There are two options:

  1. React will show a warning in the console, but will otherwise “fix the differences”.
  2. React will not fix the differences, and your markup will be a mix of the one rendered on the server and the one rendered on the client. This is bad. It seems to happen a lot due to className not updating.

What are our options?

  1. Render both versions on the server. This is what fresnel does.
  2. Render a second time on the client after rehydrate. This is described by the React rehydrate docs. This involves making sure, one way or another, that the first time a component is rendered (during rehydrate) it will use the same values as on the server, and will then trigger a useEffect or componentDidUpdateto re-render with the client-side value. It may be desirable to use useLayoutEffect.
  3. Wait for Client Hints to be supported by browsers.

Leave a comment