Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding GA script tag? #160

Closed
robinvdvleuten opened this issue Oct 30, 2016 · 76 comments
Closed

Adding GA script tag? #160

robinvdvleuten opened this issue Oct 30, 2016 · 76 comments

Comments

@robinvdvleuten
Copy link

Hi there!

I am currently converting my website to a next.js project and can't figure out on how to add the Google Analytics script tag to the page. Any thoughts?

Cheers,
Robin

@nodegin
Copy link
Contributor

nodegin commented Oct 30, 2016

checkout <Head>

@dariye
Copy link

dariye commented Oct 31, 2016

@robinvdvleuten I'd suggest creating a GoogleTagManager component and including it in the Head or right after it depending on your implementation choice.

@impronunciable
Copy link
Contributor

as people mentioned you can use the Head tag or a react-friendly solution like https://github.com/react-ga/react-ga

@gtramontina
Copy link

Any tips on getting react-ga working with next.js?

@impronunciable
Copy link
Contributor

@gtramontina it should work. Otherwise I'm one of the react-ga contributors so I could help with that

@musemind
Copy link

No need for react-ga. Use the google tag manager with the <Head> component to inject analytics. There are some small changes to the google tag manager code necessary to make it work in nextjs/react:

<Head>
      <script dangerouslySetInnerHTML={{__html: `(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
        new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
        j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
        'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
      })(window,document,'script','dataLayer','GTM-XXXXXX');`}} />
    </Head>
    <noscript dangerouslySetInnerHTML={{__html: `<iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXXX" height="0" width="0" style="display:none;visibility:hidden;"></iframe>`}} />

image

@MoOx
Copy link
Contributor

MoOx commented Dec 13, 2016

I would recommend a better solution to track client side page view: autotrack. See MoOx/phenomic#384 for more information.

@sedubois
Copy link
Contributor

@impronunciable it would be great to have an example as after some googling I'm still confused 😃 Could this be reopened?

  • @MoOx seems to imply that react-ga isn't necessary, should use Google AutoTrack instead
  • but Autotrack instructs to create a script tag with code which accesses the window object, so that doesn't work server-side
  • and anyway if using a react component such as react-ga, where should it be put? In a render? componentWillMount? getInitialProps? It's not super clear as it kind of supposes that you use react-router and load the code a single time for all pages.

The page tracking should work both when the page is server-rendered and then when navigating to another page. It would be would be great to see how the whole thing should integrate in a typical Next app with a Head, a page layout HOC, etc.

Would also be great to know if you think there's a better/simpler/less cumbersome/more state-of-the-art combination than Next.js + Google Analytics Autotrack...

@impronunciable
Copy link
Contributor

impronunciable commented Jan 17, 2017 via email

@MoOx
Copy link
Contributor

MoOx commented Jan 17, 2017

Pretty sure autotrack is more than enough, only injected on the client side - no need to think about the server-side.

@MoOx
Copy link
Contributor

MoOx commented Jan 17, 2017

See MoOx/react-toulouse@c42045d for an example. It's not using next, but I guess it should be easy to adapt.

@timneutkens
Copy link
Member

Yeah we don't want to track server renders, Since it Will obfuscate things like location.

@MoOx
Copy link
Contributor

MoOx commented Jan 17, 2017

No you don't since it will be handled by the client: keep in mind that a page is request by the client and will be rendered in the client browser :)

@sedubois
Copy link
Contributor

OK I didn't realise the code shouln't run server-side. So I used autotrack as recommended by @MoOx, I think this should work: sedubois/relate@50dc3f3

Just haven't confirmed yet if it counts page views correctly, this is new to me 😬 😄

@MoOx
Copy link
Contributor

MoOx commented Jan 17, 2017

You can use "real time" view to debug GA easily.

@sedubois
Copy link
Contributor

@MoOx yes well that's what bothers me, I don't see anything in the realtime panel when I visit https://relate.now.sh in parallel in another tab (0 active users)

@sedubois
Copy link
Contributor

I'm still not getting this... I don't get any data with the code I wrote.

All the examples I see seem to require a <script href="https://www.google-analytics.com/analytics.js" async /> but things in the Head don't seem to be executed in the client? (my console.log aren't printed in browser console...)

An example would still be very appreciated 🙂

@sedubois
Copy link
Contributor

Ah, got it working by switching to react-ga 😄

sedubois/relate@a9d2bce

screen shot 2017-01-18 at 14 01 28

@luandro
Copy link

luandro commented Mar 7, 2017

How did you get it to work? I've been trying to call a pageview function on componentDidMount.

The production app GA Check fails.

@sedubois
Copy link
Contributor

sedubois commented Mar 7, 2017

@luandro don't remember the details, but you can look at the commit I linked above if it helps

@luandro
Copy link

luandro commented Mar 7, 2017

I did and tried it. I did the same as you, calling configureAnalytics on top, and pageview on componentWillMount, but still get negative on GA Check.

In your code it doesn't seem you actually connected page hoc anywhere, did you?

@durk0
Copy link

durk0 commented Apr 10, 2017

@luandro Simply add ga(‘set’, ‘page’, window.location.pathname); ga(‘send’, ‘pageview’); to change url of pageview event.

@ryardley
Copy link

ryardley commented May 3, 2017

Maybe there is a more 'next.js' way to do this but WRT running scripts on the page I found that if I created a Script component like so:

// modules/Script.js
export default ({children}) => (
  <script dangerouslySetInnerHTML={{__html: `(${children.toString()})();` }}></script>
);

Then I could do this which worked for me:

import Document, { Head, Main, NextScript } from 'next/document'
import Script from '../modules/Script';

export default class MyDocument extends Document {

  render () {
    return (
      <html>
        <body>
          <Main />
          <Script>
            {
              () => {
                console.log('foo');
              }
            }
          </Script>
          <NextScript />
        </body>
      </html>
    )
  }
}

The caveat is that you must enclose everything in a function.

@karthikiyengar
Copy link

karthikiyengar commented May 17, 2017

Is it possible to track individual pageviews without explicitly calling the ga('send', 'pageview') on every page. If we absolutely have to do it, where do we invoke it from having certainty that ga has been loaded?

Is it possible to tie into route change events in some way to log pageviews?

@vinaypuppal
Copy link
Contributor

vinaypuppal commented May 17, 2017

@karthikiyengar I am using react-ga as suggested by others above and logging pageviews on componentDidMount of every page.

import ReactGA from 'react-ga'

export const initGA = () => {
  console.log('GA init')
  ReactGA.initialize('UA-xxxxxxxx-x')
}
export const logPageView = () => {
  ReactGA.set({ page: window.location.pathname })
  ReactGA.pageview(window.location.pathname)
}
componentDidMount () {
    initGA()
    logPageView()
  }

@youfoundron
Copy link

@vinaypuppal is it common practice to initGA for every page component?

@notrab
Copy link
Contributor

notrab commented May 31, 2017

@rongierlach I'm using a Layout component which calls the initialize and pageview methods on ReactGA within componentDidMount().

export default class extends Component {
  componentDidMount() {
    ReactGA.initialize('UA-1234567-1')
    ReactGA.pageview(document.location.pathname)
  }

