posthog-js icon indicating copy to clipboard operation
posthog-js copied to clipboard

'dangerouslySetInnerHTML' warning when initializing posthog and next-theme

Open msquinn opened this issue 2 years ago • 6 comments

When I try to use posthog in addition to next-theme I get an error in the browser saying Warning: Prop dangerouslySetInnerHTML did not match. I'm using Next 13 app router. It seems as though adding my posthog provider to the layout component strips some attributes related to my theme from my project at build time, which later get applied and loaded client side, causing a mismatch. Here's a code sandbox with the minimal code to reproduce the issue. Note, to actually see the error in the sandbox you'll need to pass in your own API key for Posthog and view the application in it's own browser tab and then check the logs from there.

Screenshot 2023-08-16 at 9 08 36 AM

msquinn avatar Aug 16 '23 14:08 msquinn

Also running into this issue, any fixes?

jaxony avatar Jan 01 '24 06:01 jaxony

bumping this! having the same issue

financialvice avatar Feb 22 '24 23:02 financialvice

same issue here

jakevollkommer avatar Feb 28 '24 20:02 jakevollkommer

So I originally posted my question a few months back. The response from the posthog team was underwhelming. They just told me to leave as is. I actually ended up using a different project called highlight.io for session recording, because I couldn't figure out the proper workaround. It probably exists, though it might just be to suppress the hydration warning.

https://nextjs.org/docs/messages/react-hydration-error

msquinn avatar Feb 28 '24 21:02 msquinn

I've narrow it down to the session recordings.

https://github.com/PostHog/posthog-js/assets/1843792/c1d80d03-d462-4d96-a38a-eacd7c16ad66

Everything else seems to be working fine

wladpaiva avatar Apr 28 '24 00:04 wladpaiva

Can we init posthog after first render, by useEffect? Works for me:

export default function CSPostHogProvider({ children }: { children: ReactNode }) {
    useEffect(() => {
        if (!posthog.__loaded) {
            posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY || "", { api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST });
        }
    }, []);
    return <PostHogProvider client={posthog}>{children}</PostHogProvider>;
}

andlipro avatar Jun 11 '24 19:06 andlipro

Has anybody found a solution for this?

stormynight9 avatar Jul 08 '24 16:07 stormynight9

+1 seeing this in remix with prop mismatches if I add posthog

martinamps avatar Jul 10 '24 20:07 martinamps

The solution I've found for this is to create a new org for development only and disable screen recording on PH's dashboard

wladpaiva avatar Jul 11 '24 14:07 wladpaiva

hey all, sorry we missed this!

i'm just investigating to see what I can find out (but I'll admit to not having used SSR or Next JS much so feel free to prompt or correct me!)


I've forked the supplied code sandbox and can replicate the issue whether or not I include posthog

That might only mean I'm confused about the problem :)

If I open the application in incognito mode then I don't get the error

Adding posthog back in using just the JS snippet (and in incognito mode) I don't get the error

And doing the same with the code sandbox in the initial OP I don't get an error in incognito but I do in my normal browser


So I originally posted my question a few months back.

@msquinn sorry you weren't happy with the response... you linked to the nextjs link in your comment so I can't follow up, but I'm happy to look into it 💖

pauldambra avatar Jul 11 '24 19:07 pauldambra

So, I'm only getting the error because I have the locator js extension installed

are others able to test if they still get the error in an incognito window or with all browser extensions disabled

(I'm super happy to investigate further but need to be able to consistently repro or i'll just be chasing my tail - and you all will know next/ssr better than me 😊)

pauldambra avatar Jul 11 '24 19:07 pauldambra

@wladpaiva toggling session replay didn't change anything for me but then I may just not be replicating the problem correctly)

pauldambra avatar Jul 11 '24 19:07 pauldambra

also see https://x.com/martinamps/status/1811491989084577911 https://github.com/martinamps/posthog-repro/tree/main

pauldambra avatar Jul 11 '24 21:07 pauldambra

We are indeed using radix components, specifically shadcn/ui

jakevollkommer avatar Jul 11 '24 21:07 jakevollkommer

so looking at the repro provided by martin i get the same error if i don't include posthog

