react: Make hydration errors more actionable
Hey folks! We at Sentry build error/performance monitoring SDKs and wanted to reach out to see if we could improve the state of hydration errors and make them more actionable. Specifically, we want to look at the stacktraces of hydration errors when you are using production react bundles.
Since React 18 has been getting more adoption, many of our users using React SSR apps have been getting flooded with hydration errors, like listed below:
'https://reactjs.org/docs/error-decoder.html?invariant=422', // There was an error while hydrating this Suspense boundary. Switched to client rendering.
'https://reactjs.org/docs/error-decoder.html?invariant=423', // There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root...
'https://reactjs.org/docs/error-decoder.html?invariant=425' // Text content does not match server-rendered HTML...
When we discussed this with the community, many users simply wanted to just filter these errors because they were not actionable.
Looking at the implementation in the codebase a simple string error is thrown:
and because of how it is generated the stack trace is often not detailed enough to give users insights about what components/functions were problematic.
For example, here’s a stacktrace I generated from a stock Next.js application with one of these hydration errors:
https://sentry-test.sentry.io/share/issue/b70ea591bbfc4728b33da495148ac9cd/

Error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.
at Vk(./node_modules/react-dom/cjs/react-dom.production.min.js:280:383)
at 4448/oF/<(./node_modules/react-dom/cjs/react-dom.production.min.js:280:319)
at Jk(./node_modules/react-dom/cjs/react-dom.production.min.js:280:319)
at Ok(./node_modules/react-dom/cjs/react-dom.production.min.js:271:86)
at Hk(./node_modules/react-dom/cjs/react-dom.production.min.js:268:399)
at J(./node_modules/scheduler/cjs/scheduler.production.min.js:13:197)
at R(./node_modules/scheduler/cjs/scheduler.production.min.js:14:126)
As you can see the frames all come from react-dom internals, which means users have no way to start investigating where to look beyond the URL of the page.
In the end we’ve decided to filter these out from default from sentry: https://github.com/getsentry/sentry/issues/45038, but we recognize this is not an ideal solution. Hydration errors are things people should fix, and we want to make it easier for people to fix them!
So with that in mind, are there ways we could make this easier for users? Could we attach a componentStack like we do with error boundaries for these errors? Could we point users to the element that was causing this issue? Any and all ideas/feeback greatly appreciated.
About this issue
- Original URL
- State: open
- Created a year ago
- Reactions: 45
- Comments: 17 (4 by maintainers)
These changes only affect dev mode right? I think production hydration errors still have these same data quality issues.
Unfortunately there are a variety of environments where dev mode isn’t good enough to debug this, see this twitter thread as an example.
For folks using Replay, which has DOM snapshots, we can help you debug this with a diff:
https://changelog.getsentry.com/announcements/diff-hydration-errors-with-replay
Heya!
I happened on this whilst helping @slorber look into a hydration issue that has surfaced as Docusaurus migrates to React 18:
https://github.com/facebook/docusaurus/issues/9379#issuecomment-1754450268
We tried plugging in the errorInfo logging in place we got this:
It’s more information than we had, but it’s still not very actionable. Consider the
componentStackIt’s not obvious what this reveals, beyond that a div is involved. Are there other steps we should be following to get more meaningful output perhaps?
The above was obtained by:
Browse to http://localhost:3000/ and opening devtools to look at the console.
Interestingly (and not related to this) we’ve been seeing errors that only seem to surface on Ubuntu which is intriguing.
@gaearon apologize for the delay, had gone on vacation last week!
made a quick example to help address some of the points you made: https://github.com/AbhiPrasad/nextjs-hydration-error-example using a nextjs app.
I had to monkeypatch
onRecoverableErrorsince next does not expose this, so please excuse that part of the code 😅.You’re right that they mostly help. We generally get a
componentStacklike so in prod builds:This + the URL of the page is usually good enough to figure out where the issue is. The problem is they aren’t solely the built-in components all the time, so we get minified React component names in there for more complex use cases, which is still pretty confusing for users.
This would be great!
When running https://github.com/AbhiPrasad/nextjs-hydration-error-example in production mode, invariant 423 does not generate a
componentStack(message:There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.)This is partly a Sentry consequence, since we need the filename and line/col number to do source mapping, which helps with the case where there are minified component names in the stacktrace. Something like
G@http://localhost:3000/_next/static/chunks/main-b1241a9a70bb7dcd.js:1:7600for example.But we can live without this for now!
Finally, when you generate a hydration issue, you get 3 errors thrown:
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.I understand why all of these errors must be thrown (they all signal different parts of the problem), but IMO the experience of getting all of these in the console/in your production error monitoring system is confusing and can be overwhelming for users, especially those on the more junior side. Perhaps we can think of a way of reducing the amount of errors thrown?
Having
componentStackas a property on hydration errors would be really cool and useful.Even though it would also be cool to have file/lineno/col for individual tags I don’t think it is absolutely necessary - without it the component stack is already useful enough. Implementing this would probably mean having to patch the stack trace together somehow using synthetic errors which is probably quite expensive.
Appreciate the quick response!
So this works pretty decently to a certain extent, we just have to convince framework authors to expose these hooks more easily, as it’s better than nothing.
For 425 and 418, we get
componentStacks:So here
a,div, andiare react components, which should help a lot with debugging. The only issue is that they don’t have function names/line and col numbers - so we can’t apply sourcemaps to transform these into readable component names. Of course people can set adisplayName, but that’s not realistic for a bigger application.Here’s some basic boilerplate that will work to send this stuff to an error monitoring service:
It seems like 423 does not generate a
componentStackthough. Given though that the other invariants are always thrown with this, I think it’s fine.So with that in mind two immediate items:
onRecoverableError.Seems like this has been a feature request in Next.js, and it seems Astro can also improve here. Remix thankfully seems to be super transparent with exposing
hydrateRoot. Gatsby also allows for this. We at Sentry can help talk to these folks!componentStack.Without proper frames, we can never apply sourcemaps, so users are stuck with minified component names.
As for anything else, I’m trying to brainstorm what other tools we can give to people to help identify what to fix here. Ideally most folks just want the line of code that they need to change.
If bundle size is a concern from adding new logic, perhaps we can add an API that returns a errored response to the server that CSR had to occur. Then the server could report many details about what kind of HTML it tried to serve, and as along as you can trace the error somehow between backend/frontend, you might be able to debug this faster.
Seems like Next.js 13.2 has improved their error overlays and local debugging experience for these - https://github.com/facebook/react/issues/24519#issuecomment-1439915463
https://github.com/vercel/next.js/pull/45089 seems to be this PR
Edit: These are dev only - which still means the production stacktraces still have the same problem - which is what we are looking to improve here.