⚙️

【Next.js 13】環境ファイル別で Google Analytics を設定する

2023/06/30に公開
3

この記事では、Production と Staging 環境で別々の Google Analytics データストリームを利用する手順を紹介します。NextJS 特有の環境ファイルの扱い方や、NextJS 13 から正式に使える App Directory でも利用可能な設定例になります。

はじめに

NextJS における環境ファイルについて

NextJS では .env.local .env.development .env.production の 3 種類の環境ファイルが用意されていますが、next start や  next build で利用される環境ファイルは .env.production のみです。

例えば Staging と Production 環境を用意している場合、環境ごとに next build で Staging 用環境ファイルか Production 用環境ファイルを選べるほうが好ましいでしょう。しかし、NextJS では環境別に読み込む環境ファイルを指定することができないので、パッケージを利用する必要があります。

https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables

env-cmd を導入する

env-cmd が環境別に環境ファイルを読み込むことを可能にしてくれます。

npm add env-cmd

このパッケージを利用して、package.yamlを以下のように編集することで、任意の環境ファイルを指定することが可能になります。

"build": "env-cmd -f .env.prod next build",
"build:stg": "env-cmd -f .env.stg next build",

Google Analytics の設定

環境ファイルを用意する

まずは、Google Analytics GA4 のデータストリームを Production と  Staging 用に用意して、測定 ID を入手します。
その後、env-cmdで指定した環境ファイルに測定 ID を入力します。

.env.prod
NEXT_PUBLIC_GA_ID='G-[測定ID]'
.env.stag
NEXT_PUBLIC_GA_ID='G-[測定ID]'

gtag.ts を用意する

後々、測定 ID を取得したり pageview を呼び出すためのコードを用意します。

/src/lib/gtag.ts
export const GA_MEASUREMENT_ID = process.env.NEXT_PUBLIC_GA_ID || "";

export const existsGaId = GA_MEASUREMENT_ID !== "";

export const pageview = (path: string) => {
  window.gtag("config", GA_MEASUREMENT_ID, {
    page_path: path,
  });
};

ローカルで next dev などでテスト中は Google Analytics にデータを飛ばさないので、ローカルの環境ファイルの測定 ID を空欄にすることで existsGaId が false になり、後々 Google Analytics を呼び出さないような仕組みになります。

gtag の型を定義します。

npm add -D @types/gtag.js
/src/types/gtag.d.ts
declare module 'gtag.js';

Google Analytics Client Component を作成する

先ほど作成した gtag.ts を用いて、Google Analytics を呼び出すための Client Component を作成します。NextJS 13 以降は、明示的に 'use client' と指定してクライアントサイドで実行するよう指定する必要があります。

/src/components/GoogleAnalytics.tsx
'use client'

import { usePathname, useSearchParams } from 'next/navigation'
import Script from 'next/script'
import { useEffect } from 'react'

import { existsGaId, GA_MEASUREMENT_ID, pageview } from '../lib/gtag'

const GoogleAnalytics = () => {
  const pathname = usePathname()
  const searchParams = useSearchParams()

  useEffect(() => {
    if (!existsGaId) {
      return
    }
    const url = pathname + searchParams.toString()
    pageview(url)
  }, [pathname, searchParams])

  return (
    <>
      <Script
        strategy='lazyOnload'
        src={`https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`}
      />
      <Script id='gtag-init' strategy='afterInteractive'>
        {`
          window.dataLayer = window.dataLayer || [];
          function gtag(){dataLayer.push(arguments);}
          gtag('js', new Date());
          gtag('config', '${GA_MEASUREMENT_ID}', {
            page_path: window.location.pathname,
          });
        `}
      </Script>
    </>
  )
}

export default GoogleAnalytics

layout.tsx から Google Analytics を呼び出す

最後に、app 直下の layout.tsx から Google Analytics を呼び出して、設定完了です。

/src/app/layout.tsx
import GoogleAnalytics from '@/components/GoogleAnalytics'

// 途中省略...

export default function RootLayout({ children }: { children: React.ReactNode }): JSX.Element {
  return (
    <html lang='en'>
      <head>
        <GoogleAnalytics />
      </head>
      <body className={zen_kaku_gothic_new.className}>{children}</body>
    </html>
  )
}

宣伝

ダイエット中の人を応援するために、ファミリーレストランのメニューをガチャガチャ形式で選べるネタ(?)サイトを作成しました。

カロリーを指定してメニューを選べるので、ネタで遊んでみてください!
https://dietgacha.net/

普段は、誰でも使いやすい「筋トレ記録アプリ」を開発しておりますので、こちらも是非ご利用ください!

https://komms.co.jp/brnt

参照

https://zenn.dev/yuji/articles/cb4f2562cf70ae
https://qiita.com/ruiiixiii/items/2f3e3497d13ec804eb40

Discussion

masamasa

はじめまして!Masahiroと申します。
Next.jsのGoogle Analyticsの導入について質問があります。
今個人でアプリを作っているのですが、kazukiさんの記事を参考に作っています。
下記のコードで

layout.tsx
import GoogleAnalytics from '@/components/GoogleAnalytics'

// 途中省略...

export default function RootLayout({ children }: { children: React.ReactNode }): JSX.Element {
  return (
    <html lang='en'>
      <head>
        <GoogleAnalytics />
      </head>
      <body className={zen_kaku_gothic_new.className}>{children}</body>
    </html>
  )
}

<head>内でGoogleAnalytics コンポーネントを読み込んでいるのですが
chromeの検証ツールで見てみると
scriptの読み込みが<body>内に読み込まれてしまいます。
しかし、google Analyticsは正常に計測されています(計測は本番環境のみ)

Google Analyticsを読み込んでいる場所はapp/layout.tsxになります。
本来読み込まれる場所は<head>で合っていますでしょうか?
色々と原因を調べて見たのですが、
他に<head>に取り入れているのはSEO対策のMetadataのtitledescriptionをそれぞれのディレクトリのlayout.tsxで書いていますがそこまで影響はないと考えています。

原因などは分かりますでしょうか?
ソースコードは下記になります。

github
https://github.com/masahiro96848/zerone_blog

kazuki23 (Kazuki Fukushima)kazuki23 (Kazuki Fukushima)

Masahiroさん、コメントありがとうございます!

おっしゃる通り、<head><GoogleAnalytics /> を読み込んでも実際には <body> 内で読み込まれるようです。

これは GoogleAnalytics.tsxnext/scriptstrategylazyOnloadafterInteractive で指定しているからだと思われます。
https://nextjs.org/docs/app/api-reference/components/script

GitHub でも類似 Issue が過去に上がっています。
https://github.com/vercel/next.js/issues/26591

特に動作上で問題を感じていないのでこのまま運用しようと思っていますが、もし何か計測で問題があるようでしたらご指摘いただけますと幸いです!

masamasa

ご返信ありがとうございます!
参考資料の共有もありがとうございます!

これは GoogleAnalytics.tsx の next/script の strategy を lazyOnload や afterInteractive で指定しているからだと思われます。

そういう挙動なんですね!

現状、google analyticsで計測が出来ていて、特に問題は出ていないです!
また、何か問題がありましたら相談させてください!