Next.js: Adding GA script tag?

Created on 30 Oct 2016  ·  74Comments  ·  Source: vercel/next.js

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

Most helpful comment

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

All 74 comments

checkout <Head>

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

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

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

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

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

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

@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...

I can investigate this

On Tue, Jan 17, 2017, 7:59 AM Sébastien Dubois notifications@github.com
wrote:

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

  • @MoOx https://github.com/MoOx seems to imply that react-ga isn't
    necessary, should use Google AutoTrack
    https://github.com/googleanalytics/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...


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/zeit/next.js/issues/160#issuecomment-273108099, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAKHp9-2bfLXj2hMGlNlkqUSRqEL7R2Cks5rTJ8kgaJpZM4KkXxa
.

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

See https://github.com/MoOx/react-toulouse/commit/c42045d1001ab813c5d7eae5ab2f4f8c08c0025e for an example. It's not using next, but I guess it should be easy to adapt.

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

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 :)

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: https://github.com/relatenow/relate/commit/50dc3f317eb3efa045d5bbe40d1a1a1ad1116458

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

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

@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)

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 🙂

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

The production app GA Check fails.

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

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?

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

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.

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?

@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()
  }

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

@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.

@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))
  }

Trying to get this to work, but still no response from Google Analytics. This is my code in react-ga.js:

import ReactGA from 'react-ga'

const dev = process.env.NODE_ENV !== 'production'

export const initGA = () => {
  ReactGA.initialize("UA-XXXXXXXXX-X", {
    debug: dev,
  })
}

export const trackPageView = (
  pageName = window.location.pathname + window.location.search
) => {
  ReactGA.set({page: pageName})
  ReactGA.pageview(pageName)
}

export const trackCustomEvent = (category, action) =>
  ReactGA.event({category, action})

export default undefined

And my index page:

import {initGA, trackPageView} from '../modules/react-ga.js'
[...]
Index.componentDidMount = function() {
    initGA()
    trackPageView()
}

with the UA code replaced by my actual code, of course.

@filostrato How did you fix it? I'm stuck at it as well. :)

@exogenesys Ended up putting this in utils/analytics.js:

import ReactGA from 'react-ga'

const dev = process.env.NODE_ENV !== 'production'

export const initGA = () => {
  ReactGA.initialize("UA-xxxxxxxxx-x", {
    debug: dev,
  })
}

export const logPageView = (
  pageName = window.location.pathname + window.location.search
) => {
  ReactGA.set({page: pageName})
  ReactGA.pageview(pageName)
}

export const trackCustomEvent = (category, action) =>
  ReactGA.event({category, action})

export default undefined

and changed my Layout.js to extend React.Component:

export default class Layout extends React.Component {
    componentDidMount () {
      if (!window.GA_INITIALIZED) {
        initGA()
        window.GA_INITIALIZED = true
    }
    logPageView()
  }

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

Didn't find a way to make it work in a component which utilizes getInitialProps, but since Layout.js doesn't it worked just fine; not sure what to do if I want more detailed statistics, but I'll cross that bridge when I come to it.

@filostrato That did it. Thanks a lot. :)

Hey guys

I've just dealt with the implementation of GA on my site as well and this is what I've come up with.

First I used the solution from @notrab that seemed clean and straight forward:

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

  render() {
    …
  }
}

However, I noticed:

  • componentDidMount was executed when I switched between pages of different types, so the initialize function was called several times
  • componentDidMount was _only_ executed when I switched between pages of different types, so the pageview function was called only then => I have a video pagetype on my website and I saw in the GA Live view that while switching between different video pages only the initial video page was tracked

I created a higher order component to address these two problems. For the first one I used @filostrato's solution.

import React, { Component } from 'react';
import ReactGA from 'react-ga';
import Router from 'next/router';

const debug = process.env.NODE_ENV !== 'production';

export default (WrappedComponent) => (
  class GaWrapper extends Component {
    constructor (props) {
      super(props);
      this.trackPageview = this.trackPageview.bind(this);
    }

    componentDidMount() {
      this.initGa();
      this.trackPageview();
      Router.router.events.on('routeChangeComplete', this.trackPageview);
    }

    componentWillUnmount() {
      Router.router.events.off('routeChangeComplete', this.trackPageview);
    }

    trackPageview (path = document.location.pathname) {
      if (path !== this.lastTrackedPath) {
        ReactGA.pageview(path);
        this.lastTrackedPath = path;
      }
    }

    initGa () {
      if (!window.GA_INITIALIZED) {
        ReactGA.initialize('UA-XXXXXX-XX', { debug });
        window.GA_INITIALIZED = true;
      }
    }

    render() {
      return (
        <WrappedComponent {...this.props} />
      );
    }
  }
);

And in your <Layout> / <PageWrapper> / <PageScaffold> component or whatever you call it:

import GaWrapper from './ga-wrapper';

const PageScaffold = () => (…);

export default GaWrapper(PageScaffold);

Maybe that helps anyone.

I don't like the call of Router.router.events.… though, because this isn't part of the documented API. However, when I tried to call Router.onRouteChangeComplete which should be the correct way, I got a ReferenceError because onRouteChangeComplete was undefined. Maybe the documentation is outdated?

This has worked for me in a next.js website. And got around the 'window not defined' error I was running into with the usual implementation of react-ga.

@osartun solution worked for me. Thank you! 👍

Thanks to @osartun's code, I was able to come up with the following solution for firing pageview events using google's new gtag (which replaces the _universal analytics_ scripts):