import { RemixBrowser } from "@remix-run/react";
import { startTransition, StrictMode, useEffect } from "react";
import { hydrateRoot } from "react-dom/client";
// import posthog from "posthog-js";

function PosthogInit() {
  // useEffect(() => {
  //   posthog.init('phc_RpO1rh8u4ZgIDxSelv4kQDvyiyyty6iUiLpbs3sTOyb', {
  //     api_host: 'https://us.i.posthog.com',
  //     person_profiles: 'identified_only',
  //   });
  // }, []);

  return null;
}
startTransition(() => {
  hydrateRoot(
    document,
    <StrictMode>
        <RemixBrowser />
        <PosthogInit/>   
    </StrictMode>
  );
});

causes the error

if you comment out <PosthogInit/> then the hydrate error goes away

(i also had to disable two browser extensions that caused a similar error in order to test this)


So, I could still be misunderstanding but still can't repro :)

pauldambra avatar Jul 11 '24 22:07 pauldambra

Ahh nice catch - thanks for looking. We'll add this to the list of reasons I have largely avoided frontend for the past decade 😂 .... For remix at least, this may simply be a documentation issue.. some googling/testing suggests this is a better implementation -- it works + no warnings:

import * as React from 'react';
import { RemixBrowser } from '@remix-run/react';
import { hydrateRoot } from 'react-dom/client';
import posthog from 'posthog-js';

function hydrate() {
  posthog.init('token', {
    api_host: 'https://us.i.posthog.com',
    person_profiles: 'identified_only',
  });

  React.startTransition(() => {
    hydrateRoot(
      document,
      <React.StrictMode>
        <RemixBrowser />
      </React.StrictMode>,
    );
  });
}

if (window.requestIdleCallback) {
  window.requestIdleCallback(hydrate);
} else {
  window.setTimeout(hydrate, 1);
}

likely could go even simpler and throw the

disclaimer: I started learning Remix last week, so I'm no expert

martinamps avatar Jul 12 '24 14:07 martinamps

you've got a week on me @martinamps 🤣

pauldambra avatar Jul 12 '24 14:07 pauldambra

hey all, i'm going to close this so that future travellers see that the problem/solution here was never related to PostHog and it's only that SSR is tricky

ofc if someone can come up with a repro of where posthog isn't working well here do reopen this or log a new issue - we love fixing things - i just can't find anything for us to fix here 😊 😌

pauldambra avatar Jul 13 '24 10:07 pauldambra

@pauldambra though I can't speak tot he original nextjs issue, it seems reasonably likely the documentation/instructions provided by PostHog are wrong for remix though. So I'd advocate updating those to mitigate new users people churning or opening issues like this

martinamps avatar Jul 13 '24 10:07 martinamps

For Next.js, you can use next/dynamic like this:

// app/layout.js

import './globals.css'
import { PHProvider } from './providers'
import dynamic from 'next/dynamic'

const PostHogPageView = dynamic(() => import('./PostHogPageView'), {
  ssr: false,
})

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <PHProvider>
        <body>
          <PostHogPageView /> 
          {children}
        </body>
      </PHProvider>
    </html>
  )
}

See our docs for more: https://posthog.com/docs/libraries/next-js#app-router

ivanagas avatar Jul 15 '24 20:07 ivanagas

For Next.js, you can use next/dynamic like this:

// app/layout.js

import './globals.css'
import { PHProvider } from './providers'
import dynamic from 'next/dynamic'

const PostHogPageView = dynamic(() => import('./PostHogPageView'), {
  ssr: false,
})

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <PHProvider>
        <body>
          <PostHogPageView /> 
          {children}
        </body>
      </PHProvider>
    </html>
  )
}

See our docs for more: https://posthog.com/docs/libraries/next-js#app-router

we've been doing this since the beginning and still seeing the warning every time

jakevollkommer avatar Jul 29 '24 21:07 jakevollkommer

I am still facing this issue:

My PHProvider:

"use client";

import posthog from "posthog-js";
import { PostHogProvider } from "posthog-js/react";

import { IS_POSTHOG_ENABLED } from "./client";

if (IS_POSTHOG_ENABLED) {
  posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
    api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
    capture_pageview: false,
    session_recording:
      process.env.NODE_ENV === "production"
        ? {
            maskAllInputs: true,
          }
        : undefined,
  });
}

export function PHProvider({ children }: { children: React.ReactNode }) {
  return <PostHogProvider client={posthog}>{children}</PostHogProvider>;
}

Aggregating all my providers like this here here:

export function Providers({
  children,
  language,
  currentUser,
}: React.PropsWithChildren<{ language: string; currentUser: User | null }>) {
  return (
    <SessionProvider>
      <CurrentUserProvider serverValue={currentUser}>
        <I18nProvider language={language}>
          <SWRConfig value={SwrConfig}>
            <ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
              <PHProvider>{children}</PHProvider>
            </ThemeProvider>
          </SWRConfig>
        </I18nProvider>
      </CurrentUserProvider>
    </SessionProvider>
  );
}

And then in layout/app I import them:

export default async function RootLayout({ children }: React.PropsWithChildren) {
  // ... doing here some stuff around session and language

  return (
    <html lang={lang} suppressHydrationWarning>
      <body>
        <Providers language={lang} currentUser={currentUser}>
          <NextTopLoader color="#6AA0A0" showSpinner={false} />
          <Toaster position="top-right" richColors />
          {children}
        </Providers>
        <Analytics />
      </body>
    </html>
  );
}

It still leads to: Bildschirmfoto 2024-08-08 um 16 29 31

Though I followed the guides here: Posthog Guide Vercel Guide

Any idea how to get this solved?

maakle avatar Aug 08 '24 14:08 maakle

@maakle I'm not working at the moment so may not react quickly here but... in previous reproductions people shared I completely removed PostHog and they still got the error. In your example the error appears to be to do with a theme. If you completely remove PostHog do you still get the error

pauldambra avatar Aug 08 '24 15:08 pauldambra

@maakle , try init posthog inside PHProvider in useEffect. This is possible solution for many NextJS SSR issues (https://nextjs.org/docs/messages/react-hydration-error)

andlipro avatar Aug 08 '24 15:08 andlipro

I think the issue was added in a newer package. When reverting back to an older version of the sdk e.g. "posthog-js": "^1.130.2", it works.

@andlipro your approach also works with the latest "posthog-js": "^1.154.5" as of this writing. Thank you!

"use client";

import posthog from "posthog-js";
import { PostHogProvider } from "posthog-js/react";
import { useEffect } from "react";

import { IS_POSTHOG_ENABLED } from "./client";

export function PHProvider({ children }: { children: React.ReactNode }) {
  useEffect(() => {
    if (IS_POSTHOG_ENABLED) {
      posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
        api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
        capture_pageview: false,
        session_recording:
          process.env.NODE_ENV === "production"
            ? {
                maskAllInputs: true,
              }
            : undefined,
      });
    }
  }, []);

  return <PostHogProvider client={posthog}>{children}</PostHogProvider>;
}

maakle avatar Aug 08 '24 15:08 maakle

cc @ivanagas for docs inspiration maybe

pauldambra avatar Aug 08 '24 15:08 pauldambra

Ty, documenting here: https://github.com/PostHog/posthog.com/pull/9143

If this doesn't work for someone, please send recreation of issue and I will try to debug.

ivanagas avatar Aug 08 '24 22:08 ivanagas

@ivanagas without if (!posthog.__loaded) { check inside useEffect it shows warning about double initialization to me. Can we use this check or there are public method instead?

andlipro avatar Aug 08 '24 22:08 andlipro

@ivanagas without if (!posthog.__loaded) { check inside useEffect it shows warning about double initialization to me. Can we use this check or there are public method instead?

What is the warning? When you call init, the library will check the __loaded method itself and return the current instance. See: https://github.com/PostHog/posthog-js/blob/f5a0d12603197deab305a7e25843f04f3fa4c99e/src/posthog-core.ts#L365-L376

ivanagas avatar Aug 08 '24 22:08 ivanagas

yes, this warning. I see warning and I assume that calling init twice is wrong, and I should take care about this check in my code. But there are no other method to check __loaded

andlipro avatar Aug 08 '24 22:08 andlipro