Giter Site home page Giter Site logo

nextjs-google-analytics's Introduction

Note

You might not need this package. Please check Third Party Libraries first.

Nextjs Google Analytics

npm version codecov CodeFactor

Google Analytics for Next.js

This package optimizes script loading using Next.js Script tag, which means that it will only work on apps using Next.js >= 11.0.0.

Installation

npm install --save nextjs-google-analytics

TL;DR

Add the GoogleAnalytics component with the trackPageViews prop set to true to your custom App file:

// pages/_app.js
import { GoogleAnalytics } from "nextjs-google-analytics";

const App = ({ Component, pageProps }) => {
  return (
    <>
      <GoogleAnalytics trackPageViews />
      <Component {...pageProps} />
    </>
  );
};

export default App;

You can pass your Google Analytics measurement id by setting it on the NEXT_PUBLIC_GA_MEASUREMENT_ID environment variable or using the gaMeasurementId prop on the GoogleAnalytics component. The environment variable will override the prop if both are set.

Usage

Your Google Analytics measurement id is read from NEXT_PUBLIC_GA_MEASUREMENT_ID environment variable, so make sure it is set in your production environment:

If the variable is not set or is empty, nothing will be loaded, making it safe to work in development.

To load it and test it on development, add:

NEXT_PUBLIC_GA_MEASUREMENT_ID="G-XXXXXXXXXX"

to your .env.local file.

As an alternative, you can use the gaMeasurementId param to pass your Google Analytics measurement id.

The NEXT_PUBLIC_GA_MEASUREMENT_ID environment variable will take precedence over the gaMeasurementId param, so if both are set with different values, the environment variable will override the param.

Scripts

Use the GoogleAnalytics component to load the gtag scripts. You can add it to a custom App component and this will take care of including the necessary scripts for every page (or you could add it on a per page basis if you need more control):

// pages/_app.js
import { GoogleAnalytics } from "nextjs-google-analytics";

const App = ({ Component, pageProps }) => {
  return (
    <>
      <GoogleAnalytics />
      <Component {...pageProps} />
    </>
  );
};

export default App;

By default, scripts are loaded using the afterInteractive strategy, which means they are injected on the client-side and will run after hydration.

If you need more control, the component exposes the strategy prop to control how the scripts are loaded:

// pages/_app.js
import { GoogleAnalytics } from "nextjs-google-analytics";

const App = ({ Component, pageProps }) => {
  return (
    <>
      <GoogleAnalytics strategy="lazyOnload" />
      <Component {...pageProps} />
    </>
  );
};

export default App;

also, you can use alternative to default path for googletagmanager script by

<GoogleAnalytics gtagUrl="/gtag.js" />

Page views

To track page views set the trackPageViews prop of the GoogleAnalytics component to true.

// pages/_app.js
import { GoogleAnalytics } from "nextjs-google-analytics";

const App = ({ Component, pageProps }) => {
  return (
    <>
      <GoogleAnalytics trackPageViews />
      <Component {...pageProps} />
    </>
  );
};

export default App;

By default it will be trigger on hash changes if trackPageViews is enabled, but you can ignore hash changes by providing an object to the trackPageViews prop:

// pages/_app.js
import { GoogleAnalytics } from "nextjs-google-analytics";

const App = ({ Component, pageProps }) => {
  return (
    <>
      <GoogleAnalytics trackPageViews={{ ignoreHashChange: true }} />
      <Component {...pageProps} />
    </>
  );
};

export default App;

As an alternative, you can directly call the usePageViews hook inside a custom App component, do not set trackPageViews prop on the GoogleAnalytics component or set it to false (default):

// pages/_app.js
import { GoogleAnalytics, usePageViews } from "nextjs-google-analytics";

const App = ({ Component, pageProps }) => {
  usePageViews(); // IgnoreHashChange defaults to false
  // usePageViews({ ignoreHashChange: true });

  return (
    <>
      <GoogleAnalytics /> {/* or <GoogleAnalytics trackPageViews={false} /> */}
      <Component {...pageProps} />
    </>
  );
};

export default App;

The module also exports a pageView function that you can use if you need more control.

Custom event

You can use the event function to track a custom event:

import { useState } from "react";
import Page from "../components/Page";
import { event } from "nextjs-google-analytics";

export function Contact() {
  const [message, setMessage] = useState("");

  const handleInput = (e) => {
    setMessage(e.target.value);
  };

  const handleSubmit = (e) => {
    e.preventDefault();

    event("submit_form", {
      category: "Contact",
      label: message,
    });

    setState("");
  };

  return (
    <Page>
      <h1>This is the Contact page</h1>
      <form onSubmit={handleSubmit}>
        <label>
          <span>Message:</span>
          <textarea onChange={handleInput} value={message} />
        </label>
        <button type="submit">submit</button>
      </form>
    </Page>
  );
}

For the possible parameters that can be specified in the event, please refer to the event command in the Google tag API reference.

Consent

You can use the consent function to update your users' cookie preferences (GDPR).

 const consentValue: 'denied' | 'granted' = getUserCookiePreferenceFromLocalStorage(); // 'denied' or 'granted'
 
 consent({
    arg: 'update',
    params: {
      ad_storage: consentValue,
      analytics_storage: consentValue,
    },
 });

For the possible values that can be specified in arg and params, please refer to the consent command in the Google tag API reference.

Web Vitals

To send Next.js web vitals to Google Analytics you can use a custom event on the reportWebVitals function inside a custom App component:

// pages/_app.js
import { GoogleAnalytics, event } from "nextjs-google-analytics";

export function reportWebVitals({ id, name, label, value }) {
  event(name, {
    category: label === "web-vital" ? "Web Vitals" : "Next.js custom metric",
    value: Math.round(name === "CLS" ? value * 1000 : value), // values must be integers
    label: id, // id unique to current page load
    nonInteraction: true, // avoids affecting bounce rate.
  });
}

const App = ({ Component, pageProps }) => {
  return (
    <>
      <GoogleAnalytics />
      <Component {...pageProps} />
    </>
  );
};

export default App;

If you are using TypeScript, you can import NextWebVitalsMetric from next/app:

import type { NextWebVitalsMetric } from "next/app";

export function reportWebVitals(metric: NextWebVitalsMetric) {
  // ...
}

Using the gaMeasurementId param

All exported components, hooks, and functions, accept an optional gaMeasurementId param that can be used in case no environment variable is provided:

// pages/_app.js
import { GoogleAnalytics, event } from "nextjs-google-analytics";
import { gaMeasurementId } from "./lib/gtag";

export function reportWebVitals({
  id,
  name,
  label,
  value,
}: NextWebVitalsMetric) {
  event(
    name,
    {
      category: label === "web-vital" ? "Web Vitals" : "Next.js custom metric",
      value: Math.round(name === "CLS" ? value * 1000 : value), // values must be integers
      label: id, // id unique to current page load
      nonInteraction: true, // avoids affecting bounce rate.
    },
    gaMeasurementId
  );
}
const App = ({ Component, pageProps }) => {
  usePageViews({ gaMeasurementId });

  return (
    <>
      <GoogleAnalytics gaMeasurementId={gaMeasurementId} />
      <Component {...pageProps} />
    </>
  );
};

export default App;

Debugging you Google Analytics

  1. Install the Google Analytics Debugger.

  2. Turn it on by clicking its icon to the right of the address bar.

  3. Open the Chrome Javascript console to see the messages.

    On Windows and Linux, press Control-Shift-J.

    On Mac, press Command-Option-J.

  4. Refresh the page you are on.

TypeScript

The module is written in TypeScript and type definitions are included.

Contributing

Contributions, issues and feature requests are welcome!

Show your support

Give a ⭐️ if you like this project!

LICENSE

MIT

nextjs-google-analytics's People

Contributors

bwsix avatar cretezy avatar dependabot[bot] avatar jacksonhardaker avatar jonbellah avatar leunensmichiel avatar mauriciorobayo avatar megafinz avatar pablomayobre avatar snaka avatar venables avatar vladmykol avatar yongzhenlow avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

nextjs-google-analytics's Issues

Change the condition to check google analytics Id

Here in above referenced code
_gaMeasurementId is formed using -
const _gaMeasurementId = process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID ?? gaMeasurementId;

What if a user wants to use multiple google analytics IDs for different purposes in any use case?
Why we didn't go with -
const _gaMeasurementId = gaMeasurementId ?? process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID;

fix(script-element): Script is invalid in Head of NextJS

Hey, nice package u wrote, thanks for the effort. I wanna bring up a topic, not sure if anyone experienced it before, so here it goes, Script element of nextjs/script doesn't work when it's descendant of Head of next/head, I ended up using native script element instead. The reason I brought up this is because, it's recommended to put the google analytics script in head tag, see here. I think the fix is quite easy