pages/_document.js

import Document, { Head } from 'next/document';

const GA_TRACKING_ID = '..';

export default class MyDocument extends Document {
  render() {
    return (
      <html lang="en-AU">
        <Head>
          <script async src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`} />
          <script
            dangerouslySetInnerHTML={{
              __html: `
                window.dataLayer = window.dataLayer || [];
                function gtag(){dataLayer.push(arguments)};
                gtag('js', new Date());
                gtag('config', '${GA_TRACKING_ID}');
              `,
            }}
          />
        </Head>
        ...
      </html>
    );
  }
}

scaffold.js / layout.js / wraper.js / whatever your project names it:

import url from 'url';
import Router from 'next/router';

const GA_TRACKING_ID = '...';

const withPageViews = WrappedComponent =>
  class GaWrapper extends React.Component {
    componentDidMount() {
      // We want to do this code _once_ after the component has successfully
      // mounted in the browser only, so we use a special semiphore here.
      if (window.__NEXT_ROUTER_PAGEVIEW_REGISTERED__) {
        return;
      }

      window.__NEXT_ROUTER_PAGEVIEW_REGISTERED__ = true;
      let lastTrackedUrl = '';

      // NOTE: No corresponding `off` as we want this event listener to exist
      // for the entire lifecycle of the page
      // NOTE: This does _not_ fire on first page load. This is what we want
      // since GA already tracks a page view when the tag is first loaded.
      Router.router.events.on('routeChangeComplete', (newUrl = document.location) => {
        if (newUrl === lastTrackedUrl || !window.gtag) {
          return;
        }

        // Don't double track the same URL
        lastTrackedUrl = newUrl;

        // Believe it or not, this triggers a new pageview event!
        // https://developers.google.com/analytics/devguides/collection/gtagjs/single-page-applications
        window.gtag('config', GA_TRACKING_ID, {
          page_path: url.parse(newUrl).path,
        });
      });
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  };

const PageScaffold = () => (…);

export default withPageViews(PageScaffold);

For anybody coming late to the party, the reason @kylewiedman's solution doesn't work is because the Router events are being defined inside of componentDidMount. Defining them outside of the component seems to fix the problem:

https://gist.github.com/trezy/e26cb7feb2349f585d2daf449411d0a4

@trezy when using gtag, there seems to be no need to import ReactGA from 'react-ga'.

I used your <script dangerouslySetInnerHtml={...} /> example and just added this one line into the injection:

window.gaTrackingId = '${gaTrackingId}';

Afterwards, this lightweight trick began to work:

Router.onRouteChangeComplete = () => {
  if (window.gtag) {
    window.gtag('config', window.gaTrackingId, {
      page_location: window.location.href,
      page_path: window.location.pathname,
      page_title: window.document.title,
    });
  }
};

A pretty large analytics.js is no longer fetched, but all the pages are still tracked!

Since this is not obvious, it would be good to have an example in the repo.

Or a next-google-analytics repository 🕵️

@prichodko Thanks a lot for putting together this example!!

found pretty nice lib, added to main layout file in componentDidMount() and it seems to work correctly. It is well documented, no hustle

https://www.npmjs.com/package/react-gtm-module

@andylacko Indeed, I had forked it to fix a few things, like HTTPS for instance: https://github.com/Vadorequest/react-gtm

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.

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.

Hey Will. Does including ReactGA in your webpack (as it's run clientside) have a significant effect on your build size?

@ChrisEdson https://github.com/react-ga/react-ga the whole library is 161kb zipped.

Hmm. That’s absolutely huge to be honest!

On Thu, 28 Feb 2019 at 19:01, William Li notifications@github.com wrote:

@ChrisEdson https://github.com/ChrisEdson
https://github.com/react-ga/react-ga the whole library is 161kb zipped.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/zeit/next.js/issues/160#issuecomment-468395136, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AC5okwth_o_BLLPeqIcSBmhKineAhYfgks5vSCeUgaJpZM4KkXxa
.

that’s the number listed on GitHub for the whole library.
How do I check the actual size once packaged in app?

Sent from my iPhone

On 1 Mar 2019, at 15:43, Chris Edson notifications@github.com wrote:

Hmm. That’s absolutely huge to be honest!

On Thu, 28 Feb 2019 at 19:01, William Li notifications@github.com wrote:

@ChrisEdson https://github.com/ChrisEdson
https://github.com/react-ga/react-ga the whole library is 161kb zipped.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/zeit/next.js/issues/160#issuecomment-468395136, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AC5okwth_o_BLLPeqIcSBmhKineAhYfgks5vSCeUgaJpZM4KkXxa
.


You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.

So the minified script is 15kb. That's quite small.

I'm going to bundle analyse my webpack and I'll report back on how much it adds in practice.

Within a total bundle size of ~500kb, my ReactGA adds about 27kb. So pretty small in the grand scheme of things.

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.

@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).

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>

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>

@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(() => {...}, [])

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. :)

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

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).

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

@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!

@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.

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

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.

@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)

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

ok i think i got it to work

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?

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

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

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.

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??

_app.js

```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);
    });
}

This is hilarious. No, actually this is sad. Can't wait for React, Next, the whole ecosystem to die.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kenji4569 picture kenji4569  ·  3Comments

swrdfish picture swrdfish  ·  3Comments

flybayer picture flybayer  ·  3Comments

timneutkens picture timneutkens  ·  3Comments

olifante picture olifante  ·  3Comments