Strongly Typed GA4 with Nextjs Part II - Danger Zone
Tags: nextjs, typescript, analytics, react
Published: 2021-09-08 06:19:11

##### In [Part One of this series](https://dev.to/asross311/strongly-typed-google-analytics-v4-with-nextjs-4g13), prior to the release of Next v11, we covered configuring GA4 with Nextjs. 

Since then, Nextjs has rolled out a next/script component which provides an alternative to dangerously escaping inner html -- a previously necessary evil when working with vanilla script tags.

##### Bonus: it's also more performant

![danger-zone](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/je2b796yrmbcbktz6tyg.gif)

Therefore, it's no longer necessary to live dangerously in the world of HTML -- semantics aside, we'll move the script tags we configured in `_document.tsx` in the first article over to `_app.tsx`.

*Important Aside: Scripts must be instantiated above the Head (next/head) tag in Next.js pages and must never be used with the head in `next/document`*

This syntactically sexy implementation can be executed as follows:

```tsx
const Noop: FC = ({ children }) => <>{children}</>;

export default function NextApp({
  Component,
  pageProps
}: AppContext & AppInitialProps) {
  
  const LayoutNoop = (Component as any).LayoutNoop || Noop;
  
  const apolloClient = useApollo(pageProps);

  useEffect(() => {
    document.body.classList?.remove('loading');
  }, []);

  const router = useRouter();

  useEffect(() => {

    const handleRouteChange = (url: URL) => {
      gtag.pageview(url);
    };
    router.events.on('routeChangeComplete', handleRouteChange);
    return () => {
      router.events.off('routeChangeComplete', handleRouteChange);
    };
  }, [router.events]);

  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>
      <Head nextSeoProps={NextSEO} />
      <ApolloProvider client={apolloClient}>
        <AuthProvider>
          <GoogleFacebookProvider>
            <LayoutNoop pageProps={pageProps}>
              <Component {...pageProps} />
            </LayoutNoop>
          </GoogleFacebookProvider>
        </AuthProvider>
      </ApolloProvider>
    </>
  );
}

``` 

For those of you curious about various methods available when leveraging the new `<Script />` component, you can [read more here](https://nextjs.org/docs/basic-features/script)

Before eagerly firing up dev or deploying these changes to preview/production, be sure to remove the following XSS honeypot from the Head of `_document.tsx`


```tsx
// ...
        <Head>
          <script
            async
            src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`}
          />
          <script
            dangerouslySetInnerHTML={{
              __html: `
                  window.dataLayer = window.dataLayer || [];
                  function gtag(){dataLayer.push(arguments);}
                  gtag('js', new Date());gtag('config',
                  '${GA_TRACKING_ID}', {
                      page: window.location.pathname
                  });
              `
            }}
          />
        </Head>
// ...
```
### Real Time Metric Reporting? Yes.

For those of you interested in sending real-time metrics to your google analytics property, you're in luck. Below your default export in `_app.tsx`, include the following function, and you'll have real-time metrics pinging in your GA4 dashboard before you know it

```tsx
export function reportGAVitals({
  id,
  name,
  label,
  value
}: NextWebVitalsMetric) {
  if (typeof window !== 'undefined')
    window.gtag('event', name, {
      event_category: label === 'web-vital' ? 'Web Vitals' : 'Next.js custom metric',
      value: Math.round(name === 'CLS' ? value * 1000 : value), // values must be integers
      event_label: id, // id unique to current page load
      non_interaction: true // avoids affecting bounce rate.
    } as Gtag.EventParams);
}

```
Recall that the `Gtag.EventParams` type from the `@types/gtag.js` package is globally available for consumption with 0 imports required -- configured in a root `index.d.ts` file as follows:

###### index.d.ts
```ts
/// <reference types="gtag.js" />

declare module 'gtag.js';
```

That's it for now my fellow TypeScript fiends, but expect more strongly typed Nextjs articles to come! Feedback on topics you'd like to see covered with Next + TypeScript would be appreciated -- anything from graphql-codegen to apollo config to api routes to flawlessly typing dynamic routes with fallback set to true in a headless context and so on and so forth.

![hate-surprises](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/caeb86t0vdeyvyt5k3e0.gif)


Cheers