  render() {
    return (
      <div>
        {this.props.children}
     </div>
    )
}

This is the cleanest way I've done it yet as if the page is server rendered, it needs to initialize. I'm sure there is a hook that could bypass the initialize if it's client rendered.

@kylewiedman
Copy link

@notrab couldn't you potentially be missing the shallow renders? They your app will just transition and your layout component may not remount. This is what I have to catch those.

componentDidMount() {
    ReactGA.initialize('xx-xxxxxxx-x')
    let trackMe = true

    if (trackMe) {
      ReactGA.pageview(document.location.pathname)
      trackMe = false
    }

    Router.onRouteChangeStart = () => {
      NProgress.start()
      this.store.dispatch(transitionPage(true))
      trackMe = true
    }
    Router.onRouteChangeComplete = () => {
      NProgress.done()
      this.store.dispatch(transitionPage(false))
      if (trackMe) { ReactGA.pageview(document.location.pathname) }
    }
    Router.onRouteChangeError = () => {
      NProgress.done()
      this.store.dispatch(transitionPage(false))
    }

    this.store.dispatch(calculateResponsiveState(window))
  }

ijjk pushed a commit to ijjk/next.js that referenced this issue Apr 13, 2019
* Add new Travis CI docs

* Implement feedback

Co-Authored-By: Timothy <hello@timothy.is>

* Improve Travis guide

* Fix weird spacing in Code blocks

* Better list order

* Further Travis guide improvements

* Give the first paragraph a context to Travis

* Better titles
@GodOfGrandeur
Copy link

GodOfGrandeur commented Jul 16, 2019

Just adding my solution to this here in case it might be useful for someone later on.

I used react-ga but the same logic should work with other ga tools.

_app.js's getInitialProps is triggered on the client side whenever user click on a new route. (First time it will run on server side).

componentDidMount of _app.js runs on client side only.

So in my _app.js, I added the following few lines of code


static async getInitialProps({ Component, router, ctx }) {
    let pageProps = {}

    if (Component.getInitialProps) {
      pageProps = await Component.getInitialProps(ctx);      
    }

    // client-side only, run on page changes, do not run on server (SSR)
    if (typeof(window) === "object") {
      ReactGA.pageview(ctx.asPath);
    }
    return { pageProps, router }
  }
  
  
  componentDidMount() {
    // client-side only, run once on mount
    ReactGA.initialize('UA-XXXXXXX-3');
    ReactGA.pageview(window.location.pathname + window.location.search);
  }

When the page is rendered in server, nothing will happen in getInitialProps as I wrapped the GA code in typeof(window) === "object".

On client side, getInitialProps will not run for the first time since everything is server rendered. GA is setup inside componentDidMount on the client side.

Subsequent route changes (even for the same route), will trigger getInitialProps on client side and ga event is triggered.

An issue which arises if you're using NextJS 9 is that this with disable Automatic Prerendering. Wondering if there is a better place to put the ReactGA.pageview(ctx.asPath);, removing it from getInitialProps means we can remove getInitialProps and not force Automatic Prerendering to opt-out as outlined here: https://err.sh/next.js/opt-out-automatic-prerendering.

Update: I found the with-google-analytics example (https://github.com/zeit/next.js/tree/canary/examples/with-google-analytics) and borrowed the bit about: Router.events.on('routeChangeComplete', url => ReactGA.pageview(url)) I need to test this to make sure url is right but looks like the right way. I've removed the getInitialProps method and added this above the class declaration.

@hkirsman
Copy link

@GodOfGrandeur This sounds promissing, will try in prod. Didn't get the version mentioned above working or seemed like so https://medium.com/@austintoddj/using-google-analytics-with-next-js-423ea2d16a98 Adding your code in _app.js seems to trigger everywhere like needed and I also like it's not triggered on server (would not have though it's an issue).

@elhakeem
Copy link

elhakeem commented Aug 25, 2019

No need for any third party, If you are using gtag use the following code I created:-

<script
    async
    src="https://www.googletagmanager.com/gtag/js?id=%your code here%" >
</script>
<script dangerouslySetInnerHTML={
    { __html: `
        window.dataLayer = window.dataLayer || [];
        function gtag(){window.dataLayer.push(arguments)}
        gtag("js", new Date());
        gtag("config", "<%your code here%>");
    `}
}>
</script>

@reinink
Copy link

reinink commented Oct 1, 2019

Another option is to use useEffect(). I've done this within my Layout.js file.

useEffect(() => {
  if (process.env.NODE_ENV === 'production') {
    window.dataLayer = window.dataLayer || []
    function gtag() {
      dataLayer.push(arguments)
    }
    gtag('js', new Date())
    gtag('config', '{GOOGLE ANALYTICS CODE}', {
      page_location: window.location.href,
      page_path: window.location.pathname,
      page_title: window.document.title,
    })
  }
})

And in the <Head>:

<Head>
  <script async src="https://www.googletagmanager.com/gtag/js?id={GOOGLE ANALYTICS CODE}"></script>
</Head>

@amorriscode
Copy link
Contributor

@reinink Just want to mention that you might want to pass an empty array as the second prop to useEffect so that it only runs the function once after the initial render.

useEffect(() => {...}, [])

@antonmyrberg
Copy link

Expanding on @reinink s solution, here's a working HOC I use to wrap my layout components/templates.

import React, { useEffect } from 'react'
import Head from 'next/head'
const GoogleAnalyticsHOC = ({ children }) => {
  useEffect(() => {
    if (process.env.NODE_ENV === 'production') {
      window.dataLayer = window.dataLayer || []
      // eslint-disable-next-line
      function gtag() {
        window.dataLayer.push(arguments)
      }
      gtag('js', new Date())
      gtag('config', process.env.GOOGLE_ANALYTICS_ID, {
        page_location: window.location.href,
        page_path: window.location.pathname,
        page_title: window.document.title
      })
    }
  }, [])
  return (
    <>
      <Head>
        <script async src={`https://www.googletagmanager.com/gtag/js?id=${process.env.GOOGLE_ANALYTICS_ID}`} />
      </Head>
      {children}
    </>
  )
}
export default GoogleAnalyticsHOC

It's not extensively tested but it works and passes myself as a realtime visitor running locally. :)

@UberVero
Copy link

UberVero commented Mar 4, 2020

I found this guide - is this a recommended approach (uses react-ga)?

@jerrygreen
Copy link
Contributor

I'd say recommended way is to use this official example:

https://github.com/zeit/next.js/tree/canary/examples/with-google-analytics

It's already posted here. Sometimes I wish github issues to be more like stackoverflow, having some answer accepted and highlighted, right after the question (when there's too many messages and emojis is not enough. One option is to restrict commenting this post, so people will create a new issue instead. And while creating they'll find a similar issue was already created (thanks to "similar issues" feature), and it has an answer (but still, the answer with emojis is lost somewhere in the middle of long history, so it's not very convenient to find answers on github).

@janbaykara
Copy link

janbaykara commented Apr 8, 2020

It's already posted here. Sometimes I wish github issues to be more like stackoverflow, having some answer accepted and highlighted, right after the question (when there's too many messages and emojis is not enough....

Github is prototyping this over on this repo's Discussions tab.

Screenshot 2020-04-08 at 16 59 58

@jerrygreen
Copy link
Contributor

@janbaykara wow, really? We all think the same way :) I hope they will find a way to somehow translate an issue to a discussion though, or it will be the same thing (if functionality will be merged to issues, and all existing discussions will become issues). But thanks for the response!

@Vadorequest
Copy link
Contributor

@3lonious I've personally ditched Google Analytics, as it does not do a good job with SPA in general. (really bad experience, especially with SSR+CSR and double events hard to track down)

Regarding Next.js, check out https://github.com/UnlyEd/next-right-now/blob/v2-mst-aptd-gcms-lcz-sty/src/components/pageLayouts/Head.tsx (using next/Head), that's how I include script tags in my apps.

Dont hesitate to take a look at NRN https://unlyed.github.io/next-right-now, it's meant to be a good example on "how to do X" with Next.js and should save you quite some headaches.

@3lonious
Copy link

im not sure i get it, i dont see any script tags in that file?

@antonmyrberg
Copy link

This is also a solution that works. I haven't tested it extensively so please do that yourself.

In pages/_document.js, add this to your <Head>. See the docs on how to override the document file.

<script async src='https://www.googletagmanager.com/gtag/js?id=YOUR_GA_TRACKING_ID'></script>
<script
    dangerouslySetInnerHTML={{
        __html: `
            window.dataLayer = window.dataLayer || [];
            function gtag(){dataLayer.push(arguments)}
            gtag('js', new Date());
            
            gtag('config', 'YOUR_GA_TRACKING_ID');
              `
      }}>
</script>

It reports traffic to my GA account at least.

@Vadorequest
Copy link
Contributor

@3lonious Script tags and style tags work the same way.
The above solution also works, from experience. (it's how I used to make GA work)

@3lonious
Copy link

ok thanks guys. im gonna try and get back to you

@3lonious
Copy link

ok i think i got it to work

@3lonious
Copy link

thanks Anton. ok i have a new one for you haha

i have a chat app i use for customer service but that script tag dosnt work either? any ideas?

@3lonious
Copy link

its telling me to add this second tag in the body? what do you do with that?
Screen Shot 2020-06-19 at 6 48 22 AM

@ernaneluis
Copy link

How About TBT? If you add google analytics it will slower your speed score

@jerrygreen
Copy link
Contributor

How About TBT? If you add google analytics it will slower your speed score

That’s because of their recommendation of placing the script before the content. If you place it under the content, the score won’t go down.

I personally don’t see real benefits of placing the script first. If the page load was somehow broken, and HTML didn’t fully load somehow and it was aborted, then I might not count it as a hit, so whatever.

@ChaitraK2000
Copy link

I m working with nextjs. I m trying to use react google place auto complete API. And wants to insert those script tag of API? Where should I insert those script tag??

@Poshtarenko-Dmitriy
Copy link

Poshtarenko-Dmitriy commented Aug 31, 2020

_app.js

    import ReactGA from "react-ga";
 
    componentDidMount() {
      ReactGA.initialize("XX-XXXXXXXXX-X", { debug: false });
      ReactGA.set({ page: this.props.router.pathname });
      ReactGA.pageview(this.props.router.pathname);
      this.unlisten = this.props.router.events.on("routeChangeComplete", (router) => {
          ReactGA.set({ page: router });
          ReactGA.pageview(router);
        });
    }

@jfbrennan

This comment has been minimized.

hkirsman added a commit to hkirsman/tsitaatcom_site_frontend that referenced this issue Oct 22, 2020
@RiusmaX
Copy link

RiusmaX commented Mar 1, 2021

With React hooks :

_app.js

import { useEffect } from 'react'
import { useRouter } from 'next/router'
import ReactGA from 'react-ga'
import Layout from '../components/Layout'

function MyApp ({ Component, pageProps }) {
  const router = useRouter()

  useEffect(() => {
    const handleRouteChange = (url, { shallow }) => {
      ReactGA.set({ page: url })
      ReactGA.pageview(url)
    }
    ReactGA.initialize('XX-XXXXXXXXX-X', { debug: false })
    ReactGA.set({ page: router.pathname })
    ReactGA.pageview(router.pathname)
    router.events.on('routeChangeComplete', handleRouteChange)
    return () => {
      router.events.off('routeChangeComplete', handleRouteChange)
    }
  }, [])

  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  )
}

export default MyApp

@balazsorban44
Copy link
Member

This issue has been automatically locked due to no recent activity. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@vercel vercel locked as resolved and limited conversation to collaborators Jan 28, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests