1

I want to track custom events in GA4 via GTM. I have setup a GA4 custom event tags which works fine. See screenshot below for example, it's not 100% correct to be working as described here but it's just for illustration. I was able to confirm my custom events triggered in GTM via the dataLayer.push() are passed to GA4.

Here's how I push the event data to the dataLayer:

dataLayer.push({
    "event": "GA4Event",
    "ga4EventName": "view_item_list",
    "ga4EventParameters": {
        "item_list_name":"Homepage",
        "items":[{
            "item_id":"8888888",
            "item_name":"A product",
            "item_brand":"a brand",
            "price":220.4892,
            "currency":"USD",
            "index":0
        }]
    }
});
 

Screenshot of GA custom event configuration in GTM

My gripe with this is that I need to list all event parameters (attributes from the ga4EventParameters object) one by one in the GA4 event tag configuration. See section circled in red in the screenshot. I honestly don't want to do this. It is tedious (we have multiple custom events with their own attributes), and will break as soon as we introduce a new attribute and we don't put it in that list.

I've created my own tag template to make it so that the entire ga4EventParameters object gets passed to GA4 without having to list all of its attributes. It works some of the time, but not all the time, which is where I need help.

Here's the template code (short version, removed init. code and imports):

const gtag = copyFromWindow('gtag');

// The GA4 tag id
const measurementId = data.measurementId;


if (measurementId) {
  const ga4EventName = copyFromDataLayer('ga4EventName');
  
  if (ga4EventName) {
    let ga4EventParameters = copyFromDataLayer('ga4EventParameters');
    
    if (!ga4EventParameters) {
      ga4EventParameters = {};
    }

    // Set the GA4 property id to send the custom event to
    ga4EventParameters.send_to = measurementId;
    
    log('GA4 event data:', ga4EventName, ga4EventParameters, gtag);
    
    if (gtag) {
      gtag('event', ga4EventName, ga4EventParameters);
      log('gtag called');
    } else {
      log('gtag not avail.');
    }
  }

} else {
  log('Tag is missing at least one of its configuration parameters');
}

My issue is with gtag, it's only present in window once all the GTM events have occurred:

  • If I run a dataLayer.push() with my custom event once the page is entirely loaded and GTM has completely run (like after 3-4seconds), it works completely fine, everything's great.
  • However, I have dataLayer.push() in the HTML of my page. This means it gets executed early. My custom GTM tag gets triggered by it, but gtag doesn't exist in window yet, so I can't send the event to GA4.

I tried triggering my custom tag template on the Window Loaded event that GTM has, but same thing, gtag isn't available yet.

I'm at a loss, I don't know how to solve this. Sure I could just use setTimeout to delay my dataLayer.push() call on the page but it would be unreliable as that delay would vary depending on device, network speed and script execution speed.

On a side note, I have no idea what script exposes gtag to window, and no idea why it gets set so late.

4
  • Can you share how you init the gtag? gtag should not be init so late and just wonder why need to copy from window for gtag function. It should be access without copy when init done. Commented Oct 5, 2024 at 14:05
  • @darrelltw I do not initiate gtag anywhere. If I remove the copyFromWindow, then GTM complains that gtag is undeclared. I was counting on gtag to be initialized by GTM, so that I could just use it easily without having to do any manual initialization. But it looks like it's not possible. It's frustrating because in Tag Assistant, I can see that native GTM events like Set or Consent Default do use gtag without any issue but I don't know how they do it. Commented Oct 29, 2024 at 16:18
  • @Link14 I am at the same situation. I was expecting gtag to be available since GTM fetchs it as i can see on the network tab: googletagmanager.com/gtag/… Did you find any solution? Thanks Commented Sep 11 at 9:02
  • @MiguelPájaro I've posted my solution as an answer to this question. It's not the best, but it's something. Commented Oct 6 at 18:37

1 Answer 1

0

This is the workaround we ended up using. Not perfect, but it works decently.

We're setting up a "queue" in the template storage, which is a simple array. Example:

const templateStorage = require('templateStorage');

let queue = [];
templateStorage.setItem(storageKey, queue);

Whenever there's a custom GA4 event happening, the custom template gets called. We check if gtag is available. If it is, we call gtag to push the event to GA4. If it isn't, we store the event in the queue.

const gtag = copyFromWindow('gtag');

if (gtag) {
    gtag('event', ga4EventName, ga4EventParameters);
} else {
    let queue = templateStorage.getItem(storageKey);
    if (!queue) {
        queue = [];
    }

    queue.push({ga4EventName: ga4EventName, ga4EventParameters: ga4EventParameters});

    templateStorage.setItem(storageKey, queue);

We've setup a "Timer trigger" in GTM, interval 250, limit 15. The goal is to process the queue as soon as gtag gets available. Since there's no way to know for sure when it will be the case, we have to retry multiple times until it's there.

    const gtag = copyFromWindow('gtag');
    if (gtag) {
        let queue = templateStorage.getItem(storageKey);
        // Delete the queue right away so it doesn't get processed a second time in a concurrent event
        templateStorage.removeItem(storageKey);
        if (queue && queue.length) {
            log('Got the queue!', queue);
            for (let i = 0; i < queue.length; i++) {
                gtag('event', queue[i].ga4EventName, queue[i].ga4EventParameters);
            }
            log('Queue processed');
        }
    } else {
        log('No gtag yet, not checking the queue');
    }
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.