Next.jsでGoogle Analytics(gtag.js)のPageView送信


Next.js を使ったサイトかつ、 import Link from 'next/link' で画面遷移をする際にGoogle AnalyticsのPageViewを送信するシンプルな方法を書いておきます。

react-ga, next-ga を使わずに Hocでシンプルに行うやり方。

実装方法

環境

  • next 9.1.7

Hocを作成

GA_MEASUREMENT_ID は書き換えてください。

このhocを next.js ではどのディレクトリに置けばいいかは
https://react-file-structure.surge.sh/
を参考にしてください。

import React, { Component } from 'react';
import Head from 'next/head';
import Router from 'next/router';

export default ComposedComponent => {
  return class withGA extends Component {
    static displayName = `withGA(${ComposedComponent.displayName})`;

    static async getInitialProps(ctx) {
      const pageProps = ComposedComponent.getInitialProps
        ? await ComposedComponent.getInitialProps(ctx)
        : {};

      return { ...pageProps };
    }

    componentDidMount() {
      Router.events.on('routeChangeComplete', () => {
        setTimeout(() => {
          window.gtag('config', 'GA_MEASUREMENT_ID', {
            page_title: window.document.title,
            page_location: window.location.href,
          });
        }, 0);
      });
    }

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

_app.js にラップ

pages/_app.js に↓にようにラップします

import React from 'react';
import App from 'next/app';

import withGA from 'your_path/withGA';

class CoreApp extends App {
  static async getInitialProps({ Component, ctx }) {
    let pageProps = {};
    if (Component.getInitialProps) {
      pageProps = await Component.getInitialProps(ctx);
    }
    return { pageProps };
  }

  render() {
    const { Component, pageProps } = this.props;
    return <Component {...pageProps} />;
  }
}

export default withGA(CoreApp);  // これをやりたいだけ

備考

componentDidMount で setTimeout入れているのは、 window.document.title がうまく取得できないためです。
いずれ改修されて不要になるかもしれません。
参考: https://github.com/zeit/next.js/issues/6025#issuecomment-467423149

下記のように console に出力しつつ、 A -> B -> C とページ遷移した際の C の送信で意味がわかります。

componentDidMount() {
  // titleが1つ前の画面のtitileになっている(CのときにBのtitle)
  console.log("1", {
    page_title: window.document.title,
    page_location: window.location.href,
  });

  Router.events.on('routeChangeComplete', () => {
    // titleが1つ前の画面のtitileになっている(CのときにBのtitle)
    console.log("2", {
      page_title: window.document.title,
      page_location: window.location.href,
    });

    setTimeout(() => {
      console.log("3", {
        page_title: window.document.title,
        page_location: window.location.href,
      });

      window.gtag('config', 'GA_MEASUREMENT_ID', {
        page_title: window.document.title,
        page_location: window.location.href,
      });
    }, 0);
  });
}

まとめ

「単純にPageViewを送信できればいいんだけどねー」という目的に対して
ググってもピンポイントでやりたいことの情報が見つからないのでまとめてみました。