🐱

Next.js で GTM + GA4を利用する

2021/11/15に公開
1

概要

Next.jsにGoogle Tag Manager(以降はGTMと記載します)、Google Analytics 4(以降はGA4記載します)を連携させる手順をまとめた記事です。

対象読者

以下の条件を満たす人を対象読者とします。

  • Next.js(TypeScript)で開発をしている
  • GA4でアクセス解析をやりたい

前提条件

  • Next.jsで運用済のアプリケーションが存在する
  • GA4のプロパティを作成してある

具体的な手順

ここからは具体的な手順になります。

私が友人と開発している、猫のLGTM画像を共有できるサービス「LGTMeow」に設定した例を元に解説します。

https://lgtmeow.com/

GTMの設定

GTMのアカウントを作成

以下の情報を入力します。

  • アカウント名
  • コンテナ名
  • ターゲット プラットフォーム

アカウント名はわかりやすい名前であれば問題ありません。

コンテナ名は対象サイトのドメイン名を入れるのが良さそうです。

URLが https://lgtmeow.com であれば lgtmeow.com となります。

create-tag-manager-account

GA4 連携用のタグを追加

最初にGA4の測定ID(G-から始まる12桁の値)を控えておきます。

タグ → 新規からGoogleタグを作成します。

create-ga4-tag1

タグの名前はわかりやすい名前であれば何でも大丈夫です、ここではGAとしました。

さきほど控えておいた測定ID(G-から始まる12桁の値)を入力します。

create-ga4-tag2

配信トリガーはAll Pages(ページビュー)を設定します。

create-ga4-tag3

ここまで設定できたら、ワークスペースの右上の「公開」を押下して、このバージョンを公開します。

workspace_publish

Next.jsのアプリケーションにタグを追加する

以下のようなComponentを定義します。

src/components/GoogleTagManager.tsx
import Script from 'next/script';
import React from 'react';

export type GoogleTagManagerId = `GTM-${string}`;

type Props = {
  googleTagManagerId: GoogleTagManagerId;
};

const GoogleTagManager: React.FC<Props> = ({ googleTagManagerId }) => (
  <Script
    id="gtm"
    strategy="afterInteractive"
    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','${googleTagManagerId}');
      `,
    }}
  />
);

export default GoogleTagManager;

.envNEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID を追加します。

設定するのは GTM- から始まるGTMのコンテナIDです。

NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID=GTM-XXXXXXX

window.dataLayer を呼び出せるように型定義を追加します。

src/utils/gtm.ts
export const googleTagManagerId =
  process.env.NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID || '';

declare global {
  interface Window {
    dataLayer: Record<string, unknown>[];
  }
}

_app.tsx でさきほど作成したGTM用のComponentを呼び出します。

src/pages/_app.tsx
import { AppProps } from 'next/app';
import { googleTagManagerId } from '../utils/gtm';
import GoogleTagManager, {
  GoogleTagManagerId,
} from '../components/GoogleTagManager';

const App = ({ Component, pageProps }: AppProps): JSX.Element => (
  <GoogleTagManager
    googleTagManagerId={googleTagManagerId as GoogleTagManagerId}
  />
  {/* eslint-disable-next-line react/jsx-props-no-spreading */}
  <Component {...pageProps} />
);

export default App;

ちなみにNext.js 13以上でApp Routerを使っている場合 @next/third-parties/google を利用するのが簡単です。

src/app/layout.tsx 内に定義されている RootLayout に設定します。

import { GoogleTagManager } from '@next/third-parties/google'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
      <GoogleTagManager gtmId="GTM-XYZ" />
    </html>
  )
}

https://nextjs.org/docs/app/building-your-application/optimizing/third-party-libraries

この状態でアプリケーションにアクセスするとGA4にイベントが送信できていることを確認できるかと思います。

noscriptタグについて

公式だと以下のようなタグをbodyの一番上に追加するように記載されています。

<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXXX"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

Next.jsの場合 _document.tsx に以下のように <noscript> タグを追加することで対応できます。

src/pages/_document.tsx
import Document, {
  DocumentContext,
  Head,
  Html,
  Main,
  NextScript,
  DocumentInitialProps,
} from 'next/document';
import React from 'react';
import {googleTagManagerId} from '../infrastructures/utils/gtm';

export default class CustomDocument extends Document {
  static async getInitialProps(
    ctx: DocumentContext,
  ): Promise<DocumentInitialProps> {
    const initialProps = await Document.getInitialProps(ctx);

    return {
      ...initialProps,
    };
  }

  render(): JSX.Element {
    return (
      <Html prefix="og: https://ogp.me/ns#">
        <Head />
        <body>
          <noscript
            dangerouslySetInnerHTML={{
              __html: `
              <iframe
                src="https://www.googletagmanager.com/ns.html?id=${googleTagManagerId}"
                height="0"
                width="0"
                style="display:none;visibility:hidden"
              />`,
            }}
          />
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

しかしそもそもNext.jsでアプリケーションを開発している場合、クライアントサイドのJavaScriptが無効な場合まともに動作させる事は難しいです。

要件にもよりますが <noscript> は省略しても良いかもしれません。

ちなみにNext.js 13以上でApp Routerを使っている場合 @next/third-parties/google を利用すれば <noscript> も追加されます。

カスタムイベントの送信

ここまでで基本的な設定は完了ですが、最後にGA4にカスタムイベントを送信する方法を紹介します。

LGTMeow の「他の猫ちゃんを表示」ボタンのクリックをカスタムイベントとして登録してみます。

GTM上でデータレイヤーの変数を追加

変数 → 新規 → データレイヤーの変数を追加します。

create_var1

create_var2

名前は fetch_random_images_trigger とします。

トリガーの追加

トリガー → 新規 → カスタムイベントを選択します。

create_trigger1

  • イベント名は「fetch_random_images」とします
  • このトリガーの発生場所は「一部のカスタムイベント」とします
  • イベント発生時の条件は Event 等しい fetch_random_images とします

create_trigger2

GA4用のイベントタグの追加

タグ → 新規 → Googleアナリティクス: GA4イベント → 設定タグに既存のGAを選択 → イベント名にさきほど作成したイベントを選択します。

トリガーにはさきほど設定したトリガーを選択します。

add_tag

ここまで設定できたら、ワークスペースの右上の「公開」を押下して、このバージョンを公開します。

workspace_publish

ボタンクリック時にイベントを送信する

Next.js側の対応です。

さきほど作成した src/utils/gtm.ts に以下の関数を追加します。

// ランダムでLGTM画像を表示させる機能が利用された際に実行する
type SendFetchRandomImagesLabel = 'fetch_random_images_button';

export const sendFetchRandomImages = (
  label: SendFetchRandomImagesLabel,
): void => {
  window.dataLayer.push({
    event: 'fetch_random_images',
    fetch_random_images_trigger: label,
  });
};

「他の猫ちゃんを表示」ボタンのクリック時に sendFetchRandomImages を呼び出すようにします。

  const handleRandom = async () => {
    try {
      const imageList = await fetchRandomImageList();
      setAppState({ imageList: imageList.images, isFailedFetchImages: false });

      sendFetchRandomImages('fetch_random_images_button');
    } catch (e) {
      setAppState({ imageList: [], isFailedFetchImages: true });
    }
  };

GA4の管理画面でイベントが送信されているかテスト

「他の猫ちゃんを表示」ボタンをクリックしてリアルタイムでイベントが送信されているか確認します。

realtime

今回は例として単純なカスタムイベントを定義しました。

本来この程度であればわざわざカスタムイベントを設定しなくても、イベントの計測を行なうことが可能です。

ソースコードを変更しなくてもさまざまな計測ができるのがGTMを利用するメリットなので、 カスタムイベントを使うかどうかは慎重に考える必要があります。

おわりに

以上がNext.jsでGTM、GA4を使う方法になります。

この記事を書くために以下の記事を参考にさせていただきました。

以上になります。最後まで読んでいただきありがとうございました。

Discussion

keitaknkeitakn

@next/third-parties/google についての補足 2023-12-20

@next/third-parties/google ですが現在TypeScriptで利用するとVercelでのBuildに失敗する問題があります。

詳しくは以下のissueを参照してください。

https://github.com/vercel/next.js/issues/58472

どうも型定義のエクスポート方法に問題があるようです。

https://github.com/vercel/next.js/issues/58472#issuecomment-1812294404 に書かれている方法で回避可能ですが自分の環境だとJestでのテストが動作しなくなってしまったので、一旦 @next/third-parties/google の採用を見送っています。詳しくは以下のPRを参照して頂ければと思います。

https://github.com/nekochans/ai-cat-frontend/pull/74