vercel / next.js

The React Framework
https://nextjs.org
MIT License
125.07k stars 26.71k forks source link

with-google-tag-manager: `pageview` event doesn't fire on initial page load #21037

Open dsbrianwebster opened 3 years ago

dsbrianwebster commented 3 years ago

What example does this report relate to?

with-google-tag-manager

What version of Next.js are you using?

10.0.3

What version of Node.js are you using?

10.0.3

What browser are you using?

Chrome

What operating system are you using?

macOS

How are you deploying your application?

next start

Describe the Bug

pageview event doesn't fire on initial page load, only routchange

Expected Behavior

to be able set up a single tag in GTM to use the "pageview" dataLayer event, however the routeChangeComplete only fires on subsequent page views.

To Reproduce

We're going with a slight adaptation from the example, using a hook instead of a regular component. Either way, I think the addition of a call to gtm.pageview on initialPage would be a nice addition to with-google-tag-manager.

export default function useGTM() {
  const router = useRouter();

  React.useEffect(() => {
    const handleRouteChange = (url: string) => {
      // Wrap it in a rAF to ensure the correct data is sent
      // https://github.com/zeit/next.js/issues/6025
      requestAnimationFrame(() => gtm.pageview(url));
    }
    router.events.on('routeChangeComplete', handleRouteChange);
    return () => router.events.off('routeChangeComplete', handleRouteChange);
  }, [router.events]);
}
sanders54 commented 3 years ago

You could try using this package. Init Google Tag Manger in app.js clientside.

React.useEffect(() => {
         // init GA/GTM
        TagManager.initialize({
            gtmId: "YOUR-GTM-ID"
        })
}, []); 
RXminuS commented 3 years ago

I have a sneaky suspicion it's related to this https://github.com/vercel/next.js/issues/11535. We've also been having issues with several Pixel trackers who under-reported conversions and we think it's because of a similar reason. I'm investigating now

dsbrianwebster commented 3 years ago

@RXminuS, perhaps.

The requestAnimationFrame(() => gtm.pageview(url)); bit in our hook (above) might help #11535, I commented there as well.

But, I don't believe that routeChangeComplete is supposed to fire on the initialPage load though. So the primary need to close this issue is for the with-google-tag-manager example to fire a gtm.pageview(url) on the initial page load.

balazsorban44 commented 3 years ago

We had the same problem, and therefore opened a PR with a solution we came up with at work!

https://github.com/vercel/next.js/pull/21824

Let me know if it fixes your issue!

dsbrianwebster commented 3 years ago

@balazsorban44 really interesting approach using the reportWebVitals function! I like the logic behind it a lot and that would certainly solve the issue. Looking forward to seeing the feedback from the Next.js team you added for review.

morganfeeney commented 3 years ago

Hey @dsbrianwebster I've been using GTM quite a bit at work, we had to solve related issues. What you have said about the gtm.pageView event not firing on page load is correct, the main purpose of gtm.pageView is to deal with window.onLoad not firing when routing between pages, which causes issues with reporting.

You can also use the window.onLoad event to push data into the dataLayer, and I'm pretty sure you can use GTM to send data to analytics using triggers and tags.

Without completely understanding what it is you are aiming for, all I can provide is this quick and dirty example:

// _app.js

import '../styles/globals.css'
import { useEffect } from 'react'

function MyApp({ Component, pageProps }) {

  useEffect(() => {
    window.onload = () => {
      window.dataLayer?.push({ event: 'page view from onload' });
    };
  },[])

  return <Component {...pageProps} />
}

export default MyApp

I hope it helps, if not, feel free to give me more detail, e.g. how is the data being fed to analytics, is that even a factor? p.s. You can also setup a history event trigger to track all history state changes, without using callbacks via routeChangeComplete, but this also won't deal with window.onLoad.

What I also realised is that when I wanted to push more data via pageProps, using router.events didn't help, I've written more detail here in an article.

pfcodes commented 2 years ago

Hey @dsbrianwebster I've been using GTM quite a bit at work, we had to solve related issues. What you have said about the gtm.pageView event not firing on page load is correct, the main purpose of gtm.pageView is to deal with window.onLoad not firing when routing between pages, which causes issues with reporting.

You can also use the window.onLoad event to push data into the dataLayer, and I'm pretty sure you can use GTM to send data to analytics using triggers and tags.

Without completely understanding what it is you are aiming for, all I can provide is this quick and dirty example:

// _app.js

import '../styles/globals.css'
import { useEffect } from 'react'

function MyApp({ Component, pageProps }) {

  useEffect(() => {
    window.onload = () => {
      window.dataLayer?.push({ event: 'page view from onload' });
    };
  },[])

  return <Component {...pageProps} />
}

export default MyApp

I hope it helps, if not, feel free to give me more detail, e.g. how is the data being fed to analytics, is that even a factor? p.s. You can also setup a history event trigger to track all history state changes, without using callbacks via routeChangeComplete, but this also won't deal with window.onLoad.

What I also realised is that when I wanted to push more data via pageProps, using router.events didn't help, I've written more detail here in an article.

thanks for this

kyubums commented 1 year ago

In my case, react1^8, next^13.

window.load is not firing well on every route changing I use useEffect with router.asPath and It works perfectly.

here's my short example

const router = useRouter();

useEffect(() => {
    const delay = setTimeout(() => {
      window.dataLayer.push({
        event: 'ShowPage',
      });
    }, 0);

    // To avoid fire event twice. react^18, on development mode.
    return () => {
      clearTimeout(delay);
    };
}, [router.asPath]);
philiplindberg commented 1 year ago

Experiencing the same problem. I ended up using the react-gtm-module library to conditionally initialize Google Tag Manager (GTM) based on the current environment and set up a custom event (next_route_change) to track all page views (including the initial page load). Here's what the relevant section of my _app.tsx looks like:

export default function App({ Component, pageProps }: AppProps) {
  const router = useRouter()

  useEffect(() => {
    if (process.env.NODE_ENV !== 'production') return

    TagManager.initialize({ gtmId: process.env.NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID })
    gtmEvent('next_route_change')
  }, [])

  useEffect(() => {
    if (process.env.NODE_ENV !== 'production') return

    const routeChange = () => gtmEvent('next_route_change')
    router.events.on('routeChangeComplete', routeChange)

    return () => {
      router.events.off('routeChangeComplete', routeChange)
    }
  }, [router.events])

  return <Component {...pageProps} />
}

Here's my GTM helper function:

export function gtmEvent(event: string) {
  if (!window.dataLayer) return
  window.dataLayer.push({ event })
}

To make typescript happy, I also had to add a global.d.ts file and declare the following types:

declare global {
  interface Window {
    dataLayer: Record<string, any>[]
  }
}

export {}
dkrefta commented 1 year ago

@philiplindberg thanks for sharing :) it was not working for me either when I was using the method of the doc example, now it's working using the react-gtm-module, I hate to use libs but this time was all the fault of the Vercel :rage4: