diff --git a/website/src/google-analytics.ts b/website/src/google-analytics.ts
new file mode 100644
index 0000000000..565e0fff8f
--- /dev/null
+++ b/website/src/google-analytics.ts
@@ -0,0 +1,66 @@
+import { useEffect } from 'react'
+import type { NextRouter } from 'next/router'
+
+// https://developers.google.com/analytics/devguides/collection/gtagjs/pages
+const pageview = (url: string, trackingId: string) => {
+ ;(window as any).gtag('config', trackingId, {
+ page_path: url,
+ })
+}
+
+/**
+ * @example
+ * function AppWrapper(appProps: AppProps) {
+ * const { Component, pageProps, router } = appProps;
+ * const analytics = useGoogleAnalytics({ router, trackingId:"UA-XXXXXX-X" });
+ *
+ * return (
+ * <>
+ *
+ *
+ *
+ * >
+ * )
+ * }
+ */
+export function useGoogleAnalytics({
+ trackingId,
+ router,
+}: {
+ trackingId: string
+ router: NextRouter
+}) {
+ useEffect(() => {
+ const handleRouteChange = (url: string) => {
+ pageview(url, trackingId)
+ }
+ router.events.on('routeChangeComplete', handleRouteChange)
+ return () => {
+ router.events.off('routeChangeComplete', handleRouteChange)
+ }
+ }, [router.events, trackingId])
+
+ // Why not a component? Next.js + CJS goes crazy when I use `next/router.js` and `next/script.js`.
+ // I get: https://reactjs.org/docs/error-decoder.html/?invariant=130&args%5B%5D=object&args%5B%5D=
+ // Probably because of two different versions of React or something. Not sure...
+ return {
+ loadScriptProps: {
+ strategy: 'afterInteractive' as const,
+ src: `https://www.googletagmanager.com/gtag/js?id=${trackingId}`,
+ },
+ configScriptProps: {
+ id: 'gtag-init',
+ strategy: 'afterInteractive' as const,
+ dangerouslySetInnerHTML: {
+ __html: `
+ window.dataLayer = window.dataLayer || [];
+ function gtag(){dataLayer.push(arguments);}
+ gtag('js', new Date());
+ gtag('config', '${trackingId}', {
+ page_path: window.location.pathname,
+ });
+ `,
+ },
+ },
+ }
+}
diff --git a/website/src/pages/_app.tsx b/website/src/pages/_app.tsx
index 92acdea185..a9c27fa7c8 100644
--- a/website/src/pages/_app.tsx
+++ b/website/src/pages/_app.tsx
@@ -2,6 +2,7 @@ import 'remark-admonitions/styles/infima.css'
import '../../public/style.css'
import { appWithTranslation } from 'next-i18next'
+import Script from 'next/script'
import { Box, extendTheme, theme as chakraTheme } from '@chakra-ui/react'
import { mode } from '@chakra-ui/theme-tools'
@@ -22,6 +23,8 @@ import {
import type { AppProps } from 'next/app'
import React from 'react'
+import { useGoogleAnalytics } from '../google-analytics'
+
ExtendComponents({
Instruction: (props: React.ComponentProps) => (
@@ -78,6 +81,10 @@ const tutorialMdxRoutes = {
function AppContent(appProps: AppProps) {
const { Component, pageProps, router } = appProps
+ const googleAnalytics = useGoogleAnalytics({
+ router,
+ trackingId: 'G-246BWRER3C',
+ })
const isDocs = router.asPath.startsWith('/docs')
const isTutorial = router.asPath.startsWith('/tutorial')
@@ -85,6 +92,8 @@ function AppContent(appProps: AppProps) {
return (
<>
+
+