Next.js で GTM + GA4を利用する
概要
Next.jsにGoogle Tag Manager(以降はGTMと記載します)、Google Analytics 4(以降はGA4記載します)を連携させる手順をまとめた記事です。
対象読者
以下の条件を満たす人を対象読者とします。
- Next.js(TypeScript)で開発をしている
- GA4でアクセス解析をやりたい
前提条件
- Next.jsで運用済のアプリケーションが存在する
- GA4のプロパティを作成してある
具体的な手順
ここからは具体的な手順になります。
私が友人と開発している、猫のLGTM画像を共有できるサービス「LGTMeow」に設定した例を元に解説します。
GTMの設定
GTMのアカウントを作成
以下の情報を入力します。
- アカウント名
- コンテナ名
- ターゲット プラットフォーム
アカウント名はわかりやすい名前であれば問題ありません。
コンテナ名は対象サイトのドメイン名を入れるのが良さそうです。
URLが https://lgtmeow.com
であれば lgtmeow.com
となります。
GA4 連携用のタグを追加
最初にGA4の測定ID(G-から始まる12桁の値)を控えておきます。
タグ → 新規からGoogleタグを作成します。
タグの名前はわかりやすい名前であれば何でも大丈夫です、ここではGAとしました。
さきほど控えておいた測定ID(G-から始まる12桁の値)を入力します。
配信トリガーはAll Pages(ページビュー)を設定します。
ここまで設定できたら、ワークスペースの右上の「公開」を押下して、このバージョンを公開します。
Next.jsのアプリケーションにタグを追加する
以下のようなComponentを定義します。
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;
.env
に NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID
を追加します。
設定するのは GTM-
から始まるGTMのコンテナIDです。
NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID=GTM-XXXXXXX
window.dataLayer
を呼び出せるように型定義を追加します。
export const googleTagManagerId =
process.env.NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID || '';
declare global {
interface Window {
dataLayer: Record<string, unknown>[];
}
}
_app.tsx
でさきほど作成したGTM用のComponentを呼び出します。
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>
)
}
この状態でアプリケーションにアクセスすると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>
タグを追加することで対応できます。
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上でデータレイヤーの変数を追加
変数 → 新規 → データレイヤーの変数を追加します。
名前は fetch_random_images_trigger
とします。
トリガーの追加
トリガー → 新規 → カスタムイベントを選択します。
- イベント名は「fetch_random_images」とします
- このトリガーの発生場所は「一部のカスタムイベント」とします
- イベント発生時の条件は
Event 等しい fetch_random_images
とします
GA4用のイベントタグの追加
タグ → 新規 → Googleアナリティクス: GA4イベント → 設定タグに既存のGAを選択 → イベント名にさきほど作成したイベントを選択します。
トリガーにはさきほど設定したトリガーを選択します。
ここまで設定できたら、ワークスペースの右上の「公開」を押下して、このバージョンを公開します。
ボタンクリック時にイベントを送信する
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の管理画面でイベントが送信されているかテスト
「他の猫ちゃんを表示」ボタンをクリックしてリアルタイムでイベントが送信されているか確認します。
今回は例として単純なカスタムイベントを定義しました。
本来この程度であればわざわざカスタムイベントを設定しなくても、イベントの計測を行なうことが可能です。
ソースコードを変更しなくてもさまざまな計測ができるのがGTMを利用するメリットなので、 カスタムイベントを使うかどうかは慎重に考える必要があります。
おわりに
以上がNext.jsでGTM、GA4を使う方法になります。
この記事を書くために以下の記事を参考にさせていただきました。
- Next.js に Google Tag Manager + Google Analytics 4 を導入するための整理
- Next.js&GoogleAnalytics設定
- React Native for Web + TypeScriptでGoogleTagManagerを使ってみた
以上になります。最後まで読んでいただきありがとうございました。
Discussion
@next/third-parties/google
についての補足 2023-12-20@next/third-parties/google
ですが現在TypeScriptで利用するとVercelでのBuildに失敗する問題があります。詳しくは以下のissueを参照してください。
どうも型定義のエクスポート方法に問題があるようです。
https://github.com/vercel/next.js/issues/58472#issuecomment-1812294404 に書かれている方法で回避可能ですが自分の環境だとJestでのテストが動作しなくなってしまったので、一旦
@next/third-parties/google
の採用を見送っています。詳しくは以下のPRを参照して頂ければと思います。