この記事は朝日新聞社 Advent Calendar 11日目の記事です。
Next.jsでGTM連携する方法と注意点
Next.jsで作成されたサイトにGoogle Tag Manager(以後 GTMと呼びます)を導入してサイトのユーザー行動計測を行ったのですが、導入する際に公式ドキュメントに詳細な説明がなく、next/linkによるページ遷移が原因で導入に手こずってしまったので、実践した導入方法と注意点をご紹介します。
この記事では、GA4(Google Analytics4)とGTMの管理画面操作方法や設定については、Googleのマニュアルが充実しているので触れません🙏
動作環境
動作環境は以下です。
next 14.0.3
Pages Router
導入方法
基本的なPV(ページビュー)情報を計測するために必要な対応を紹介します。
サイトのPV計測など全てのページ共通で計測するために、_app.tsxに実装します。
(詳細な解説は後述)
import { useEffect } from 'react'
import Script from 'next/script';
// 環境変数として定義を推奨(サンプルコードなので記載)
const GTM_ID = 'GTM-XXX'
function MyApp({ Component, pageProps, router }) {
useEffect(() => {
const sendPageView = () => {
if (window.dataLayer) {
window.dataLayer.push({ event: 'pageview' });
} else {
console.warn(`GTM datalayer does not exist`);
}
};
router.events.on('routeChangeComplete', sendPageView);
return () => {
router.events.off('routeChangeComplete', sendPageView);
};
}, [router.events]);
return (
<>
{GTM_ID && (
<Script
id={GTM_ID}
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','${GTM_ID}');
`,
}}
/>
)}
<Component {...pageProps} />
</>
);
}
export default MyApp;
GTMが推奨しているのでスクリプト実行を無効にされた場合の対策としてnoscriptタグの内容を_document.tsxの、bodyタグの直下に記載します。
<body>
<noscript>
<iframe
src={`https://www.googletagmanager.com/ns.html?id=${GTM_ID}`}
height="0"
width="0"
style={{ display: 'none', visibility: 'hidden' }}
/>
</noscript>
...
</body>
解説
ここからは、はまった注意点や実装をしていて得た知見を記載します。
GTMでは、ページの変更をトリガーで検知してGAにデータを送信します。
このトリガーは、「ページビュートリガー」や「履歴の変更トリガー」などGTMで計測されるタイミングが決められているものと「カスタムイベントトリガー」といって開発者が明示的に起動するトリガーの2種類があります。
従来のMPA(Multi Page Application)サイトの場合は「ページビュートリガー」を設定しておけばGTMがよしなに計測をしてくれていたのですがNext.jsアプリでLinkコンポーネントやnext/routerを使ってページ遷移を実現している場合はページ遷移ごとにloadイベントが発火せず計測がうまくいきません。
そこで、解決方法として2つの方法を試しました。
1つ目の方法は、Next.jsアプリで_app.tsxに初期化処理のみ(サンプルコードのuseEffect部分は削除)を記載して「ページビュートリガー」と「履歴の変更トリガー」を組み合わせて計測する方法です。
この方法の利点としてはアプリケーションのコードに記載する内容は最低限でGTM管理画面上の設定で対応できることです。
トリガーとして履歴の変更を使う理由としてはNext.jsがページ遷移する際にHistory APIを利用しているためです。
しかし、この方法を検証していく際に、「ページビュートリガー」と「履歴変更トリガー」で二重計測をしないように除外設定を入れないといけない部分が多かったり、リロードやブラウザバックの動作が安定しなかったため採用しませんでした。
(SPAサイトが増えているので今後、GTMが対応してくれていくかも...)
2つ目の方法は、「ページビュートリガー」とrouter.eventsでページ遷移を検知して発火した「カスタムイベントトリガー」を組み合わせて計測する方法です。
この方法の利点は実行タイミングを開発者がコントロールできるところとイベントだけではなくカスタムバリューなど付加情報も簡単に付与できる点です。
useEffect(() => {
const sendPageView = () => {
if (window.dataLayer) {
window.dataLayer.push({ event: 'pageview' });
} else {
console.warn(`GTM datalayer does not exist`);
}
};
router.events.on('routeChangeComplete', sendPageView);
return () => {
router.events.off('routeChangeComplete', sendPageView);
};
}, [router.events]);
routerオブジェクトの説明にあるようにrouteChangeComplete - Fires when a route changed completely
なタイミングでpageview
というカスタムイベントを送信しています。
もしも、追加でパスを送りたい場合は以下のようにしてみるのも良いです。
const sendPageView = (url) => {
if (window.dataLayer) {
window.dataLayer.push({ event: 'pageview', page_path: url });
} else {
console.warn(`GTM datalayer does not exist`);
}
};
最後に
Experimentalですがvercel/next.jsがThird Party LibrariesとしてGoogleTagManagerコンポーネントを提供しています。Next.jsがサポートしてくれたらとても心強いので今後が楽しみです!