How to add Google Analytics to Next.js using the Script Component (🙏 Stop using inline script)
Tags: nextjs, analytics
Published: 2022-01-17 10:18:37

If you are going to use Next.js for your future project, or you've already deployed a Next.js web app,  __please be sure that the Google Analytics script is not added via an inline script ⚠️__

![please gif](https://media.giphy.com/media/wfv1wVwt74kmI/giphy.gif)

It's widely known that the __website loading performance__ hugely impacts on User Experience and, since the [latest Google Algorithm updates](https://web.dev/vitals/), on SEO (Search Engine Optimization).

We can literally make our website __fall down__ in Google SERPs rankings if we don't follow the best practices in loading strategy of third-party scripts. Going deep into the complex world of third-party scripts is out of the scope of this guide, if you want to read more about it I suggest  [this amazing article.](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/loading-third-party-javascript).

__So now you may be wondering: Which is the "right way" to add Google Analytics script to a Next.js website?__

![That is an excellent question question gif](https://media.giphy.com/media/dXICCcws9oxxK/giphy.gif)

The answer comes directly from  [Vercel](https://nextjs.org/docs/messages/next-script-for-ga) 

> If you are using the gtag.js script to add analytics, use the 'next/script' component with the right loading strategy to defer loading of the script until necessary.

Thanks to the [Next.js Script Component](https://nextjs.org/docs/basic-features/script) we can choose the __right load strategy__ and defer the load of the Google Analytics script until necessary, so it will no longer be able to render-block and delay any page content from loading. 🥳

## Globally Inject Google Analytics Script in Next.js

Navigate to Google Analytics website and grab the Measurement ID. If you don't have one, all you have to do is to create a new Data Stream.

-----> Admin ----> Data Streams

![data streams.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1642370811919/QEibPr_wj.png)

----> Web

![choose web.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1642370919953/27HqGIDao.png)

- Add your website url
- Add your website name
- Click Create Stream button

![create stream.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1642371194645/-G3TK0iHZ.png)

- Copy the Measurement ID 

![measurement id and script.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1642371448732/2RUvb8rc5.png)

Let's open now the Next.js project into Visual Studio Code and create (if you don't already have one) a `.env.local` file (at root level) so we can set our Measurement ID inside an [Environment Variable](https://nextjs.org/docs/basic-features/environment-variables)

```
NEXT_PUBLIC_MEASUREMENT_ID: 'xxxxxx Your measurement id xxxxxx'
```

NEXT_PUBLIC prefix is needed to let our Next.js app know that this env variable has to be __exposed to the browser.__

Now that we have our Measurement ID set up inside and env variable, we can __globally inject__ the Google Analytics script code via the __Next.js Script Component__ and define in it our loading strategy.

Open the `pages/_app.js` file type/paste 🤫  the code below

```
import '../styles/globals.css'
import Script from 'next/script'

function MyApp({ Component, pageProps }) {
  return(
    <>
      <Script src={`https://www.googletagmanager.com/gtag/js?id=${process.env.NEXT_PUBLIC_MEASUREMENT_ID}`} strategy='afterInteractive' />
      <Script id="google-analytics" strategy='afterInteractive'>
        {`
          window.dataLayer = window.dataLayer || [];
          function gtag(){dataLayer.push(arguments);}
          gtag('js', new Date());
        
          gtag('config', '${process.env.NEXT_PUBLIC_MEASUREMENT_ID}');
        `}
      </Script>
      <Component {...pageProps} />
    </>
  ) 
}

export default MyApp
```
As you can see the __two Script Components handle the script code__ taken from the Google Analytics website, as shown in the image above, inject it globally (since the MyApp component exported from the _app.js wraps the entire app) and load it in base of the  value passed inside the `strategy` prop.

The `strategy` prop accepts three possible loading strategies:

- beforeInteractive
- afterInteractive
- lazyOnload

`afterInteractive`, the loading strategy we've chosen, is the one [suggested by Next.js](https://nextjs.org/docs/messages/next-script-for-ga) to handle the Google Analytics script. What it does is to:

> Load immediately after the page becomes interactive

The `id` prop we pass inside the second Script Component is a __unique arbitrary attribute__ we are obliged to set in order to allow Next.js track and optimize, under the hood, all the scripts it needs to handle and load. Imagine a common scenario in which you have multiple third-party scripts loaded by multiple Script Components (for ex. Ads, Social Media Widget, Cookie Consent Banner etc etc), having these unique ids for each of them will let Next.js *"organize its work"* to handle them in the best and fastest way (just like the unique `key` prop we need to pass for each `<li>` tag inside a list).

__Hei hei hei!!! Where are you going now?!?! We are not done yet!!!__

![not done yet gif](https://media.giphy.com/media/KVDpqMY28c2M40hRIp/giphy.gif)

## Add a custom function to track pageviews

Now that the Google Analytics script is globally injected we need to define a __custom function to track pageviews__.

It’s a common practice to put all the third-party library related code into a __separate folder__. So, always starting at the root level of our project, let's create this folders structure:

![folder structure.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1642375994760/oNwjj8vCu.png)

Inside `index.js` we define and export an anonymous function named `pageview`, that takes an `url` as parameter. Inside this function we call the `gtag` method on the `window` object, and pass to `gtag` three arguments:
- 'config'
- our measurement id via env variable
- an options object in which we assign the value `url` to the key `path_url`

```
export const pageview = (url) => {
    window.gtag('config', process.env.NEXT_PUBLIC_MEASUREMENT_ID, {
        path_url: url,
    })
}
```
## Subscribe and listen to Next.js Router `routeChangeComplete` event

We still have one problem to face to let Google Analytics __know how to track pageviews__ when someone on our website is browsing between __different pages/routes__.

![we can fix this gif](https://media.giphy.com/media/1wnK5Qgwj6OkkckCv5/giphy.gif)

Time to go back to `pages/_app.js` and make the magic happen.

```
import '../styles/globals.css'
import {useEffect} from 'react'
import {useRouter} from 'next/router'
import Script from 'next/script'

import * as ga from '../lib/google-analytics'

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

  useEffect(() => {
    const handleRouteChange = (url) => {
      ga.pageview(url)
    }

    router.events.on('routeChangeComplete', handleRouteChange)
    return () => {
      router.events.off('routeChangeComplete', handleRouteChange)
    }
  }, [router.events])
  return(
    <>
      <Script src={`https://www.googletagmanager.com/gtag/js?id=${process.env.NEXT_PUBLIC_MEASUREMENT_ID}`} strategy='afterInteractive' />
      <Script id="google-analytics" strategy='afterInteractive'>
        {`
          window.dataLayer = window.dataLayer || [];
          function gtag(){dataLayer.push(arguments);}
          gtag('js', new Date());
        
          gtag('config', '${process.env.NEXT_PUBLIC_MEASUREMENT_ID}');
        `}
      </Script>
      <Component {...pageProps} />
    </>
  ) 
}

export default MyApp

```
Thanks to the `useRouter` hook we can use the router object inside the `_app.js` and subscribe to the Next.js Router `routeChangeComplete` event. This event fires when a route change process is completed (as the name suggests 😅). 

We can subscribe to it calling the `on` method on `router.events` and, passing the `pageview` custom function (previously created), we let Google Analytics know how track pageviews on pages/routes changes.

The subscription to this event is done inside a `useEffect` hook that fires at any `router.events` change. As you can see `router.events` is the second argument passed to `useEffect`.

To avoid any memory leak we return from `useEffect` a clean up function in which we unsubscribe from `routeChangeComplete` event calling the `off` method on `router.events`.

The Google Analytics script is now successfully integrated into our Next.js app!! 🥳

### GTmetrix analysis

Time for a little test! 
 
Try to analyze your Next.js app with a free tool like [GTmetrix](https://gtmetrix.com/) to check if what we have done so far works properly.

In the waterfall section of Gtmetrix (selecting the JS tab) you can easily see that the Google Analytics script is loaded only after all the scripts that the web app needs to execute first to be interactive.

![gtmetrix.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1642379642467/D1WE60PyZ.png)

So it is clear how the Google Analytics script is not render-blocking and delaying any page content from loading.

## How to add Google Analytics to Next.js - YouTube Video from my YT Channel

{% youtube DT8vx50Asrk %}

Feel free to check out my Youtube Channel:

- https://www.youtube.com/channel/UCPfnsyDEXALlgqZaQXpLpYg

p.s: I hope this guide could be useful to you. For any suggestion please comment in the section down below.

Thank you for reading. From the bottom of my heart.
theItalianDev