Sending custom event to GA4 via GTM without listing all its attributes

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.