Next.jsでGoogle Analyticsを使えるようにする
Next.jsでGoogle Analyticsを使えるようにする
**Next.jsとはVercelが作成しているReactのフレームワークです。**面倒な設定を書かなくてもすぐに使えるZero Configを標榜しており、実際にwebpackやTypeScriptと一緒にReactを書く際にも特別な準備は不要です。SSRにも対応しており、Reactで開発するならNext.jsかFacebook製のCreate React Appを使うのがスタンダードになってます。
私は実務でNext.jsを使っており、このフレームワークはとても便利だと思っています。私はNext.jsの大ファンなので、Reactでの開発時にNext.jsを使う現場が増えるといいなと思って記事を書いています。
関連記事: Next.js + esa.io + VercelでJAMStackな爆速ブログを構築する
**この記事では、Next.jsでReactアプリケーションを作成する時に、Google Analytics(以下、GA)の設定をする方法をご紹介します。**一通り設定した後、TypeScript化していきます。
基本的にはNext.jsのExampleを参考にしています。ただ、実務で使うとこれだけでは足りないところがあるので、記事内では実務への橋渡しとなるような内容を盛り込んでいます。
なお、Google Analyticsのアカウント取得方法やスニペットの取得方法は記載していません。
Google AnalyticsのIDを.envに記述する
まず、ルートディレクトリに.env
ファイルを作成し、GAのIDを.env
に記述します。
NEXT_PUBLIC_GOOGLE_ANALYTICS_ID=UA-SOME_ANALYTICS_ID-1
Next.jsでは、.env
に記述した環境変数をprocess.env.NEXT_PUBLIC_GOOGLE_ANALYTICS_ID
で取得できます。この値はビルド時に注入されます。これが最初の設定です。
NEXT_PUBLIC_
という接頭辞をつけると、ブラウザにも露出する値になります。
GAイベントを発火させる関数を作成する
次に、GAイベントを発火させる関数を作成します。関数はsrc/lib/gtag.js
というファイルに記述していきます。
export const GA_ID = process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS_ID || ''
// IDが取得できない場合を想定する
export const existsGaId = GA_ID !== ''
// PVを測定する
export const pageview = (path) => {
window.gtag('config', GA_ID, {
page_path: path,
})
}
// GAイベントを発火させる
export const event = ({action, category, label, value = ''}) => {
if (!existsGaId) {
return
}
window.gtag('event', action, {
event_category: category,
event_label: JSON.stringify(label),
value,
})
}
このファイルではgtag
というGAのメソッドをラップする関数を作ります。
pageview
という関数でページビューを送信するには、config
コマンドを使用します。引数でパスを受け取り、page_path
でURLのパスを送信します。
event
という関数でGAイベントを送信する関数を記述します。例えば、DOMのクリックイベントやSubmitのイベントなどを取得する時に使います。
なお、GAに関する詳しい内容はGoogleの公式ドキュメントをご覧ください。
- ページビューの測定
- Google アナリティクスのイベントを測定する
- イベントについて(イベント名の区別に対する記述がある)
_app.jsにGAのスクリプトを書き込む
_app.js
は全てのページで共通のHTMLを書くコンポーネントです。この_app.js
にGA用のscriptタグを記述します。
import Head from "next/head";
import React from 'react'
import { GA_ID, existsGaId } from '../src/lib/gtag'
const App = ({ Component, pageProps }) => {
return (
<>
<Head>
{/* Google Analytics */}
{existsGaId && (
<>
<script async src={`https://www.googletagmanager.com/gtag/js?id=${GA_ID}`} />
<script
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${GA_ID}', {
page_path: window.location.pathname,
});`,
}}
/>
</>
)}
</Head>
<Component {...pageProps} />
</>
)
}
export default App
これで Google Analytics の JS を読み込むことができました。
Next.js v11 の Script コンポーネントを利用してリファクタする
Next.js の v11 から、Script コンポーネントが導入されました。 このコンポーネントを用いると、外部の JS を読み込むタイミングを簡単に制御できます。
ここでは、Script タグを使って Google Analytics の JS をサイトの実行に必要なスクリプトを読み込んでページがインタラクティブになった後に読み込むことで、ページが操作可能になるまでの時間を短縮する書き方を紹介します。
その恩恵を端的に書くと、Lighthouse でのスコアが上昇し、SEO に好影響を与えます。
import Script from 'next/script'
import { existsGaId, GA_ID } from '../lib/gtag'
const GoogleAnalytics = () => (
<>
{existsGaId && (
<>
<Script defer src={`https://www.googletagmanager.com/gtag/js?id=${GA_ID}`} strategy="afterInteractive" />
<Script id="ga" defer strategy="afterInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${GA_ID}');
`}
</Script>
</>
)}
</>
)
export default GoogleAnalytics
GoogleAnalytics
コンポーネントはこれだけです。先程_app.js
に書いたものを切り出しただけです。
あとは_app.js
でこのコンポーネントを呼び出すだけです。
import React from 'react'
import GoogleAnalytics from '../src/components/GoogleAnalytics'
const App = ({ Component, pageProps }) => {
return (
<>
<GoogleAnalytics />
<Component {...pageProps} />
</>
)
}
export default App
これで Google Analytics の読み込みタイミングを遅らせることができました。
_app.jsにPVをカウントするイベントを記述する
Next.js製のサイトはSPAであるため、ページを遷移するときにJavaScriptでURLを書き換えます。その際、Google Analyticsはアクセスした最初のページしかページビュー測定のイベントを送信しません。つまり、ユーザーがサイト内を回遊したときの各ページのPVを測定できないのです。
この問題は、Next.jsのRouterを使えば解決できます。RouterのURL書き換えが完了した時に発火するrouteChangeComplete
イベントのコールバックとしてpageview関数を設定します。
これをpages/_app.js
に記述します。
import { useRouter } from "next/router";
import React, { useEffect } from 'react'
import { existsGaId, pageview } from '../src/lib/gtag'
import GoogleAnalytics from '../src/components/GoogleAnalytics'
const App = ({ Component, pageProps }) => {
const router = useRouter()
useEffect(() => {
if (!existsGaId) {
return
}
const handleRouteChange = (path) => {
pageview(path)
}
router.events.on('routeChangeComplete', handleRouteChange)
return () => {
router.events.off('routeChangeComplete', handleRouteChange)
}
}, [router.events])
return (
<>
<GoogleAnalytics />
<Component {...pageProps} />
</>
)
}
export default App
これで、ページ遷移時のPVイベントをGAに送信できました。
_app.js
の記述は長くなりがちなので、Custom Hooks に切り出しておくと便利です。
import { useEffect } from 'react'
import { useRouter } from "next/router";
import { existsGaId, pageview } from '../lib/gtag'
export default function usePageView() {
const router = useRouter()
useEffect(() => {
if (!existsGaId) {
return
}
const handleRouteChange = (path) => {
pageview(path)
}
router.events.on('routeChangeComplete', handleRouteChange)
return () => {
router.events.off('routeChangeComplete', handleRouteChange)
}
}, [router.events])
}
_app.js
は以下のようにリファクタリングできます。
import React from 'react'
import usePageView from '../src/hooks/usePageView'
import GoogleAnalytics from '../src/components/GoogleAnalytics'
const App = ({ Component, pageProps }) => {
usePageView() // 追加
return (
<>
<GoogleAnalytics />
<Component {...pageProps} />
</>
)
}
export default App
GAイベントをReactコンポーネントに設定する
**では、実際にReactコンポーネントでGAイベントの設定をしましょう。**以下ではContactコンポーネントでのボタンをクリックした時に、inputタグに入力されたメッセージをGAに送信します。
import React from 'react'
import Layout from './Layout'
import * as gtag from '../lib/gtag'
const Contact = () => {
const [message, setMessage] = React.useState('')
const handleInput = e => setMessage(e.target.value)
const handleSubmit = e => {
e.preventDefault()
gtag.event({
action: 'submit_form',
category: 'Contact',
label: message,
})
setMessage('')
}
return (
<Layout>
<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>
</Layout>
)
}
export default Contact
今回はhandleSubmitの中でeventを発火しています。同様にclickイベントならhandleClick
の中に、onChangeイベントならhandleChangeの中に
event`関数を記述します。Reactでは取得したいイベントに応じて、GAイベントを柔軟に記述できます。
ここまででNext.jsでGoogle Analyticsを使うための設定ができました。
より詳しく知りたい方は ここまでのコードを反映したサンプルレポジトリ をご覧ください。
以下では、より実務に即した内容をご紹介します。
TypeScript対応をする
実務ではReactとTypeScriptの環境で開発している方も多いと思います。そのような方のために、Next.js + Google Analytics + TypeScriptの対応方法をご紹介します。
windowからGAイベントのプロパティを使うために型ファイルをインストールする
Next.jsでTypeScriptを使えるようにすると、ルートディレクトリにnext-env.d.ts
というファイルが作成されます。これはNext.jsでTSを使うなら必須のファイルで、削除してはいけません。
前の章でlib/gtag.js
にwindow.gtag()
をラップする関数を作りましたね。
gtag.js
をgtag.ts
に書き換えると、windowオブジェクトにgtagというプロパティは存在しないという意味のエラーが表示されます。
TS2339: Property 'gtag' does not exist on type 'Window'.
この型エラーを消すために、Google Analytics 用の型ファイルをインストールしましょう。
$ npm i -D @types/gtag.js
これでgtag.ts
でエラーが出なくなりました。
発火させるイベントを型で管理する
TypeScriptに対応したため、gtagをラップする関数に型をつけていきます。
まず、gtag.js
の拡張子をgtag.ts
に変更します。そして、Google Analytics のイベントの型を作成します。
type ContactEvent = {
action: 'submit_form'
category: 'contact'
label: string
}
type ClickEvent = {
action: 'click'
category: 'other'
label: string
}
export type Event = ContactEvent | ClickEvent
型の作成は必須ではありません。
ただ、型で管理するとイベント設定時のスペルミスや値が undefined になるミスを未然に防ぐことができる上に、後からの仕様変更に強くなるためこのように書くことをお勧めしています。
こうすることでEvent.jsがサイト全体のGAイベントのドキュメント代わりになります。
数が増えると管理が大変なので、yml などでイベントの定義を PM に書いてもらい、TypeScript の型を自動生成するのがベストだとは思います。
次にwindw.gtag
をラップする event 関数と、pageview
関数の引数に型をつけます。
// ...
export type Event = ContactEvent | ClickEvent
export const event = ({action, category, label}: Event) => {
if (!existsGaId) {
return
}
window.gtag('event', action, {
event_category: category,
event_label: JSON.stringify(label)
})
}
export const pageview = (path: string) => {
window.gtag('config', GA_ID, {
page_path: path,
})
}
これで型をつけることができました。
モジュールごとに分割せず一ファイルにまとめる
これまで、gtag や hooks、GoogleAnalytics コンポーネントなど役割に応じてファイルを分割してきました。
しかし、全て Google Analytics に関するモジュールであるため、gtag.tsx
の1ファイルにまとめてしまう方が管理が簡単かもしれません。
以下では、これまで書いてきたモジュールを1ファイル内にまとめておくコードを記載します。また、Event 型を実践用に少し書き換えています。
import { useRouter } from 'next/router'
import Script from 'next/script'
import { useEffect } from 'react'
export const GA_ID = process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS_ID || ''
// IDが取得できない場合を想定する
export const existsGaId = GA_ID !== ''
// PVを測定する
export const pageview = (path: string) => {
window.gtag('config', GA_ID, {
page_path: path,
})
}
// GAイベントを発火させる
export const event = ({ action, category, label, value = '' }: Event) => {
if (!existsGaId) {
return
}
window.gtag('event', action, {
event_category: category,
event_label: label ? JSON.stringify(label) : '',
value,
})
}
// _app.tsx で読み込む
export const usePageView = () => {
const router = useRouter()
useEffect(() => {
if (!existsGaId) {
return
}
const handleRouteChange = (path: string) => {
pageview(path)
}
router.events.on('routeChangeComplete', handleRouteChange)
return () => {
router.events.off('routeChangeComplete', handleRouteChange)
}
}, [router.events])
}
// _app.tsx で読み込む
export const GoogleAnalytics = () => (
<>
{existsGaId && (
<>
<Script defer src={`https://www.googletagmanager.com/gtag/js?id=${GA_ID}`} strategy="afterInteractive" />
<Script
defer
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${GA_ID}');
`,
}}
strategy="afterInteractive"
/>
</>
)}
</>
)
// イベントを型で管理
type ContactEvent = {
action: 'submit_form'
category: 'contact'
}
type ClickEvent = {
action: 'click'
category: 'other'
}
export type Event = (ContactEvent | ClickEvent) & {
label?: Record<string, string | number | boolean>
value?: string
}
よければ参考にしてみてください。
まとめ
Next.jsでGoogle Analyticsを使えるようにした上で、TypeScriptに対応しました。
Next.jsを本番環境で使用する場合、Google Analyticsは必須です。Google Analyticsを使ってユーザー行動を取得し、プロダクトの改善にぜひ役立ててください。
日本でNext.jsが現場で使われるケースが増えることを願っています。
Happy Coding 🎉