Macro Guide For Google Tag Manager

I’ve written a new Variable Guide for Google Tag Manager, which covers the new GTM UI. This guide is for the old UI.

You might be vaguely familiar with macros if you’ve ever used a computer. Basically, whenever you perform a complicated task with a simple gesture, or reuse complex code with a simple input mechanism, you’re using macros. Think keyboard shortcuts.

In Google Tag Manager, this is the essence of macros. You can do away with a lot of complexity by resorting to macros, especially if you ever find that you need the same piece of code or the same operation over and over again.

This guide will first take a cursory look at what macros really are, before going through the (current) list of available macros. I’ll add short examples and use cases for each, but there’s a whole lot more to be found online.

What is a macro
Technical details
How to use macros
The different macro types in GTM
1. 1st Party Cookie
2. Auto-Event Variable
3. Constant String
4. Container Version Number
5. Custom Event
6. Custom JavaScript
7. Data Layer Variable
8. Debug Mode
9. DOM Element
10. HTTP Referrer
11. JavaScript Variable
12. Lookup Table
13. Random Number
14. URL

What is a macro

In Google Tag Manager, a macro is a construct that you can set up in the UI. However, it’s not a tag in the sense that it would be something you’d tag your page with. Rather, macros are turned into a function of the google_tag_manager object (more on this later), and they return a value when called.

Macros in GTM

So a macro is, in essence, a placeholder for some value that you might need in your tags. Most common uses for macros would be to:

  • Retrieve a constant string (e.g. your tracking code)
  • Retrieve a variable string (e.g. the href attribute of a clicked element)
  • Retrieve the result of some simple function (e.g. the date and time of the current visit)

Macros are thus used to call for and to retrieve certain values. You can surely see how they facilitate your day-to-day GTM use, can’t you? It’s much easier to just refer to a macro name rather than having to write the value-retrieving function over and over again.

An example of a GTM macro

Without macros, whenever you rewrite your hard-coded functions or variables, you have to remember to change them wherever they were used. It’s much easier to just have a macro refer to the original location.

Technical details

Note that the following analysis is my own and only for illustrative purposes. Don’t attempt to use the DOM functions uncovered here in your own code, since they are liable to change without notice. Use macros only via the GTM UI using the syntax required by the tool.

Well my skills at reverse engineering minified JavaScript code are pretty sparse, but you can uncover a lot just by going through the DOM (Document Object Model) tree of a page where macros are active. You can do this by using a JavaScript console in your browser’s developer tools. Just type window and click enter.

For example, if you go through the DOM, you’ll find an object called google_tag_manager:

Google Tag Manager object with macro function

Here you can see the google_tag_manager array and its contents. There’s an index for your container ID (GTM-XXXX), under which you’ll find the macro function. The function is actually defined in gtm.js, which you load in the GTM container snippet.

The macro function accepts a single parameter and returns something else.

It appears that the parameter is a numeral value which refers to a certain macro that you’ve set up in GTM. I didn’t find any way to actually identify which number returns which macro value, but just by testing you can find a lot of stuff. For example, I have a macro which stores the number of images on the page. Twiddling around with the macro function I finally found the correct index:

Testing a macro index via JavaScript console

So as you can see, when you refer to a macro in your tag setup, e.g. {{number_of_images}}, it actually translates to a JavaScript function call that returns the value.

This is also the reason why your macros don’t accept parameters. The macro itself is a function call which only takes the index number of the macro as its parameter. Runtime evaluation is done on the spot with only elements found in the Document Object Model to work with.

This is why you’ll see undefined for those indices which expect a value in the data layer that hasn’t been pushed yet. As soon as the value is pushed into the data layer, you’ll find that the macro index returns the appropriate value.

How to use macros

To use a macro, you refer to it in GTM with its name between two sets of curly brackets:

{{macro name}}

Note that this is case-sensitive, so {{macro name}} and {{Macro name}} refer to different things.

You can use macros in any GTM fields which accept a text string, e.g.:

Macros in GTM tag fields

On top of that (and this is really cool), you can refer to macros in your custom HTML tags:

Macro in Custom HTML tag

Here, I check whether or not the clicked link href contained the text “.pdf”. Since it’s an auto-event macro I’m using, the macro won’t return a proper value until the auto-event variable is pushed into the data layer. So upon page load the macro {{element url}} would return undefined, but after I click a link, it would return the href attribute of the link. This means that in order for this tag to work like it should, I need a firing rule which prevents it from firing until after the auto-event variable is pushed into the data layer. Runtime evaluation, remember?

You can also, and this is really REALLY cool, refer to macros in other macros!

Macro in a custom JavaScript macro

This returns the tag name of the first child of the clicked element.

There’s a small point to make about using macros and JavaScript. You can only treat a macro as a JavaScript object in the context of a script. This means that you can freely access macro properties with dot notion, e.g. {{element}}.innerText, in Custom HTML tags and Custom JavaScript macros. However, this won’t work in other GTM fields where you can use macros (e.g. Event Category), because these fields do not run JavaScript functions. So if you want to access a property of a macro in a tag field, for example, you will need to create a separate macro for this property.

Macro in script vs. field

You’ll also use macros a lot in your firing and blocking rules:

Firing rule for debug mode

This rule would fire only when visits are tracked to my GA debug account.

Here’s the key takeaway:

Use a macro whenever you need to reuse a certain value or value-returning function.

I won’t add a separate section for when not to use a macro, but suffice to say, if you only need a certain value once or you have a function which you know won’t be reused, don’t bother creating a macro for it. Just hard-code it into your custom HTML tag or use the value directly in your tags. A macro is always, after all, another function call, which does increase the complexity of your code.

Rules are a bit different, since you must use macros in rules. You can’t write an ad hoc function into your rule; you must have a macro which you evaluate on runtime.

The different macro types in GTM

Here, I’ve listed all the different macro types you can create in Google Tag Manager (at the time of writing). I’ve added a short description, and an example use case for each.

All macros need a name, which you refer to (case-sensitive) using two sets of curly brackets, e.g. {{macro name}}.

Also, you can give a default value to some of the macros. It’s a good practice to use a default value, because you can use that as a blocking rule (e.g. {{macro}} equals “default”) in your tags, and you can avoid getting the pesky undefined as a return value.

Note also that there’s a difference between macro type and macro instance. Below, I list all the different macro types you can use to create different macro instances. You can have as many instances of a macro type as you want (as long as they have different names). Macro types are provided by GTM, and you can’t edit or create new macro types.

Macro instance and macro type

When you create a new container, you get the following pre-populated macro instances. Encased in brackets is the type of macro the pre-populated macro is an instance of:

  • element (Auto-Event Variable / Element)
  • element classes (Auto-Event Variable / Element Classes)
  • element id (Auto-Event Variable / Element ID)
  • element target (Auto-Event Variable / Element Target)
  • element url (Auto-Event Variable / Element URL)
  • event (Custom Event)
  • referrer (HTTP Referrer)
  • url (URL / URL)
  • url hostname (URL / Host Name)
  • url path (URL / Path)

So you might be a bit confused at first, since you have these pre-populated macros which are named the same as the macro types they are instances of. But you’ll get the hang of it soon enough, don’t worry.

1. 1st Party Cookie

1st Party Cookie
The 1st Party Cookie macro returns the value of the cookie whose name you indicate in the Cookie Name field. For example, if you set up a cookie named “front-page-visits” which increases by one every time the visitor visits your front page, you can set up this macro to return the value (i.e. number of visits) every time the macro is used.

Check my previous article on the session cookie for a nice use case for this macro.

Basically this macro can be used to replace any code you used to have for retrieving cookie values.

2. Auto-Event Variable

Auto-Event Variable
When you set up an event listener using GTM, you can use the auto-event variable macros to return the value of various attributes of the element you were listening to.

You need to specify the variable type of the auto-event variable you want to use. Here are the different types.

Element: This returns the element that was clicked. Note that the macro returns an object, not a string, so the best use of it is for traversing the DOM tree of the clicked element (e.g. {{Element}}.children[0].tagName returns the tag name of the first child of the clicked element).

Element classes: This returns the class attribute of the element that was clicked. For example, if the element was <div class=”mainBanner”>, you’d get “mainBanner” as the return value.

Element ID: This returns the ID attribute of the element that was clicked. For example, if the element was <a href=”/home” id=”home-link”/>, the macro would return “home-link”.

Element target: This returns the target attribute of the element that was clicked. For example, if the element was <a href=”/home” target=”blank”/>, the macro would return “blank”.

Element text: This returns the text content (if any) of the element that was clicked. It returns either the innerText or the textContent property (depending on what browser the visitor uses).

Element URL: This returns the href (in e.g. links) or action (in e.g. forms) attribute of the clicked (or submitted) element. For example, if the element was <form action=”/process-form” id=”myForm”>, the macro would return “/process-form”. You can also choose which URL component is returned by the Element URL macro (see the chapter on URL macros for a description of component types).

History New URL Fragment: The History macros are populated by the History Listener. History New URL Fragment returns the URL fragment set in the current history state. You can use this to fire your tags (e.g. pageview) when a certain URL fragment is in the URL.

History Old URL Fragment: Returns the URL fragment set in the previous history change event. For example, if you first navigate to #aboutus then to #contact, History New URL Fragment will return “aboutus” and History Old URL Fragment will return “contact”.

History New State: History New State returns the state object stored in the current history entry (if any). You can access the properties of this object to set up your content and fire your dynamic tags. Note that it’s an object that is returned, so you can’t use it directly in any GTM fields. You’ll need to use some programming logic to access the properties of this object if you want to send them as strings with your tags.

History Old State: The previous state stored in browser history (if any). You can use this to determine what the previous browser history state was, for example if navigation patterns determine the tags you want to fire.

There’s a million different things you can use auto-event variables for. The variables are most frequently used to refer to certain aspects of the element in event descriptions. You could, for example, add {{element url}} as the action of a GA event which measures link clicks on your different pages.

Check my tutorial on auto-event tracking to see how the different auto-event variables can be used to improve your site measurement.

3. Constant String

GA tracking code as macro
This macro can be used to store a string you’d use over and over again. In other words, it’s a time-saver.

Store your GA tracking code in this, so you don’t have to remember it in every single tag. Just add the UA-XXXXXXX-X into the “Value” field, and name the macro “GA Tracking Code” or something similar. After that, whenever your tracking code is required (e.g. in tags), you can just use the macro {{GA Tracking Code}}.

4. Container Version Number

Container version number
This returns your GTM container version number. If you’re in the Preview mode (as you should be whenever you’re testing), this returns the preview container version number.

A nice way to use it is to output the version number into the console using console.log({{container version}}); in your Custom HTML tag. This way you can always check the console if you’re unsure which version you’re currently running. It’s a good way to debug your live implementation.

5. Custom Event

Event macro
This macro returns the data layer “event” value of the last event that was pushed into the array. So if you’ve just executed dataLayer.push({“event”: “myEvent”});, this macro will return “myEvent”.

This should be one of your most familiar macros. It comes with every container setup under the name {{event}}. There are about a zillion use cases for this, most notably in firing and blocking rules. For example, if you only want a tag to fire when the event “fireNow” has been pushed into the data layer, you can add the rule {{event}} equals fireNow to your tag. Then, as soon as you execute dataLayer.push({“event”: “fireNow”}); the event macro will be populated with the value “fireNow” and your firing rule will trigger.

6. Custom JavaScript

Custom JavaScript Macro
Use this macro to perform a simple JavaScript function using either ad hoc values or elements in the DOM tree. The JavaScript function has to be encased in a function structure, it has to return a value and it can’t take parameters of its own.

You could use it with auto-event tracking to return the value “Outbound link” for all outbound links and “In-site link” for all in-site links. You can then use these as your Event Category in the event where you send information about the link that was clicked. The function would look like this:

See how I’m referring to another macro within this macro? :) “” would naturally need to be replaced with your hostname.

Another neat trick is to use a Custom JavaScript macro to retrieve the file extension of the link that was clicked. You can use this to create more dynamic tags. Check out the tip on this Google+ post.

7. Data Layer Variable

Data Layer Variable
Use this macro to refer to the variables you push into the data layer. Remember, you do the push with dataLayer.push({“Variable_Name”: “Some value”});. If you set this macro to look for “Variable Name”, the return value will be “Some value”.

With “version 2” (now default), you can refer to nested values using dot notation. For example, if you’ve pushed dataLayer.push({“Products”: {“Product_1”: “Lawn mower”}});, by referring to Data Layer Variable Name Products.Product_1, you’ll get “Lawn mower” as the return value.