Warning: Encountered two children with the same key when enabled alongside Vercel Analytics

I just installed this package in my project, however, if Vercel Analytics is also installed I get the following browser console warning:

Warning: Encountered two children with the same key, ``. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.

If I add a key to <GoogleAnalytics /> the warning goes away. Not sure if it was something you needed to be aware of.

import { GoogleAnalytics, event } from 'nextjs-google-analytics';
import { Analytics } from '@vercel/analytics/react';

function MyApp({ Component, pageProps }: AppProps) {
    return (
        <>
             <GoogleAnalytics
                trackPageViews={{ ignoreHashChange: true }}
               key={0}
             />
             <Component {...pageProps} key={router.asPath} />
             <Analytics />
    );
}

Hard typescript dependency?

When I install and run locally, it appears to be fine. Probably because I have some typescript stuff installed globally.

However, when I try and run my (pure javascript, no TS!) app using google app engine, it explodes with a missing dependency of "tslib":

Error: Cannot find module 'tslib'

I'd prefer to not have to add any typescript peer dependencies when I'm not actually using it. Is there a way of avoiding this with this module? I believe typescript should be an optional extra not a hard requirement.

Cheers
Matt

NEXT_PUBLIC environment variables don't seem to work

I can't get nextjs-google-analytics to work when I place my measurement ID in a NEXT_PUBLIC variable. No events get reported.

It seems to be something with the next public vars. If I make my own NEXT_PUBLIC var, no events are reported either.

      <GoogleAnalytics
        trackPageViews
        gaMeasurementId={process.env.NEXT_PUBLIC_GA_ID}
      />

But, things work great if I pass my Google Analytics ID as string like this,

<GoogleAnalytics trackPageViews gaMeasurementId="MY GA ID HERE" />

I'm using Next 12.2.5 and Nextjs Google Analytics
2.2.2
.

How do you use the Consent API?

Hi there!

I can see as per #367 you have enabled a consent API - there are no docs on your implementation of this.

Is this a function I can call from other code or am I supposed to use cookies? And if it is the latter - I assume that the basic way of implementing would be:

  • If GA cookies do not exist, deny by default (as per GDPR)
  • If user consents (or cookies are already set to allow) set GA cookies to allow
  • nextjs-google-analytics then automatically picks that up and starts sending data back to Google.

I don't mind writing the docs if you can let me know how this works!

How to use with new app router

I can't find documentation on how this can be used with the new app router of next.js 13. Can someone please help me?

I have this behavior while using this code

After more than one month than I use this code in production, I've now noticed that if someone use not to track (tested on Ubuntu 20.0.4 after that I have checked the box 'not to track') the website will fail because every request after the Google analytics request will fail. Anyone know what to do?

How I can use gtag's consent config options? (GDPR compliance)

Hello,
first thanks you for this component!

I am trying to comply the GDPR but with the default option is not possible.

I need to be able to set consent in gtag:

https://developers.google.com/tag-platform/devguides/consent

gtag('consent', 'default', {
  'ad_storage': 'denied',
  'analytics_storage': 'denied'
});

I need to be able to deny the creation of cookies by default in gtag so that only when the user accepts the use of cookies can they be created.

In short, the following happens to me:

https://stackoverflow.com/questions/60173853/how-to-set-the-google-analytics-cookie-only-after-another-consent-cookie-is-set

Thanks!

Support for server components in Next 13

Thanks for the excellent library. I noticed that in Next 13, the library isn't supported with server components. If you want to use the GoogleAnalytics component, you must set that component to "use client". Since in most cases I want <GoogleAnalytics /> to be set at the root in (app/layout.tsx) it means that I need the set the top-level component of my app to be a client component, defeating all the benefits of Next 13's server components.

If I try to use <GoogleAnalytics /> in a server component I get this error:

error - (sc_server)/node_modules/next/dist/shared/lib/router-context.js (8:37) @ eval
error - TypeError: _react.default.createContext is not a function
    at eval (webpack-internal:///(sc_server)/./node_modules/next/dist/shared/lib/router-context.js:8:38)
    at Object.(sc_server)/./node_modules/next/dist/shared/lib/router-context.js (/home/myproject/.next/server/app/page.js:880:1)
    at __webpack_require__ (/home/myproject/.next/server/webpack-runtime.js:33:43)
    at eval (webpack-internal:///(sc_server)/./node_modules/next/dist/client/router.js:24:22)
    at Object.(sc_server)/./node_modules/next/dist/client/router.js (/home/myproject/.next/server/app/page.js:671:1)
    at __webpack_require__ (/home/myproject/.next/server/webpack-runtime.js:33:43)
    at eval (webpack-internal:///(sc_server)/./node_modules/next/router.js:2:18)
    at Object.(sc_server)/./node_modules/next/router.js (/home/myproject/.next/server/app/page.js:1177:1)
    at __webpack_require__ (/home/myproject/.next/server/webpack-runtime.js:33:43)
    at eval (webpack-internal:///(sc_server)/./node_modules/nextjs-google-analytics/dist/hooks/usePageViews.js:7:18) {
  type: 'TypeError',
  page: '/'
}

I suspect it's because of the useEffect call in usePageViews.

The next-auth team was also recently struggling with a similar issue I believe (see comment).

I'm curious of there is any way to make this library work in a server component (ie. not use any useEffect calls)?

Ability to set gaMeasurementId without environment variable

Hey, thanks for the great library it's been helpful.

I'm working on a project where it'd be nice if we could pass in the gaMeasurementId as a prop instead of relying on the environment variable, as we'd like to pull the measurement ID from the CMS at build time. Any interest in this as a feature? If so I'm happy to try and open a PR for it.

Requires "tslib" manual install

Just like in issue #278 I am receiving the Module not found: Can't resolve 'tslib' error until I install tslib.

Luckily it seems that it is only required as a devDependency and the error goes away once installing it there.

Add page specific data layers and variables to documentation

Thank you for building this. It's one of the few packages out there of its kind. Question: How do I add page-specific data layers?

Example A: I have different categories of content. Blogs, Team Members, Portfolio Pieces, etc. How would I let GA4 know which is which?

Example B: I have an online shop where each product gets its own page; how would I be able to let GA4 know which products are added to a cart along with associate prices/information to those products?

Bonus: It would be great if there were some information in the documentation on how to get this up and running on the Google Analytics admin dashboard as well.

Module not found after installation

Using: NextJS v12.2.0 and nextjs-google-analytics v1.2.1.

I get an error right after installing this package. It's installed and I can see the folder in the node_modules/ however it says Cannot find module nextjs-google-analytics when I try to import it in _app or any other file.

I'm still kinda new to Next and Typescript so maybe I'm doing something wrong but still I couldn't solve this one :/

Using an inline script makes CSP unsafe

Due to the inline script used to initialize window.dataLayer, CSP requires script-src 'unsafe-inline' which makes the page less safe (for example).

Since this is meant for Next/React, there is no reason to actually use the inline script - for now I stopped using this library for this reason and changed to the following code which uses a React effect instead of an inline script.

It would be nice if the library could do something similar to avoid the safety issue.

import { useEffect } from 'react';
import { useRouter } from 'next/router';
import Script from 'next/script';

export function GoogleAnalytics(): JSX.Element {
  const router = useRouter();

  useEffect(() => {
    const gtagWindow = window as (Window & typeof globalThis & { dataLayer: unknown[] });

    gtagWindow.dataLayer = gtagWindow.dataLayer || [];

    const gtag: Gtag.Gtag = (...args: unknown[]): void => {
      gtagWindow.dataLayer.push(args);
    };

    gtag('js', new Date());
    gtag('config', process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID as string);
  }, [router.pathname]);

  return (
    <Script src={`https://www.googletagmanager.com/gtag/js?id=${process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID}`} strategy="afterInteractive" />
  );
}

Excellent library!

This is not a real issue, you've just done a very nice job with this library. Thanks!

Support 'wait-for-update' when applying default consent

It's currently possible to specify a default consent which is useful in circumstances when users are required to explicitly opt-in before sending user data to GA.

<GoogleAnalytics defaultConsent='denied' />

When a user has previously given consent it's important to allow for consent preferences to be applied before tags are fired, otherwise the default consent will be used . Google gtag consent API provides a wait-for-update argument that allows time for cookie consent libraries and platforms to apply previous consent preferences before tags are fired via a call to consent.

It is not currently possible to take advantage of this feature and as a result is likely to result in initial page load analytics from not being collected.

Allowing users to specify a value via GoogleAnalyticsProps seems like a logical option.

Setting user ID?

I'd like to set the user ID from NextAuth, if possible. Is this something I am able to do with this package?

Thank you!

Doesn't show what page they're on

Set this up with page view tracking, it shows users in realtime but doesnt show the page they're on.

<GoogleAnalytics trackPageViews />

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.