Next.js + Sentry + PostHog integration hydration error #1645
Description
Steps to reproduce:
- Configure a Next.js app with
[email protected]
as outlined in the guide (including placingposthog.init
in auseEffect
). - Add Sentry
8.x
and set up PostHog Sentry integration as outlined in the guide..
Expected: Things work :)
Observed: Next.js starts throwing a hydration error. Specifically, [email protected]
will log a warning stating "Prop dangerouslySetInnerHTML
did not match", while [email protected]
will log "A tree hydrated but some attributes of the server-rendered HTML didn't match the client properties." More detailed errors below.
Triage
This seems to happen because calling posthog.init
loads a remote config JS file by inserting a script
tag.
The error disappears if I disable remote script loading with disable_external_dependency_loading: true
; however, that also disables surveys, so it's not ideal.
I can see two possible solutions:
- Expose a flag that would switch enable
_loadRemoteConfigJSON
by default. This should allow Nextjs to hydrate without error while still getting the settings. - Let me, as the user of the library, optionally load
config.js
. E.g., if the library could skip inserting the script tag based onid
, I could manually add<Script id="deterministic-posthog-config-key" strategy="beforeInteractive" src="https://my.app/ingest/array/phc_Ds…/config.js" />
inlayout.tsx
and therefore load the config without a warning.
Sample errors
Next 14.22
Given a <Script strategy="beforeInteractive" src="https://js.stripe.com/v3/" />
in the layout:
Prop `dangerouslySetInnerHTML` did not match. Server: "" Client: "(self.__next_s=self.__next_s||[]).push([\"https://js.stripe.com/v3/\",{}])"
script
Script
body
html
RootLayout (Server)
Component@
RedirectBoundary
Component@
NotFoundBoundary
DevRootNotFoundBoundary
PureComponent@
HotReload
Router
Component@
ErrorBoundary
AppRouter
ServerRoot
Root
Details
A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:
- A server/client branch
if (typeof window !== 'undefined')
. - Variable input such as
Date.now()
orMath.random()
which changes each time it's called. - Date formatting in a user's locale which doesn't match the server.
- External changing data without sending a snapshot of it along with the HTML.
- Invalid HTML tag nesting.
It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
https://react.dev/link/hydration-mismatch
...
<Router actionQueue={{state:{...}, ...}} assetPrefix="">
<ReactDevOverlay state={{nextId:1, ...}} dispatcher={{...}}>
<HTTPAccessFallbackBoundary notFound={}>
<HTTPAccessFallbackErrorBoundary pathname="/home" notFound={} ...>
<Script id="log-locati..." strategy="beforeInte...">
<script
nonce={undefined}
dangerouslySetInnerHTML={{
-
__html: "(self.__next_s=self.__next_s||[]).push([0,{\"children\":\"\\n cons..."
-
__html: "" }}
-
type="text/javascript"
-
crossorigin="anonymous"
-
src="https://my.app/ingest/array/phc_Ds..." > <Script strategy="beforeInte..." src="https://js.stripe.com..."> <script nonce={undefined} dangerouslySetInnerHTML={{
-
__html: "(self.__next_s=self.__next_s||[]).push([\"https://js.stripe.com/v3..."
-
__html: "(self.__next_s=self.__next_s||[]).push([0,{\"children\":\"\\n cons..." }} >
</p>
</details>