The PM's Definitive Guide to Google Tag Manager

The PM's Definitive Guide to Google Tag Manager

When building a web app, there’d probably be quite a few third party apps and services you’d want to run with your app: analytics, live chat, customer support software, Facebook and other retargeting pixels, and more.

One way to install them is to paste their code right into your front-end layout every time you install a new service. It’s easy to start with, but things get messy when you send your users’ data and events into multiple external services. There’s segment.io that solves just that issue, and allows you to install and run multiple analytics channels in your app. It also provides an API to query your data called Warehouse. Look into it if you haven’t before.

Another option is to use Google Tag Manager to install, trigger and manage javascripts on your app. It’s free and flexible, but requires some getting used to. The good news is, with GTM you can do a lot of stuff in your app without distracting your front-end developers from making something people actually want.

Frankly, using GTM saved me so much time I think every startup founder and product manager should use it.

I’ll walk you through the installation and basic principles of GTM. We’ll implement a Google Tag Manager setup for a web application that I use on Amplifr:

  1. Sending your user’s profile data to multiple analytics platforms and CRMs: Intercom, Chatra.io (live chats), Google Analytics (with user identification support through custom dimentions), Amplitude Analytics, Appcues and Facebook Pixel.
  2. Events reporting to multiple analytics platforms via dataLayer and data-gtm-event HTML attributes, so you can add custom events in your front-end easily.

Installing GTM

Google Tag Manager is a container that runs everything else as you want it. It’s a piece of Javascript designed to load and run other pieces of Javascript when you want them.

Installing Google Tag Manager is as easy as pasting a snippet of javascript into your layout. Sign up or create a new account, create the container and grab its installation code.

On Amplifr, we have separate containers for the blog and the app itself. Landing pages, such as Do You Even UTM, use the app’s container. It has 42 tags, 24 triggers, 35 variables and 137 versions (yes, GTM containers have revisions).

The Basics: WTF are Tags, Variables and Triggers

Tags are the pieces of code you want to run or actions you want to perform.

Want to insert Google Analytics init code? That’s a tag. Send an event to Google Analytics? That’s another tag. Insert Facebook Pixel? The third tag.

Want to notify Mailchimp via their javascript API about some action the user has taken on your app? Yes, you can do that in a GTM tag.

Tags are fired only under the right circumstances you can set up for each tag. The set of circumstances for the running tags is called a trigger.

GTM can listen to various events from the app page and make a trigger based on them: HTML element clicks, pagе views (navigation) with a particular URL (URLs can be regex-matched or filtered). You won’t need to write a single line of code to listen and trigger based on most events (clicks and navigation). But to trigger on you app logic or some of more custom things in the app, you’ll need to send a data layer event (we’ll talk about those later) or set up some additional attributes in your app’s UI code.

Here’s a quick example of a Trigger based on a specific link click on a specific page URL:

Think of every Tag body (code) as a template. The templating engine is dead simple—it can only do one thing, substitute {{VariableName}} with it’s value. That’s it.

When the trigger fires, the tag’s body (javascript) gets executed. It uses global document javascript variable scope, and you can use document, window, and all your variables as I just described.

GTM has a set of system-provided variables, like Container Name, Container Version, current Page URL, Page Host and Page Path, and others. In addition to that, you can add variables of your own. These, for example, can be constants you just add to your container, like your Intercom App Id, or some public API keys you use more than once in the container. They can be a javascript variable you push to GTM through the dataLayer. You can also use DOM elements, data attributes and cookies, but dataLayer variables are enough for now.

Here’s how dataLayer variables work. When you init the GTM container in your app, it creates a global javascript variable called dataLayer. It’s an array object with a twist: every time you do dataLayer.push (), it sends the data you’ve pushed to GTM and the tag manager deals with the new data. If you’ve pushed an event, it handles it, and if you’ve just pushed some keys and values, it treats them as variables and assigns values to the variables that you’ve created in the GTM interface.

There’s a notation on how to push variables and events via dataLayer. For now, remember that every time you save something into the dataLayer object, you can access it in GTM and build a variable on it. You can use nested objects as well like this:

1
2
3
4
5
dataLayer.push ({
company: {
name: "ACME Inc"
}
});

Using HTML Data Attributes as Variables

Using HTML data attributes as variables is super handy. For example, you can have a {{Data Attribute GTM Event}} that will be set automatically from data-gtm-event="Important Link Click" attribute when a click occurs and the target element had the data attribute. If the target element had no data attributed, the variable will be cleared out.

There’s a naming convention for data attributes. To use data attribute called data-gtm-event as a variable source, choose a dataLayer variable with this body: gtm.element.dataset.gtmEvent. So this-notation becomes alligatorCased in GTM.

You can then use such variables in triggers to filter out important link clicks, like you could do with link classes.

This trigger will fire every time a user clicks any html element on the page that had data-gtm-event="Something" in it. On Amplifr, we use this to trigger a tag that will send out the event to analytics platforms we use.

Passing User Profile into GTM

Now that you know what GTM Variables, Triggers and Tags are, let’s push the users’ profile data to GTM so it’s available later on when you report events or init other pieces of javascript that depend on that profile information.

On Amplifr, we render user’s profile information as a meta tag in the app’s layout, and when the app loads, we trigger a GTM event called User Init and push the profile data together with the event.

This will require some code on your app—rendering the meta tag with user’s data in it and triggering the event. You’ll only have to do it once to push data into GTM, and then Tag Manager will push the data to all the analytics platforms and apps you want.

It will look like this:

1
2
3
4
5
6
7
8
9
10
let meta = document.querySelector ('meta[name=identity]')
if(meta) {
let user = JSON.parse (meta.content)
dataLayer.push ({
event: 'User Init',
user: user
})
}

Then, you just create a dataLayer v2 variable User Name and type user.name in its body.

When you push some data with an event itself in one dataLayer.push () call, the variables which depend on the data will be available for any tag that will be triggered by the event. In this case, {{User Name}} will resolve correctly in any tag that will depend on {{User Init}} event.

Here’s how our list of variables looks. Yeah, our naming notation sucks.

Understanding GTM Events and DataLayer

You can push a “GTM event” to Tag Manager by calling dataLayer.push ({event: "Event Name"}). This will not send an event to Google Analytics all by itself or do any other sort of magic.

The basic scenario for handling a specific event will be like this:

  1. Your app’s javascript runs dataLayer.push ({event: 'Custom Event'})
  2. GTM’s trigger detects the event and invokes a tag that you’ve written for that event.
  3. The tag runs some other javascript (maybe using the variables that you’ve passed to GTM by that time).

Let’s clarify how variables work together with events. All the variables you’ve passed to dataLayer before the push () call with the event key in it. All the variables you’ve passed together with the event key will be available to all the triggers and tags that will be set in motion with the push () call.

You can have an event called Subscribed to the Newsletter that is fired when a user fills in his email in the Mailchimp subscribe form. GTM would run a tag with a snippet of javascript that will go to Mixpanel and mixpanel.track ('Subscribed to the Newsletter'). You might also want to run something like ga ('send', 'event', 'Subscribed to the Newsletter'). Of course you will want to identify the user in both Mixpanel and Google Analytics before you report these events.

But it’d be annoying to write a trigger and a tag for each and every event you want to report, right?

At least we were lazy enough to build a scheme, in which every event we push or trigger with data-gtm-event attribute gets reported to the right analytics platforms and other apps. Here’s how you can build a scheme like that for your startup.

Sending Data to an Analytics Platforms from GTM

Before you build that, let’s understand what we need to do.

  1. Identify the user in every analytics platform we want to work with.
  2. Understand which events we want in each particular analytics platform. We will need at least one tag for each analytics platform here.
  3. Build the triggers that will catch data-gtm-event and the right dataLayer events (because we don’t want EVERYTHING reported to every analytics app we have). At least two tags: data attributes and dataLayer events.
  4. Build a tag for each analytics platform that will send the events if needed.

There’s a bunch of guides about setting up Google Analytics user identification. It’s pretty complicated and it’s a topic for another article, so I’ll leave it out. Let’s pick Amplitude Analytics as an example analytics platform and write triggers and tags for it.

Initializing Amplitude Analytics with Tag Manager

Create a tag, call it Amplitude -Init and paste your Amplitude javascript snippet in it with the init call. It should look like this.

Passing User’s Profile to Amplitude

  1. Create a Trigger called User Init of type “Custom Event”. Use whatever event name you want, the trick is that your front-end code should do something like this: dataLayer.push ({event: 'user-init', user: { name: '...' }}).
  2. Create a Tag called Amplitude -User Init and use trigger User Init for it. This tag will check if Amplitude is loaded (if (window.amplitude) {}), load Amplitude script if it’s not, and call amplitude.identify () with the user’s data.
  3. When editing that tag, go to Advanced Settings, Enable Custom Tag Firing Schedule, and choose Amplitude -Init to always run before this tag. This requires your init tag to only load Amplitude if it’s not yet present, or else it’ll be loadad gazillion times and we don’t want that. But this also makes sure Amplidute will always be loaded no matter what tag firing sequence occurs.

Here’s what your tag code should look like:

1
2
3
4
5
6
7
8
9
10
11
if(! window.amplitude) {
// load Amplitude script here
// call amplitude.init () after the script is loaded
} else {
amplitude.setUserId ('{{User ID}}');
}
amplitude.setUserProperties ({
name: {{User name}},
...
});

This first if-else statement makes sure we load Amplitude bulletproof.

What’s great about the GTM setup and what you can already see is that, when you need to add another analytics platform, you will only need to add a tag to push the profile data into it, and you’re done. No writing code in your app or redeploying.

Sending Events to Amplitude

Now the interesting part you’ve been waiting for.

We will write a small function in our front-end called track (). It should collect all the data it wants and then send an event called data-event via the dataLayer:

1
2
3
4
5
6
7
function track (event){
dataLayer.push ({
event: 'data-event',
eventName: event,
additionalData:...
});
};

We’ll need a new variable called {{Event Name}} that will be a dataLayer variable with code eventName. What this will do is that we will have the {{Event Name}} macro in our GTM tags that will handle the events we’ll push to it.

Create a Trigger that is called Data Event Received that listens for custom event data-event.

Now let’s create a Tag called Amplitude -Send Data Event that will be invoked on Data Event Received trigger.

Here’s what the code will look like, I’ll explain it in a second:

1
2
3
4
5
6
if(! window.amplitude) {
// load Amplitude script here
// call amplitude.init () after the script is loaded
}
amplitude.logEvent ("{{Event Name}}");

Simple, right? I lied, I won’t explain it ;-)

You can improve that code by adding custom event attributes to it via Variables. You can also do another trigger called Data Attribute Event Received, to listen on clicks with data-gtm-events on them.

Whitelisting Events for Some Platforms

We’ve just installed Amplitude Analytics via GTM. If you only need one analytics platform, you’re probably better off without GTM at all. However, when you need to install Intercom, Amplitude, Google Analytics and a bunch of other scripts, it comes in handy.

To add a new analytics service, just add it’s Tags that will be invoked on Data Event Received and pass the data you need to the service. You might’ve thought about this already: you don’t want to send all the same events to all the analytics services. It’s OK if you send a little bit extra to GA, but it’s not cool to clutter up your Intercom user’s activity feed, right?

To avoid sending all the events to all the services, just create an array of event names that you want to pass to a service, say var intercomWhitelist = []. And then, do if (intercomWhitelist.indexOf ({{Event Name}})! = -1) { intercom.trackEvent };. Easy; -)

Connecting more platforms

We’re implementing a Facebook Ads Funnel for our trial users this week, and I’ve decided to build the ads targeting on Pixel’s custom events. Everything I had to do to start gathering data into Facebook Ads was to add a Facebook Pixel -Send Data Event tag which filters the event (whitelist) and does fbq.trackCustom ({{Event Name}}); .

To actually start using GTM fluently in production and save time with it, I’ve spent a few days reading a ton of manuals. Hope this one helps you get started swiftly.

Share the story

Articles and notes on social media marketing and analytics. By the team behind Amplifr.

Subscribe and get new articles first. We send an email once a week.