NextJS and React hydration problem on production
TL;DR: The problem was Cloudflare’s minification service.
In one of the projects I was working on lately I was using the static HTML export in Next.js. The way of building web app this way has it pros and cons but here I wanted to write about the problem that didn’t make any sense.
The problem
After deploying the build to production server and accessing some of the pages through the domain, the hydration errors like these appeared:
When following the links in from the console we get:
Text content does not match server-rendered HTML.
Hydration failed because the initial UI does not match what was rendered on the server.
Unfortunately on production env we do not get any more information about what caused the problem, there is no information about which element caused the hydration that rebuilds the page.
There is a proposal in React’s repo for adding new functionality to debug hydration, but it looks like it have just vanished among other more urgent issues.
The (ideas for) solution
It could have been pretty obvious error but:
- it was only on production, no problems on dev env, where actually more info about place of the problem could be available (mostly ;))
- some of the pages with same structure had those errors and others didn’t — the only difference were the translation strings
- the translations were not changing the page’s structure so why the hydration error appeared on one language but not on other?
A lot of fixes where tried like:
- HTML structure mentioned in NextJS documentation
- disabling the 3rd party scripts to narrow the root of the problem
- replacing useLayoutEffect with useEffect
- getting rid of React’s refs and useEffect for layout updates totally
- cache busting to get latest content on the build time
- even good old Markup Validation Service to check if pages’ structures were correct
The code improved :) but the problem persisted.
The idea
After whole day of “guessing” a new idea emerged — comparing the HTML pages. If there is some change happening in HTML structure then the questions are when and where.
After comparing the generated by the next export
HTML pages with the ones that were placed on the server — no differences were found. To be sure the Postman application was used to get raw page from the server.
During that test one more thing was found — the published pages accessed directly didn’t have the hydration problems. That mean that something strange was happening on the way from the server to the client that enters the page through the domain.
After downloading the page again with Postman (but this time not from server directly but by entering the domain) and comparing to the original some small differences were found. So something had to change the page’s content on the way from server to end user.
The changes were very small and shouldn’t actually cause the hydration issue. The structure was the same but only some attributes were missing or names were changed, e.g.:
alt=””
was changed toalt
viewBox
was changed toviewbox
But there were changes and it looks like in some cases it was enough for React to initiate hydration process.
After some investigation the root cause was found — Cloudflare’s minification service. It was minifying the pages on fly and caused all those issues.
When you actually know what to look for then everything is easy: https://github.com/vercel/next.js/issues/15876
After disabling that feature the pages content served through the domain were exactly the same as the ones generated by the next export
command and the hydration problem disappeared.