You can use it nicely in conjunction with auto-event tracking. When you click an element, the data layer is updated with the object gtm.element, which can be traversed like any DOM object. For example, to get the tag name of the object that was clicked, you’d create a data layer variable macro for gtm.element.tagName. Clicking an image would then return “IMG”.

8. Debug Mode

Debug Mode
This returns “true” if you’re in Google Tag Manager Debug mode, and “false” if you aren’t.

See Lookup Table Macro below for a nice use case.

9. DOM Element

DOM Element
Use this macro to refer to any element that can be found in the Document Object Model. The element needs a unique ID identifier. You can even specify the attribute whose value you want to get in return. If you don’t specify an attribute, you get the text content of the element (if any).

Let’s say I want a certain tag to only fire on pages where the text “By Simo Ahava” can be found in the <div id=”author”> element. A good use case for just my article pages. For this to work, I’d create a DOM Element macro called “Author” with Element ID “author”. After that, I can create a firing rule {{Author}} contains “By Simo Ahava” for the tag that only fires on my article pages.

10. HTTP Referrer

HTTP Referrer
This returns the HTTP referrer (i.e. the previous page URL). You can also choose which URL component is returned by the HTTP Referrer macro (see the chapter on URL macros for a description of component types).

You can use it to check if this is the first page of the visit or not. Create the macro and then use it like:

11. JavaScript Variable

JavaScript Variable
This returns the value of a globally defined JavaScript variable.

You can use dot notation to refer to nested variables, e.g. document.doctype to get the doctype declaration of the current document. (Thanks +Brian Kuhn for pointing this out).

If you haven’t (or can’t) push the value into the data layer (preferred), or if you can’t find the variable in the DOM, you can use this macro to refer to it. A simple use case would be if you define a global variable (e.g. var myName=”Simo”;) in a Custom HTML Tag, you can use this macro to get the content of this variable in other tags as well. Sure, you could just use “myName” to refer to the global variable, but reusability is the key here. If you find that you need to change the variable reference, you just need to change it once in the variable definition and once in the macro, rather than many times in all the tags where you refer to the variable.

12. Lookup Table

My favorite macro, by far. Check my five-star review of the lookup table macro for specifics. The lookup table macro is basically a simple runtime evaluation, where you assign value Y to variable Z if X is something.

Here’s a really nice use case (originally on Google+). If you use GTM Preview & Debug mode a lot (AND YOU SHOULD!), you might be annoyed at all the debug hits in your GA account (unless, of course, you filter yourself out). You can use a combination of Lookup Table and Debug Mode macros to make sure that debug mode hits are sent to your test account:

The Lookup Table macro for debug mode

Here the macro {{GA Tracking Code}} is assigned a variable value depending on the value returned by the {{Debug Mode}} macro.

