Load dataLayer Variables prior to gtag configuration in GTM

20 views Asked by At

I have a NextJS application that take the URL of the page, uses utility functions to set values to dataLayer key-value pairs of page_category, and content_category, that are set on the page to be added as custom dimensions values (or in the gtag case, event configuration) that should be attached to each page view.

The problem I am running into is that the dataLayer.push() happens after the gtag is configured and signal sent, which means the pageviews never contain the values from the dataLayer.

I'm curious what is the best plan of action to ensure the dataLayers are loaded prior to gtag firing. I believe the fact that my dataLayer value setting code is client side, which might be loading too late for the gtag to load. Maybe I need to move it server-side?

Here is my code:

layout.tsx (Loaded server-side with GTM container tag and component import):

import Script from 'next/script';
import React from 'react';
import Analytics from '../components/Global/Analytics';

const GTM_ID = process.env.GTM_MEASUREMENT_ID;

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang='en'>
      <Script id='google-tag-manager' strategy='afterInteractive'>
        {`
        (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
        new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
        j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
        'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
        })(window,document,'script','dataLayer','${GTM_ID}');
        `}
      </Script>

      {/* Load the dataLayer.push() array */}
      <Script id='data-layer-page-scope' strategy='afterInteractive'>
        {`
          (function(w,d,s,l,i){
            w[l]=w[l]||[];
            w[l].push({
              'page': window.location.pathname,
              'content_category': 'default',
              'page_category': 'default'
            });
          })(window,document,'script','dataLayer','${GTM_ID}');
        `}
      </Script>
      <body className={`${inter.variable} ${gelasio.variable}`}>
        <noscript
          dangerouslySetInnerHTML={{
            __html: `<iframe src="https://www.googletagmanager.com/ns.html?id=${GTM_ID}" height="0" width="0" style="display: none; visibility: hidden;"></iframe>`,
          }}
        />
        <Analytics />
        <main>
          {children}
        </main>
      </body>
    </html>
  );
}

Analytics.tsx (Client-side, helper function to clean up the URL into content_category and page_category values that are passed to the dataLayer in gtm.tsx

'use client';
import { usePathname, useSearchParams } from 'next/navigation';
import { useEffect } from 'react';
import { pageview } from '../../libs/gtm';

export default function Analytics() {
  const pathname = usePathname();
  const searchParams = useSearchParams();
  var content = '';
  var page = '';

  // Mapping for "page_category"
  function formatString(inputString: string): string {
    // Extract content between furthest right slashes
    const match = inputString.match(/\/([^/]+)\/$/);

    if (match) {
      // Extract the matched content
      const content = match[1];
      // Check if the trimmed string contains a hyphen
      if (content.includes('-')) {
        // Split the string by "-"
        const words = content.split('-');
        // Capitalize the first letter of each word and join them with a space
        const formattedString = words
          .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
          .join(' ');
        return formattedString;
      } else {
        // If there is no hyphen, simply capitalize the first letter
        return content.charAt(0).toUpperCase() + content.slice(1);
      }
    }

    // Return the input string if no match is found
    return inputString;
  }

  // Mapping for "content_category"
  if (pathname == '/') {
    page = 'Homepage';
    content = 'Homepage';
  } else if (pathname.includes('/tag')) {
    page = 'Tag';
    content = formatString(pathname);
  } else if (pathname.includes('/about')) {
    page = 'About';
    content = 'Connor Phillips';
  } else if (pathname.includes('/page')) {
    page = 'Page';
    content = formatString(pathname);
  } else if (pathname.includes('/portfolio')) {
    page = 'Portfolio';
    content = formatString(pathname);
  } else {
    page = 'Post';
    content = formatString(pathname);
  }

  useEffect(() => {
    if (pathname) {
      return pageview(pathname, content, page);
    }
  }, [pathname, searchParams, content, page]);

  if (process.env.NEXT_PUBLIC_VERCEL_ENV !== 'production') {
    return null;
  }
}

gtm.tsx (Where the dataLayer is set):

type WindowWithDataLayer = Window & {
  dataLayer: Record<string, any>[];
};

declare const window: WindowWithDataLayer;

export const GTM_ID = process.env.GTM_MEASUREMENT_ID;

export const pageview = (url: string, content: string, page: string) => {
  if (typeof window.dataLayer !== 'undefined') {
    console.log(`pageview: ${url} , ${content}, ${page}`);
    window.dataLayer.push({
      page: url,
      content_category: content,
      page_category: page,
    });
  }
};
0

There are 0 answers