Google Analytics 4 explained for developers

A lot of signs pointing in different directions. Photo by Daniele Levis Pelusi on Unsplash

When I started working with Google Analytics 4, even the basics of how it fits together were hard to understand. Since we're all being forced to upgrade soon, here's the contents of my brain in case it's useful to other developers.

I don't consider myself an authority on any of this, but it might be helpful.

Contents

Why is this important?

The current version of Google Analytics (GA) is called Universal Analytics (UA). It's going to shut down completely sometime in the near future at which point any existing UA implementations will stop receiving data.

(I say the near future, because Google just announced they're pushing back the retirement date from late 2023 to July 2024. Depending on when you read this, they might have changed their plans again.)

The new version is Google Analytics 4 (GA4) and it works a little differently from UA. The complexity involved in upgrading will depend to some extent on your use of GA - you may need to rebuild your analytics setup almost from the ground up. However Google will apparently now provide options in the Setup Assistant for duplicating your UA properties into GA4.

Options for implementation

UA was relatively straightforward. You added the Google code snippet to your site, along with your own JavaScript for specific tracking you wanted and the data started flowing into UA.

GA4 is a bit different. You have two options for how to implement it - Google Tag (GTAG) or Google Tag Manager (GTM). There are other options, but they're for CMS platforms. If you're using one of these, I'm afraid you're at the mercy of the GA documentation.

GTAG and GTM are pretty similar, but GTM apparently has more features. I'm going to focus on GTM, because that's what I've worked with.

In practice this looks like this:

  • User does something on your site
  • Something (custom JavaScript or GTM itself) detects that activity
  • Data is collected by GTM
  • GTM passes the data to GA4

This is probably a far too simple explanation of this. More knowledgeable people have tried to explain to me that GTM is a 'conduit' for GA4, rather than a middleman, but the exact detail of this remains unclear.

Different ways of using Google Tag Manager

GTM is quite powerful, but there are several different ways of using it. The option you choose will depend on your circumstances.

The first option is to put the GTM snippet on your website and then configure all of your tracking through the GTM web interface.

  • PRO: no developer involvement required for adding tracking, analysts control it all
  • CON: developers have no insight into what's being run on the site in terms of tracking
  • CON: disconnect between the site and the analytics, if the site changes the analytics code could break in places
  • CON: without care, performance could become an issue (more on that later)

At the other end of the spectrum, you could do all the heavy lifting yourself, by adding the snippet, setting up GTM to recognise specific events, then do all the tracking using custom code on your site.

  • PRO: developers have full oversight and control of the tracking
  • CON: analysts have to ask developers every time something has to change or be added
  • CON: lots of work involved

There's also a middle ground, where you can setup GTM to record data based on user interactions with elements with specific data attributes. For example, you could setup GTM to look for a data-form attribute on forms, and record specific information when forms with that attribute are submitted.

  • PRO: less development work, but developers still have control over what is tracked and what isn't
  • CON: harder to do complex tracking

There's probably other pros and cons to each approach but those are the ones that spring to mind.

Data structure

UA was relatively strict in terms of the data that it accepts - it was something like action, category and label, plus some additional options, where you could put extra bits like custom dimensions.

GA4 is much less strict. You can pass it almost anything, so users are able to determine their own data schemas. There are some restrictions, but it depends on how you implement GA4.

If you're using GTM, every bit of data needs an event attribute, and you need to configure GTM to recognise each of them. Anything without a recognised event attribute will be ignored, and not passed on to GA4.

One other note - apparently there's a 100 character limit for any event parameter value passed to GTM. Anything longer and it gets cropped. This doesn't apply for pageviews but it does apply for any custom data you've created. This makes link click tracking a bit problematic if you're trying to record the href of the link (a lot of URLs are more than 100 characters). We ended up doing something complicated by splitting these values into 100 character strings inside an object then reassembling the href in GA4, which is a whole lot more effort than it should be. Hopefully Google are going to fix this soon.

Choosing GTAG or GTM

Selecting the option for how to send data to GA4 will be down to you, but interestingly both GTAG and GTM work in the same way.

During testing I found I could switch between the GTAG and GTM snippets and leave the rest of the code untouched, and data would still flow to GA4. It wasn't formatted the right way - both need some configuration - but it got through.

Apparently one advantage of GTM is that once it's got the data it can do loads more with it than UA could - for example restructuring it or sending it somewhere else (multiple GA4 properties, for example). I've yet to confirm this personally.

How the dataLayer works

GTM and GTAG both use something called the dataLayer to handle data. It's basically an array that gets created when GTM is initialised (window.dataLayer = []). Anything pushed to this array is automatically processed. That means to send data to GTM, all you have to do is window.dataLayer.push({ your: data }).

The dataLayer should be created as part of the Google code snippet, so you don't have to initialise it yourself. You also don't need to do anything to manage it - GTM will apparently prevent it from getting too large.

There's a bit of a quirk, though. Beyond the dataLayer array, there's a dataLayer object, which is entirely handled by GTM, and it's the contents of this object that will be sent to GA4.

Within the lifespan of a page, data values in the dataLayer object persist. This means that if you push two different objects to the dataLayer array, the actual data that gets sent to GA4 might be a combination of the two. Here's an example:

  • a user does something, and { a: 1 } is pushed to the dataLayer array
  • the dataLayer object is now { a: 1 }
  • the user does something else on the same page, and { b: 2 } is pushed to the array
  • the dataLayer object is now { a: 1, b: 2 }
  • the user does something else on the same page, and { a: 2 } is pushed to the array
  • the dataLayer object is now { a: 2, b: 2 }

This means that if new data doesn't contain key/value pairs that have previously been pushed, GA4 will still receive that old data, because it's still in the dataLayer object. If you want to clear it out, you'll have to push empty or default values e.g. { a: false, b: 2 }.

I didn't understand this behaviour at first but consider the order of events on a page. Normally the first thing that happens is a page view, which would include the URL of the page. As long as it doesn't get overwritten, any other data gathered from that page will automatically include that URL. That's probably useful from an analyst's perspective.

GTM performance concerns

Something we picked up on early in our work was the concern that GTM might slow down the site. If all of the tracking was configured through GTM's web interface, that might add page load.

The most obvious example of this is GTM's custom HTML tags, which can include any bit of JavaScript you like. You write it, it gets added to your site and executed. There's several concerns here.

  • the people likely to be writing this stuff are analysts, who might not write lightweight and optimised code, slowing the site itself once loaded
  • over time, as analysts leave the organisation, the intent or even existence of parts of this tracking is forgotten
  • that extra code could inflate the size of the GTM JavaScript, slowing the time to load the page

GTM's JavaScript size is around 100 kB, compared to UA's relatively tiny 20 kB. We managed to add some custom scripts (deliberately horrible, inefficient stuff) and got that up to 160 kB.

That's a big jump, but you'd really have to work to get that high - there's a character limit of around 100k and some kind of sensible compression seems to be involved (the internet is suggesting that for enterprise users there's no limit, so watch out for that). I'd be more concerned about a badly written script that does something really inefficient, like firing an event every millisecond.

GTM's JavaScript loads asynchronously so the actual impact on performance (without custom scripts) is pretty small, fortunately. But custom scripts are still a concern, which is why we've forbidden them (using a blocklist), and any changes made by an analyst in the GTM interface have to be approved by a developer before publishing.

For reference, GTAG's JavaScript is a bit smaller at around 60 kB, but saving that much compared to losing the functionality of GTM wasn't (for us) worth it.

Because of various legal things it's now important to let users consent to cookies before you do anything like tracking. As far as I know there are two ways of doing cookie consent with GTM.

The first is to do what we do - handle it yourself. Our cookie handling code is reasonably complex and basically doesn't do anything involving GTM until users consent to cookies. It doesn't even add the GTM script to the DOM before then, which means that non consenting users don't even have to download the JavaScript. If they do consent, we load the GTM code snippet and kick everything off, but not before.

The other approach is to let GTM handle cookie consent. Basically this involves loading GTM and immediately sending an event to block cookie consent, then attaching an event listener to the cookie banner 'yes' button to reverse that. It's slightly less clean that the previous approach, but far simpler and probably much more appropriate if you're unlikely to be collecting anything approaching personal data (e.g. if your site is fairly simple and all you care about are page views).

Other notes and opinions

While I admire a lot of the work Google does I have to say that their user interfaces for analytics as well as the related documentation can be quite hard to navigate and understand. Analyst colleagues have also said that the GA4 interface in particular is very different from UA and difficult to use.

From my limited perspective I'd almost think that GA4 wasn't quite ready for mass usage, but I'm sure Google are working extremely hard to get it ready for the flood of use it's going to get soon.

Hopefully some of that was useful. I'll leave you with a suggestion for some further reading - Simo Ahava's site contains a lot of good stuff about analytics, including from a technical perspective.

Related

This article is tagged with