Remember that lookup tables only accept simple runtime evaluation of “when X equals Y”, so you can’t do calculations (e.g. “when X < 10").

13. Random Number

Returns a random number between 0 and 2147483647.

You can use it to create a random hash for your URLs (for cache-busting) OR you can follow this example of using the random number to fire a tag for only a certain percentage of your visitors.

14. URL

Returns the URL or a specific component thereof. The various component types are:

URL: Returns the entire URL minus fragment (fragment begins with a #).

Protocol: Returns the protocol of the URL address (i.e. http or https).

Host Name: Returns the full hostname of the URL (e.g. or you can choose to strip the www-prefix.

Port: Returns the port number of the hostname. If no port number is specified, the return value is either 80 or 443 (for http and https, respectively).

Path: Returns the URI of the URL address, i.e. the structure immediately following the hostname. You can also specify “default pages”, which are stripped out of the return value if found.

Query: Returns the query string of the URL address (without leading question mark and possible fragment). You can also specify a query key for the macro, after which only the value of the key is returned if found in the URL.

Fragment: Returns the fragment of the URL without the leading #.

You could query for only email campaign traffic by creating a URL of component type Query. Set “utm_medium” as the Query Key. Then you can employ a firing rule for only those visits which came via email with {{URL Query}} equals email.


Macros are a really powerful way of making a complicated setup simple. You can use macros to dramatically reduce the amount of code AND separate tags in your Google Tag Manager container.

When using macros, a bunch of best practices should be observed:

  • Name your macros so that you’ll instantly identify them (e.g. “Name – Macro Type”)
  • Only use macros when they facilitate either coding or maintenance; don’t use them as replacements for one-off functions or variables
  • Document your macro use somewhere so that you’ll know what repercussions changes might have

The last is especially important in large implementations, where dozens and dozens of macros are used in dozens and dozens of tags.

Do you have any questions about macros? Drop a comment on this post if you didn’t find what you were looking for above.


  1. says

    Hi Simo,

    Tks a lot for your post..One question please..
    Which method (steps) and particularly which type of macro(s) would you recommend for tracking fields abandonment for a form (basic config. : method post on a cms /php platform) with GTM (from scratch) ? Tks in advance..

  2. Ciaran says

    Hi Simo,

    I am trying to create a macro that will capture data from a within the form element. The specific input type is a radio button. Would it be possible to record which button the user selected?

    • says

      Hi Ciaran,

      We’re having this same discussion in Google+, but I thought I’d share the solution to anyone who might be in the same situation. If you just have one set of radio buttons on the form (so only one radio button per form can be selected), you could create a Custom JavaScript macro with:

      function() {
      var radioBtns = {{element}}.getElementsByTagName(“input”);
      for (var i = 0; i < radioBtns.length; i++) { if(radioBtns[i].type===”radio” && radioBtns[i].checked) { return radioBtns[i].value; } } return “N/A”; } So here the macro function goes over all radio button elements on your form an returns the value of the selected button. If no button is selected, it simply returns the string “N/A”. You can substitute this with an operation of your choice.

  3. says

    Hey Simo,

    Was considering using the random number macro as a custom dimension, kind of like a user id. I think it would work as a key to associate user interaction data like events to ecomm outcomes for users who are not logged in and do not have a user id. Any reason you can think why this wouldn’t work? I would really appreciate you thoughts. Thanks!

    • says

      Hi Trevor,

      Sounds like a pretty valid approach, but there are a few caveats.

      First of all, are you thinking of a user-level custom dimension, so it persists across sessions? Remember that a “user” in GA is actually just a single device, so if the user uses a different device they’ll get another random number ID.

      Second, if you choose to view data on session-level instead, you can just create a visit-scope custom dimension, and make the random number macro call with every pageview. Sure, it’ll overwrite the previous value every time, but the point is that the last number that is assigned will apply to the whole session retroactively as well (that’s the beauty of a visit-scope custom dimension). So you don’t need to make any checks as to whether a random number was already assigned or not.

      Third, if you want to look at data through a user-level custom dimension (probably best in this case, as a event to ecom might take multiple sessions, I presume), you’ll need to use a custom cookie to store the original random ID. You see, a user-scoped dimension applies to the session it was set in and all subsequent sessions, so it doesn’t go through the user’s sessions in the past retroactively. This means that if you apply a new random number ID to the user with every session, you’ll have multiple IDs attached to the same user, and this will botch your measurement.

      So you’ll need an intermediary function, where before you set the random number ID to the user, you’ll check via cookie whether such an ID already exists. If it does, make sure you don’t send anything with the custom dim, or it will overwrite the previous value. If no cookie exists, you assign the ID to the custom dimension hit AND your write a cookie with the ID (make sure it has an expiration date +2 years or something, so that it mimics GA user cookie).

      You’ll just need a function to write the cookie with (see e.g. this post for the code), because you can use GTM’s 1st Person Cookie macro to query if the cookie exists.

  4. says

    Hey Simo,

    Thanks for the reply on Twitter!
    In GTM, I’m counting newly created projects as a conversion on our site.

    Currently we have a {{referrer}} URL indicating a project is created and then a {{URL path}} capturing the newly created project ID for Google Analytics. This captures all newly created projects. Is there a way I can flag that the lead has come from advertising on an earlier page (marked with utm_source=advertising)?

    I feel like I’m almost there and have tried with the Cookie macro and Analytics Event but may be missing the mark!

    Any help appreciated and thanks again for replying on Twitter :)

    • says

      Hi Luke,

      Well if the query parameters aren’t stripped from the URL, you can get them with the URL macro type. Create a new URL macro, set Component Type to “Query”, and set query key to “utm_source”. Whenever the macro is called, it returns the value of the current url query parameter utm_source (if found).

      If you don’t want to use standard GA reports, where you can already segment by visits which had utm_source=advertising, you could create a session-scope custom dimension, where you store the value of the url query macro you just created. Then you can segment your data using this custom dim. Is this what you were after?

      Or did you want to do a check in your GTM tag, like “did the user come to the session via utm_source=advertising, if they did, fire tag X, if they didn’t don’t fire it”? If so, you’ll need to create a 1st party cookie for this, and then check for it with the cookie macro in the firing rule of tag X.

      • says

        Hey Simo,

        It was the latter option you mentioned, it sounds like I need to create a 1st party cookie for this stuff.

        Many thanks,

  5. says

    Hi Simo: I keep on coming back to this post… Ho hum… This caught my eye: “Element text: This returns the text content (if any) of the element that was clicked. It returns either the innerText or the textContent property (depending on what browser the visitor uses).” Can you expand on this comment? For example how would IE be different than Safari?

    • says

      innerText is not supported by Firefox, so GTM has to use textContent instead. There’s a slight difference in how they work, since textContent also returns text within <script/> and <style/> tags, for example. Also, innerText doesn’t return text nodes which have been hidden with CSS, whereas textContent does.

  6. Ade says

    Hi Simo, great post thanks.

    I have a quick question. I have setup a ” Data Layer Variable” type macro.
    And I am referring it in my custom html tag like this:
    var order_id = {{orderTotal}};

    But on the front end instead of getting value of that variable I am getting something like this:

    Do know what am I doing wrong?

    • says

      Hi Ade,

      That’s the function the macro translates to when the tag is written in the DOM. Where and how do you see the function “on the front end”? Are you inspecting the script in the DOM?

      If your variable has a value (so there’s been a dataLayer.push with that variable BEFORE the Custom HTML tag is run), it should resolve correctly to the value. You could test it in your Custom HTML tag with


      If the alert contains the value, your macro works.

      • Ade says


        Thanks for a quick response, I have to say using “alert” is a neat trick.
        I was just looking at the page source to debug.

        Anway, I get an “undefined” prompt from my alert box. I can only assume that DataLayer push is not working correctly.
        I think I will now have to go back and debug that part first.

        Please feel free to share any further tips you may have about this.

        Thanks once again.


      • says

        Undefined means that the macro doesn’t resolve. You have to make sure the push is done before the tag is fired. Remember also that variables are case-sensitive.

  7. says

    Really great guide. I used it to create a javascript macro that collected data from a form and sent it as an event label to Google Analytics upon submission. Very helpful!

    • says

      Yes, change {{url}} to {{url path}}. Right now you’re trying to match a URL (e.g. “”) with an exact match path (“/path”) so you’re not getting any hits. You’ll need to change the macro to something that has the path only, i.e. {{url path}} (or {{url pathname}}). If you don’t see a macro like that, you can create it by creating a new macro with

      Macro Type: URL
      Component Type: Path

      • Vijay Kc says

        I did like this:

        {{urlpath}} -> contains -> /abcd/1232/
        {{event}} -> contains ->
        {{element-id}} -> contains -> abcdid

        Macro Type: URL
        Component Type: Path

        But it is still not fired the GTM Tag and no hitting as well.

        Any idea?

      • says

        Well, just a screenshot will not do, I’d have to know more.

        Are you trying to capture a link click or just a click? If the former, why don’t you have {{event}} equals gtm.linkClick, if the latter are there nested elements in the markup that might screw with click capturing.

        Your rule right now is looking for:

        ANY click that occurs on an HTML element with id=”abcdid” on a page whose URL path has /abcd/1232/ somewhere in it.

      • Vijay Kc says

        Hi Simo,

        Thanks for quick reply.

        I am trying to get click only BUT when i click on that button GTM Tag didn’t fire.

        My firing rule is correct right?

        No idea why it is not firing when click on that button.

        How can i test it is firing or not? Can you give me some suggestion?

  8. Vijay Kc says

    Hi Simo,

    Thanks for quick reply.

    I am trying to get click only BUT when i click on that button GTM Tag didn’t fire.

    My firing rule is correct right?

    No idea why it is not firing when click on that button.

    How can i test it is firing or not? Can you give me some suggestion?

  9. says

    Hello Simo, thanks for such a thorough article!
    I need to implement a custom dimension for logged-in users via Google Tag Manager. As I understand, I should put in a macros for logged-in users in the Dimention field. So how can I create that macros? I didn’t find this in the post, sorry.

    • says


      There’s no “standard” way to do this, and it’s really mostly up to your content management system to provide you the means to do this. You see, most often login is a server-side function, and login status is thus maintained in a server-side variable. With a front-end solution like Google Tag Manager, you can’t access server-side variables directly, because that way it would be too easy to make some damage with JavaScript injection, for example.

      The way to get the login status is to modify the dataLayer declaration in your page template. So before the GTM container snippet you’ll need code that looks something like this:

      dataLayer = [{‘logged-in’: ‘<FUNCTION_TO_GET_LOGIN_STATUS>’}];

      The function would have to be something that your CMS provides. For example, if you use WordPress, you can write the login status into dataLayer with the following piece of code:

      dataLayer = [{‘logged-in’: <?php print is_user_logged_in() ? ‘true’ : ‘false’; ?>}];

      or something (and this needs to be in the PHP file of your template, not on any post or anything).

      Then you’ll need to create a data layer variable macro to store the value of ‘logged-in’ (it will be either true or false). After that, you can use this macro in any setting which requires info on whether the visitor was logged in or not.

      Plugins like Metronet Tag Manager (for WordPress) provide a direct UI for adding stuff like this into the data layer.

      • says

        Thank you very much for such a professional answer! Now I just need to figure out how to configure that for Magento.

  10. says

    What a thorough, precise, and thoughtful guide on GTM & macros.

    Refreshing to read a guide that is concise, precise, thorough, and relevant.

    Screenshot, description, use case. Yes!

  11. Antwan says

    Can i create a rule depend on {{DataLayer Variable}}
    1- dataLayer.push({‘xyz’:’okay’});
    2- Create {{Macro_xyz}} as dataLayer which is mapping to xyz
    3- Create Rule which has -> {{Macro_xyz}} equal okay
    4- if i will create a tag depend on that Rule it will work on all cases ?

    • says

      Hi Antwan,

      Take a look at this post:

      Every tag needs an {{event}} rule to fire. If you don’t specify one (e.g. you just use {{url}} matches RegEx .*), the tag will fire upon {{event}} equals gtm.js.

      So if you have a rule with just the condition {{Macro_xyz}} equals okay, this information has to be in the dataLayer BEFORE {{event}} equals gtm.js, i.e. above the GTM container snippet in the page template. Otherwise the data layer variable won’t be available by the time the {{event}} equals gtm.js condition is matched.

      • Antwan says

        SO Do you mean the rule should be 2 thing

        1- {{event}} equals gtm.dom.
        2- {{Macro_xyz}} equal okay.

        Is That will fire properly ?
        I appreciated .

      • Antwan says

        Second Option
        Or Create a Rule just on condition {{event}} equal okay
        and set dataLayer.push({‘event’:’oaky’}); inside the code

        So which option would be better


  12. Jesper H says

    Hi Simo!

    I really appreciate all your blog posts on GTM and I use them extensively! I have a question though. I am trying to implement no 10, Http referrer, but I don’t understand where I should input the code you posted, “if ({{referrer}} et cetera”. Could you help me out?


    • says

      Hey Jesper,

      I probably should have made this more obvious in the article, but the use cases are just examples of how to use the macros. Basically, whenever you see JavaScript code in a GTM example, there are two places you can use the code in: 1) Custom JavaScript macros, 2) Custom HTML Tags.

      So in the Referrer example, for instance, you could have a Custom JavaScript Macro which returns true if the page is a landing page, like:

      function() {
      if ({{referrer}}.indexOf(“”) >= 0) {
      return true;
      } else {
      return false;

      Or you can use it in a Custom HTML Tag (just remember to wrap it with <script></script>) if you want to do some other processing.

  13. Antwan says

    sorry because any tag didn’t show on the comment ,
    so i will add image tag at custom html tag so the image didn’t show inside the page source

    • says

      Yep, you can’t add tags to these comments unless you escape them using HTML encoding (&lt + semicolon for left bracket, &gt + semicolon for right).

      So you’re trying to add an image tag to the page using GTM? Should work. Are you sure you’re not accidentally wrapping the IMG tag in a <script> block?

      • Antwan says

        All i have inside the tag just the image tag without any wrapper.
        i can’t see the image on the page source but i can see it when i open Chrome Developer tool at the resource tab.

        So what should i do to be able to see it at the page source.
        By the way this image is just a tracking pixel


      • says

        I see. Well don’t worry, your image most likely is there. You won’t see it in the page source, since that’s just a static representation of the markup that is served to the visitor. Dynamic elements (such as those created / modified with JS) can be identified by inspecting the document object model directly (e.g. via JS console) or by using, for example, Chrome Dev Tools’ appropriate pane.

  14. Enzo says

    Hi Simo,

    very great post!!!

    I’m trying to track the div id’s in eventTracking. I tried with {{element ID}} but it doesn’t work.

    In GA Debug I actually see the ID as:

    Tracking click on
    [rlRfxEventTracker] id=typeswitch_RF

    Thank you in advance for the feedback :-)

  15. Antwan says

    i am getting an error at the console ” Uncaught #Object” at gtm.js?id=GTM-XXXXXX:49
    But not everytime sometime getting error sometime not getting
    and i can’t debug or even understand what is the error and the tag is firing


      • says

        In my experience, this error has popped up when there’s a fault in your Custom HTML Tag or Custom JavaScript, and it throws an error which isn’t handled by your script.

        I’ve seen it happen especially when loading a third-party JavaScript marketing library which uses document.write, and then there’s a <noscript> in the Custom HTML Tag as well. Removing the <noscript> fixed the error every time.

        So my advice is to go through the Custom HTML Tags you have, and disable each one-by-one to see which one causes the error.


  16. mittwoda says

    Hello and thanks a lot for your clear explanations.
    I met a problem that, in my GTM interface, there is no ELEMENT URL macro !

    So, I am not able to track with a link click listener.

    Is that a bug ?

    • says

      Nope, might be just that you have a container where either someone deleted it or it’s old enough to have existed before the macro was added as a standard macro.

      It’s easy to create, just create a new Auto-Event Variable Macro with name {{element url}}, and set its Macro Type to Element URL.

  17. Alessandro says

    Hi Simo,

    thanks for sharing. Great post and great blog as well.

    I’m trying to track a form submission and unfortunately I can’t use the FormSubmit Listener.

    The URL of the form page, the ThankYou page and the Error message page are the same so I can’t even use the URL of the ThankYou page as rule.

    I’m thinking to use the text in Thankyou message to identify my event.
    I have created a Custom HTML tag to extract the H3 of the page which can be “Thank You” or “Error”:

    var getH3 = document.getElementsByTagName(‘h3’);
    var giveH3 = getH3[0].innerText




    The alert works so I have created 2 different event tracking to differentiate two cases.
    I have tested the events using a macro “Constant String” and they worked as well.

    At the end, I have create a Macro “javaScript variable” passing the variable giveH3 as Global Variable Name but nothing. It doesn’t work.

    Can’t I pass a variable from a Custom HTML tag to the edit Macro?
    What am I doing wrong? Can you help me please?

    Thanks in advance.


    • says

      To store the variable as a global variable, prefix it with the window objexct, e.g. window.getH3 = …

      But a far easier way is to create a Custom JavaScript Macro:

      function() {
      var getH3 = document.getElementsByTagName(‘h3’)[0];
      return getH3 ? getH3.innerHTML : ‘n/a’;

      And just replace ‘n/a’ with whatever value / type you want to get if there’s no H3 available on the page.

  18. says

    Hi Simo,

    Thanks for a great article, bookmarked!
    i have a question which i haven’t really managed to figure out.

    I have a pixel tracking from a thrid party on my site that’s implemented with tag manager and set to fire on an event with the page load (there’s no unique url for the page where the tag should fire). I also need to make this tag fire only when the utm_source contains the tracking from the third party.
    I’ve tried using a url macro with query utm_source but the tag is fired after the landing page and by then the utm_source is gone. Any idea how i can solve this?

    thanks :D

    • says


      Unfortunately there’s no way to test for {{macro}} is of type undefined, yet. If it were, you could do a simple rule, where the tag fires only if

      {{url query utm_source}} is not undefined

      However, what you could do is create another macro, just {{url query}}, i.e. macro of URL / Query type without a query key specified. Then you could create a firing rule

      {{url query}} matches RegEx utm_source

      And the tag would fire only if utm_souce was in the URL.

  19. says


    I’m spent most of the weekend reading your stuff – really insightful even for a non-tech like me :-)

    I’m having a big issue getting an Event to fire on a Form (it uses Ajax and stays on same page) . I’ve tried gtm.formSubmit,, gtm.clickLink and tried {{element url}} equals (as per the bit above).

    Is there s definitive way of tracking these ?



    • says

      Hi John

      No, there isn’t a definite way of tracking these. The problem with single-page apps is that the default behavior of a submit event (usually redirect or page refresh) is cancelled and replaced with some JavaScript handling. This is often done in a non-GTM friendly manner, using a return false; or e.stopPropagation() calls, which do not pass the submit event to GTM’s listeners. gtm.formSubmit will work with a form where default behavior has been prevented IF it’s done in a way which doesn’t stop event propagation (e.g. e.preventDefault();). This will require some cooperation from your developers, so that they can rewrite the form script to accommodate for this.

      However, should work on the submit button. Sure, it doesn’t take into account invalid form submissions, but it’s better than nothing. gtm.linkClick works only if the submit button is wrapped in a link tag (<a />).

      The best way would definitely be to make sure the form passes the submit event to GTM’s listeners by allowing propagation all the way up to the document node. Tracking clicks on the submit button with or gtm.linkClick should work fine, but they don’t account for form validation.

      Here are a couple of articles:

      Explanation of event propagation and GTM:
      Advanced form tracking:

      • says

        Thanks Simo

        That sent me off in another direction! :-)

        I’m struggling to get any of the Element macros to be recognised..,classes, id etc

        It look so simple but just not happening. If you get a chance can you maybe point out to me the element values I could use ?

        I use ‘Inspect Element’ in Chrome to try and identify them



      • says


        Well, your contact form is doing exactly what I assumed it is: it’s not propagating the submit event OR the click event on the submit button. Thus the listeners will not catch submit or click events on the form, and there’s no {{element}} macro that you could even use. This will require your developers to help you out. It looks like you’re using jQuery to handle the form. That’s fine, but the clicks and submits must be allowed to propagate for GTM to be able to listen to the events.

        The other buttons, e.g. “request a call back” can be tracked with either a or gtm.linkClick listener and {{element classes}} contains avia_image (I’m assuming that the only elements with class avia_image are the call back images).

  20. Michal says

    Does the gtm form listener requires a page refresh after submission to launch all the tags and rules that he needs?

    I’ve got an event tracking set up that listens for a form submission to create gtm.formSubmit event. And then I have a gtm analytics tag that send the event to GA whenever the {{event}} equals gtm.formSubmit.

    Sounds good, right?
    Too bad that doesn’t work. I’m thinking it because the site does nothing after you click the submit button. It just scrolls you all the way up and gives you the thank you message.

    Feel free to check it out by yourself:

    • says


      Nope, a page refresh isn’t required. All that’s needed is a JavaScript ‘submit’ event which propagates all the way to the top of the document object model where GTM’s listeners are.

      The problem is that you have a LOT of custom script running on the page. You’ll need to ask your developers the following:

      1) Is there some script that handles the form submit event of this particular form?
      2) If so, is there a statement which prevents propagation of the submit event (either event.stopPropagation() or return false;)
      3) If so, could this be modified so that propagation is not prevented (using event.preventDefault() instead, for example)

      Right now all evidence points to the fact that something “hijacks” the form submit and prevents the event from ever reaching GTM’s listeners.

  21. says


    If we want to grab the ID ( of the parent of a parent element would this be valid?

    Data Layer Variable Name:

    example: (pasting in structure html)
    *div id=”whyBox0″ class=”whyBox third” style=”background:#eee url(/quiet.jpg) no-repeat center center;”*
    *div class=”whyOverlay” style=”height: 100%; min-height: 101px; background: url(/opacity-blue90.png);”*
    *h3 class=”font30 white” style=”font-size: 25px;”>Quiet</h3*
    *img src="/icon-plus.png"*
    *div class="font18 boxHeadline" style="display: block;"*
    *You can have a normal conversation while the disposer is running.*

    • says

      Sure, nothing wrong with that approach.

      However, traversing the DOM too far up or down is usually a sign of markup which doesn’t suit your analytics needs. It would be better to annotate the clicked element itself with enough detail, so you don’t have to go to parents’ parents with the selector. This can, of course, be a problem, since it requires a development effort. If so, then scraping the DOM for ancestors is fine as a bandaid.

  22. says

    thank you for great article.

    Could you help me to solve my confusion?
    I managed to collect some user data in datalayer and access variables via macros in GTM (Iam collection userID, groupID, user_signed: true/false)

    Iam confused how to set-up CUSTOM DIMENSION in GA.

    What “Scope” should I use for user data above?
    (userID varies for logged in user and for guests is set to ‘0’)


    • says


      How you set up your custom dimensions depends completely on how you want to analyze your data.

      For example, user_signed should probably be a session scoped dimension, since I would imagine you’d want to analyze sessions by users who were signed in vs. sessions by users who weren’t.

      It’s impossible to give a generic answer to your question, since everyone has different needs for tracking across scopes. It’s also impossible to give a specific answer to your question, since I have no idea what you want to do with userID, groupID, etc.

      It all boils down to how you want to analyze your data. You might want to look up some basic guides for how (and why) to use Custom Dimensions in the first place. For example:

      Sorry I can’t be more specific, but your question is very particular to your own tracking requirements.

  23. Tom says

    Hi Simo

    Really interesting reading. I have a variable that is either a string, or undefined. I’m trying to create a rule to test for undefined, or not. But never seems to work unfortunately :( Do you have any experience of trying this?



    • says


      Yeah, there’s no test for undefined. The best way is to use the Default Value option provided by many tag types, and then test for {{variable}} does not equal (default value you chose).

      If there’s no Default Value, you’ll need to use Custom JavaScript:

      function() {
      return {{variable}} || “empty”;

      This will return the variable if it has a value, or “empty” if it’s undefined.

      • Tom says

        Thanks Simo! I had to play around a bit because I could only make changes in GTM not in the data layer. Here is the code in case it’s useful. If the variable is undefined or null then it explicitly returns NONE. Otherwise contains the actual variable so in theory the actual data.

        function() {
        if ((typeof == ‘undefined’) || ( == null)) {
        return ‘NONE’;
        return ;

    • says

      You can’t use JavaScript (since it’s client-side) to directly access PHP variables (since PHP is server-side).

      You can, for example, add the $_POST variable to dataLayer in a push, and then access its value with a Data Layer Variable Macro.

  24. Yuet Chow says

    Hi Simo,

    Is the Query component type of URL macro type case sensitive? My tests seem to suggest so but I could be wrong.
    If it is, do you have any suggestion implementing a case in-sensitive query macro.


    • says


      Yes – it is case-sensitive. Here’s a Custom JS Macro that retrieves the value of the given, case-insensitive query param. You will need a URL Query macro {{url query}} (no key) for this to work.

      • Yuet Chow says


        This works beautifully. Thanks.
        However, I realized I will have to repeat this same javascript for every case-insensitive query key I need. And your solution help to morph into another solution which seem to work with some tests I did but would appreciate greatly to run through you.

        Create a Custom JS Macro to convert the full url to lower case {{url}}.toLowerCase()

        In the normal URL macro specify the Query key in lower case and specify URL Source in More settings section to use the lower case full url in the earlier custom JS macro.


      • says

        Ooh, yeah, .toLowerCase() is FAR more elegant than using RegEx here, since you’re only looking for specific pattern matches. Excellent idea! Might have to steal this one for a #GTMtips post (with due credit to you, of course) :)

  25. Yuet Chow says

    Hi Simo,

    Thanks for your reassurance. I am new to GTM and your blog has been of tremendous help to me. Thank you.

    There is a flaw in my earlier blanket lower casing the url. The data values get forced to lower case too which is not ideal. I have modified your function to parse through the query and only lower casing the parameter name (str[0]) and reconstruct the url.


  26. Aaron says

    How would I create a macro that grabbed the path of the url that a user is clicking to?
    This is what I came up with:

    Macro Name- element path
    Macro Type URL
    Component Type Path
    URL Source {{element url}}

  27. says


    Is the function google_tag_manager[“GTM-W92WQQ”].macro(8), still available?
    I try to access this from the console, replacing with my own container ID and testing different values for the macro parameter, but I get no value.


      • says

        The string is actually ‘gtm’ + timestamp when the macro was written, so it wouldn’t really do you any good to find out what it is, because the string changes every single time the macro is invoked. Since it’s a parameter call and there’s no interface to query for available values, there’s no way of polling for all existent macros the same way you could before (just by looping from 1 to 100 or something).

        But the Debug Panel now tells you the state of every macro with every Data Layer interaction, so I wonder why you’d want to know exactly how to call the macro() function?

  28. Jesus says

    Hi Simo, excellent post (and blog)!
    We’d like to use GTM to include tracking pixels in our website.
    Currently we are using GTM to include static pixels (with not dynamic data).
    For pixels that require dynamic data, for example, in a 3 step form we want to send in step 2 name and surname that were required in step1. In step 3 we’ll send price and postal code required in step2.
    We are creating these pixels from server side to include all the data, but we’d like to take the pixel implementation out from our source code.
    Our idea is put all the data in javascript variables or in input hidden fields and then, use GTM macros to get the data.
    Not sure if it’s the best approach or maybe there is a better solution for this use case.

    • says

      Best way is to render the form values in the dataLayer on the subsequent page.

      So if in page 1 the user writes his or her name “John Smith”, then on page 2, your server-side script writes the dataLayer with:

      ‘name’ : ‘John Smith’

      Then you can grab this value using a Data Layer Variable and include it in your tag.

      The other options would be to use a cookie or Local Storage.

  29. says

    Hi Simo,
    great Post, it help me a lot in last few days.
    i am getting a problem, as i have defined a event on the div which contain an icon and some text in span. when i click on the empty area on div event fire correctly. but when i click on the child element (icon or span) it fire a the event with the “undefined”. why is that?

    • says

      Hi lalit, I’m not sure I follow. Where’s the “undefined”? What’s the macro you have in the field which now contains “undefined”? What’s this “event” you’ve defined, some listener?

      I need to get the terminology straight before I can help :-)

  30. says

    Hi Simo

    Great blog. I’ve been referring to it for some time now. I used your post on Forms and macros to capture info in form text fields, radio buttons and drop-downs. I’m so glad there’s someone like you around writing clearly and responding to queries.

    I’m a little bit stuck with reading data from a page that has just loaded.

    The site doesn’t have element IDs but does have classes. I’ve tried using a custom javascript macro (adapted from the form example) to read the info in an with class=”quote” and send it to a GA event but it’s not working:

    function() {
    var quoteAmount = document.getElementsByClassName(“quote”)[0];
    return quoteAmount.value || “”;

    Are you able to help?


    • says

      All good. Have worked it out. The code below works. It only took me 12 days…

      function() {
      var quoteAmount = document.getElementsByClassName(“quote”)[0];
      return quoteAmount.innerHTML;

  31. says

    Hello Simo –

    We are setting up a modal form tracking with Tag Manager using the DOM Element approach. The only issue is that forms Element ID is automatically generated (based on the page’s control structure). So the element ID could be ctl24_ctl00_PanelThankYou, ctl25_ctl00_PanelThankYou or ctl26_ctl00_PanelThankYou

    Can the DOM element macro be set based on the end of the element ID? For example, if the element id ends with “PanelThankYou” then use the firing rule, it will be more reliable.

  32. says

    Hi Simo,

    Thanks for great info. As i’m not skilled about tag manager i can not solve my problem with the custom js. In my case i can not get the data with a data layer, thus i have to use javascript variable macro option.

    But i can not understand how the macro will work. For e.g i have product price info in html as: ‘price’ , quantity as ‘amount’ etc.. How the macro should be created to be able to get that value from the code? I will use the info for abandoned cart marketing therefor i need to pass it to the rejoiner script that is fired in cart pages.

    I will be greatful if you can explain the way i should go with an example.


  33. Ammiel says

    Hello Simo,

    In one of your posts you are saying
    “Undefined means that the macro doesn’t resolve. You have to make sure the push is done before the tag is fired. Remember also that variables are case-sensitive”

    My question is : how to make sure the push is done before the tag is fired ? What is the adequate GTM rule ?
    I setup {{element classes}}, {{element id}} and {{element txt}} macros but they are always displaying “undefined” value in GA….


    • says


      The absolute easiest way to do this is to always push an ‘event’ key with every dataLayer.push() you have that has information for tags. For example, this push():

      ‘event’ : ‘pushDone’,
      ‘userId’ : ‘12345’

      would 100% sure have the data layer variable ‘userId’ available for any tag that fires with the ‘pushDone’ event.

      As for {{element classes}}, etc., they will display undefined if no auto-event is triggered or if the element that was the target does not have the corresponding attribute (class for classes, id for id, text content for text). Auto-event tracking works so that once a click, for example, is registered, a dataLayer.push() is automatically made with prepopulated values for the {{element *}} macros. If your tag the fires on {{event}} equals, these macros will carry the value to GA, providing the element that was clicked has attributes for these macro values.

  34. says

    Hey Simo,

    Great post, I have one problem. I am trying to track external campaigns using google tag manager. I have set up the variables to capture the value of utm_source and utm_medium queries. And i am sending this values using a tag using a field to set parameters.

    But the problem i have is that universal analytics is not collecting any of these values. I have nothing in the campaign section.

    Thanks in advance

      • Jake K says

        Simo – I’ve looked at a few sites including yours here where you mention being able to set the campaignSource. I’m trying to get the Source dimension in Analytics to update with the value I’m setting for the campaignSource field. I wasn’t seeing anything and updated my tag to include the campaignName and campaignMedium, however, my source in Analytics is still showing as (direct) not my predefined value. I am not sending a utm_source parameter in the URL, instead want to set it based on whether or not a string exists in the URL (i.e. the name of one of my parameters that is unique to me.)

      • says


        If you have campaignName, campaignSource and campaignMedium (all three are required) in your tag, it will override the source/medium/campaign for all hits with the tag, 100 % sure. You can test it with string values (so not Variables or macros, just write text into the field value) and check Real Time reports to see the traffic source.


  35. says

    Hi Simo,

    Wow so what do we do about
    “innerText or the textContent property (depending on what browser the visitor uses).”

    Do we have a catch all solution?

    • says

      What do you mean? :) The way the {{element text}} is constructed means you don’t have to worry about cross-browser issues. GTM chooses the one which works for your browser.

  36. Ana says

    hello Simo , I created a custom html tag to measure clicks on the menu on my blog each menu item , pick up your child link what macro advice?

    • says


      You didn’t give any information about how they use the ‘+’ in the URLs, but you can just create a search-and-replace filter in Google Analytics to remove all ‘+’ symbols from the Request URI. If you want to modify the data in the source, you can override Document Path with a Custom JavaScript Macro that returns the {{url path}} String with all ‘+’ symbols removed.

  37. Roland says

    Hi, Thanks for this helpful post. I have a question/problem when I try using the custom javascript in a macro, for example where you have {{element}}.innerText, when I try to preview the container I get an error saying that element is undefined.

    • says

      Hey Roland,

      It means you’ve either created the container before auto-event tracking was introduced, or you’ve deleted the default {{element}} macro at some point.

      Just create a new macro of type “Auto-Event Variable”, set its variable type to “Element” and name it “element” (without quotes).

  38. says

    Hi Simo,

    I have set-up an event trigger for the “Contact Us” page.

    The unique tag I have created for the Contact Us page includes a firing rule to only fire when URL = But Google Analytics now reports a total that includes both visits and actual “submits” because when a visitor submits the form the page URL remains the same: Example –

    Event Category = Contact Us Event (10 total events)
    Page title = Contact Us | United Networks (7 total events)
    Page title = Status page (3 total events)

    The only way I can differentiate between visits and “submits” is to view the secondary dimension “page title”. How can I update my tag so it only fires when a user clicks the submit button?

  39. Jim Williams says

    Simo – I refer to this post al the time. I have just set up a 1st Party cookie Macro to pass the value of a cookie as a custom dimension. However – just relaised that the cookie value begins with t= then a unique id. i.e t=123456789 I just want to use the number string 123456789.

    Is there away to strip out the first 2 characters i.e. the t= so that the macro just passes the numbers?


    • says


      You’ll need to create an intermediate Custom JS Macro:

      function() {
      return {{cookie macro}} ? {{cookie macro}}.split(‘=’)[1] : undefined;

      This will return whatever comes after = in the value of the cookie.



Leave a Reply

Your email address will not be published. Required fields are marked *

Please do not write HTML or other formatted code in your comments!