HonoX で Google Analytics のタグを埋め込む

2024/5/30

今話題の Hono/HonoX で Google Analytics のタグを突っ込もうとしたらなかなかにハマったのでメモします

TL;DR

以下のようなものを _renderer.tsx に突っ込めば OK

1{import.meta.env.PROD ? (
2 <>
3 {/* <!-- Google tag (gtag.js) --> */}
4 <script
5 async
6 src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"
7 ></script>
8 <script
9 dangerouslySetInnerHTML={{
10 __html: `
11 window.dataLayer = window.dataLayer || [];
12 function gtag() { dataLayer.push(arguments); }
13 gtag('js', new Date());
14 gtag('config', 'G-XXXXXXXXXX');
15 `,
16 }}
17 ></script>
18 </>
19) : (
20 <></>
21)}

静的な script タグを突っ込みたい

HonoX はゴリゴリに新しいフレームワークです。
今回個人的に制作中の MPA 的なものを HonoX で組んでおり、一旦リリースしようかというタイミングで Google Analytics を入れようとしてハマりました。

HonoX を使いつつ、 @hono/react-rendererreactRenderer を使っていることを前提としています。
このとき、Google Analytics のタグを埋め込む方法は大きく分けて2つあると思います。

  1. 各ページの Island 部分で react-ga4 等のライブラリを使って埋め込む
  2. 頑張って静的な部分に埋め込む(サーバー側で最初からレンダリングしておきたい)

1の方法は試していないですが恐らくできるでしょう。
ただクライアント側の動作でタグを埋め込まないといけなかったり、いちいちページビューのたびにイベントを手動で送ったりしないといけなさそうでめんどくさそうです。
つまり、せっかくのアイランドアーキテクチャなんだし、かつ SPA ではないのでもうちょい賢くかっこよくやりたいわけです。

可能なら2の作戦で行きたいですよね。
そしてかつ、_renderer.tsx とかでヘッダの中に埋め込んで楽をしたいわけです。

そこで以下の方法を試しました。

うまくいかなかった作戦

hono/html のヘルパーを使う

https://hono.dev/helpers/html

こちらのヘルパーを使ってタグを埋め込むことを考えました。
しかしこれはうまくいきません。

script タグや、その中身を html`...` を使うと、それらがすべて HTML のコメントアウト \<!-- --> を巧みに使った状態でエスケープされてしまい、script タグが全く動作しません(セキュリティ的には安全そうではある)
html ではなく raw() を使っても同様の現象が起きました。

文字列として埋め込む

以下のような作戦です。

1 <script
2 async
3 src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"
4 ></script>
5 <script>
6 {"window.dataLayer = window.dataLayer || [];function gtag() { dataLayer.push(arguments); }gtag('js', new Date());gtag('config', 'G-XXXXXXXXXX');`,
7 }}
8 ></script>

React のプロの方であればわかるかもしれませんが、これもやはりうまくいきません。

うまくいく作戦: dangerouslySetInnerHTML

というわけで dangerouslySetInnerHTML を使います。どう見てもやべー名前

https://react.dev/reference/react-dom/components/common#dangerously-setting-the-inner-html

公式ドキュメントのような感じで例えば Markdown をパースしたものを埋め込みたい、とかそういうときに使うやつですね。
つまり任意の HTML が突っ込めるから危ないよ、ということです。

動的な文字列を突っ込める場合はエスケープをしっかりした状態で突っ込まないと本当に危ないです。

一方今回は多分安全だとわかっている Google Analytics のタグを埋め込むだけなのでこれを使ってしまいましょう。

というわけで冒頭のようにビルド後のみ Google Analytics を有効にしたい場合は以下のようにすればよいわけです。

1{import.meta.env.PROD ? (
2 <>
3 {/* <!-- Google tag (gtag.js) --> */}
4 <script
5 async
6 src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"
7 ></script>
8 <script
9 dangerouslySetInnerHTML={{
10 __html: `
11 window.dataLayer = window.dataLayer || [];
12 function gtag() { dataLayer.push(arguments); }
13 gtag('js', new Date());
14 gtag('config', 'G-XXXXXXXXXX');
15 `,
16 }}
17 ></script>
18 </>
19) : (
20 <></>
21)}

無事にめでたしめでたしです

該当箇所をビルド後に1行にしたければ以下のようにしてもよいですね(お好みでどうぞ)

1{import.meta.env.PROD ? (
2 <>
3 {/* <!-- Google tag (gtag.js) --> */}
4 <script
5 async
6 src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"
7 ></script>
8 <script
9 dangerouslySetInnerHTML={{
10 __html:
11 "window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-XXXXXXXXXX');",
12 }}
13 ></script>
14 </>
15) : (
16 <></>
17)}

おしまい

カテゴリー
プログラミング言語
フレームワーク/ライブラリ
ハードウェア
その他技術系
雑記