Next.jsアプリにGoogle Analytics 4のトラッキングを設定する(TypeScript版)

GA4への切り替えのアナウンスやブログ記事が増えてきました。 npmを探せばよしなにしてくれるライブラリもありそうで、実運用ではこちらを使う方が効率が良いかもしれません。 今回はGA4でのトラッキング方法の勉強もかねて、 […]

広告ここから
広告ここまで

目次

    GA4への切り替えのアナウンスやブログ記事が増えてきました。

    npmを探せばよしなにしてくれるライブラリもありそうで、実運用ではこちらを使う方が効率が良いかもしれません。

    今回はGA4でのトラッキング方法の勉強もかねて、あえて手組してみたので、その覚書です。

    型情報のインストール

    TypeScriptで実装する場合、gtag()の型情報を手に入れておくと便利です。

    npm i -D @types/gtag.js

    ファイルを作成してグローバル変数で使えるようにします。(これはもしかするといらないかも)

    types/gtag.d.ts 

    /// <reference types="gtag.js" />
    
    declare module 'gtag.js';
    

    IDEがgtagで補完するようになれば、OKです。

    gtag.jsの読み込み

    続いてGA4のトラッキングに使うJSファイルを読み込みましょう。

    pages/_app.tsxに定義します。

    import type { AppProps } from 'next/app'
    import Script from 'next/script'
    
    const GA_TRACKING_ID = process.env.NEXT_PUBLIC_GA_TRACKING_ID as sting // 'G-XXXXX'
    
    function MyApp({ Component, pageProps }: AppProps) {
      return (
        <>
          <Script
            async
            strategy='lazyOnload'
            src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`}
          />
          <Script strategy='afterInteractive'>
            {`
              window.dataLayer = window.dataLayer || [];
              function gtag(){dataLayer.push(arguments);}
              gtag('js', new Date());
              gtag('config', '${GA_TRACKING_ID}', {
                page_path: window.location.pathname,
              });
            `}
          </Script>
          <Component {...pageProps} />
        </>
      )
    }
    
    export default MyApp

    next/scriptを利用する形に変わっていますが、よくよく読むとAnalyticsの設定画面で表示されるコードとほぼ同じです。

    <script async src="https://www.googletagmanager.com/gtag/js?id=xxxx"></script>
    <script>
      window.dataLayer = window.dataLayer || [];
      function gtag(){dataLayer.push(arguments);}
      gtag('js', new Date());
    
      gtag('config', 'G-xxxx');
    </script>

    ページの遷移時にイベントを送信する

    Analyticsの設定画面で表示されるコードのうち、ページビューに相当するイベントを送信している処理はgtag('config', 'G-xxxx');です。

    この関数を、Next.jsのルーティング機能で遷移した場合にも発火するようにpages/_apps.tsx に実装しましょう。

    
      const router = useRouter()
      useEffect(() => {
        const handleRouteChange = (url: string) => {
          if (!window) return
          window.gtag('config', GA_TRACKING_ID, {
            page_location: url,
          })
        }
        router.events.on('routeChangeComplete', handleRouteChange)
        router.events.on('hashChangeComplete', handleRouteChange)
        return () => {
          router.events.off('routeChangeComplete', handleRouteChange)
          router.events.off('hashChangeComplete', handleRouteChange)
        }
      }, [router.events])

    windowのnullチェックは省略してもよいかもしれません。SSRまわりで事故る印象があるため、個人的に手癖として追加しています。

    別ファイルにまとめてみた

    管理しやすさを考えると、以下のような1コンポーネントにまとめた方が良いかもしれません。

    export const GATracking: FC<{
      trackingId?: string
    }> = ({trackingId}) => {
      const router = useRouter()
      const { events } = router || {}
      useEffect(() => {
        const handleRouteChange = (url: string) => {
          if (!window) return
          window.gtag('config', '${trackingId}', {
            page_path: url,
          });
        }
        events.on('routeChangeComplete', handleRouteChange)
        events.on('hashChangeComplete', handleRouteChange)
        return () => {
          events.off('routeChangeComplete', handleRouteChange)
          events.off('hashChangeComplete', handleRouteChange)
        }
      }, [events])
      return (
        <>
          <Script
            async
            strategy='lazyOnload'
            src={`https://www.googletagmanager.com/gtag/js?id=${trackingId}`}
          />
          <Script strategy='afterInteractive'>
            {`
              window.dataLayer = window.dataLayer || [];
              function gtag(){dataLayer.push(arguments);}
              gtag('js', new Date());
              gtag('config', '${trackingId}', {
                page_path: window.location.pathname,
              });
            `}
          </Script>
        </>
      )
    }
    

    SSRがあるアプリなどで、windowを使いたくない場合は、useCallbackなどで関数に押し込めたものをHookにするのも手だと思います。

      const putEvent  = useCallback((eventName: Gtag.EventNames, params: Gtag.EventParams) => {
        if (!window) return
        window.gtag('event', eventName, params)
      }, [])

    参考記事

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark