Tips to track an ajax based website using GTM and Universal Analytics

I’ve been dealing with some Ajax based website tracking these past months (mostly AngularJS based ones), and I’ve learn a lot of stuff , mostly some important points about how Google Tag Manager does work.

Usually tracking an Ajax based website is a PITA, since mostly we don’t have page reloads as all the content is loaded asynchronously. So either you ask the developers to push you the content loaded info into the dataLayer or you play with the history location.

Still we need to have some points in mind while tracking those sites with Google Tag Manager (and maybe natively or using any other tag management system).

1. Use a fixed tracker name

The way Google Tag Manager works, everytime a Google Analytics tags is fired it creates a new tracker with a name (“GTM”+Current timeStamp ). This is not a problem for most tracking implementations, but we need to take in mind that websites based in Ajax usually changes the current URI without even needing to reload the page, so it may happen that if we’re using the campaign tagging we may ending having a lots of unneeded visit respawn and referrals.

Let me explain this with a simple example: has a link to our site and it’s using the campaign tagging to allow us to track their visits to us.

So the visit will end in our page with the following values:


Let’s map this to Universal Analytics parameters:


Universal Analytics uses the “dl” parameter  (document.location) and dr(document.referrer), to calculate the visit attribution. It will search for gclid, utm_ parameters in the “dl” parameter value (in that order of preference) and if it doesn’t find them it will try to calculate the attribution from the “dr” string.

As we have the the utm parameters in our landing page, the referrer won’t be taken in mind to calculate the visit attribution, so we’re ok at this point.

source: test_source
medium: test_medium
campaign: test_campaign

But if the visitor decides to go to another page, and like we said in this case there will be no page reload  Google Tag Manager will spawn a new tracker and we’ll end having those values.


As we said before if there’re no utm or gclid parameters in the document.location Google analytics in going to look to the document.referral string and it will find a different domain than the current loading one, so it will track a new visit:

medium: referral
campaign: /

D’oh. This is wrong, we’ll miss the original visit attribution, plus we’ll have some extra ghost sessions in our data.

Best way to do this (even if it’s not recommended) to set a fixed tracker name in our tags.


This way, the same tracker instance will be used everytime and the original dl parameter will be kept.

2. Force the Non-Interaction Flag to false in your pageviews

This is another important point to have in mind. Google Tag Manager uses the set command to set the values instead of setting them directly to hit. So if we fire and non-interactive event tag, the subsecuent hits will inherit that flag making the subsecuente pageviews to have the “ni” parameter attached to them.

Let’s how Google Tag Manager is going to translate a single Event tag:

ga("create", "UA-40180485-1", {"cookieDomain": "auto","name": "myTracker"});
ga("myTracker.set", "&gtm", "GTM-MW3DNR");
ga("myTracker.set", "nonInteraction", true);
ga("myTracker.send", {"eventAction": "test","eventCategory": "test", "eventLabel": "test","eventValue": undefined,"hitType": "event"});

As you can see the nonInteraction is globaly for the tracker, and this will affect our next pageviews (remember that we don’t have any page reload so the nonInteraction flag will be kept till we disable it.

An easy going fix for this without needing to deal with hitCallback/eventCallback is to automatically remove the “ni” parameter for all pageviews as it’s not likely going to need it (ever used a non-interactional pageview?).

Let’s see how can we achive this. We can’t use undefined string on GTM input boxes as it will be treated as a string, therefore we’re going to create a new Variable that is going to return us the real undefined value.


ow we can safely force the “ni” parameter to undefined in our pageview tags this way:


Did you fance any other problems when tracking ajax based sites?, share them in a comment 🙂

Greets: Thanks fly one more time to Yehoshua Coren from Analytics Ninja, for reviewing these points with me.


  • I think i be missed something – why Google Tag Manager will spawn a new tracker when the visitor go to second page?
    If your page isn’t reloaded, then GTM will not run again so the pageview will not fired again, isnt it?

    • Because that’s the way Google Analytics tags work in GTM. If you don’t define a tracker name for your tags (what is the recomended way to do it), everytime they are fired a new tracker initialization is performed with a semi-random tracker name ( “gtm”+{{current_microseconds}} ) .

  • How many pageview trackers do we need to setup? Do we have just one with the field sets at the end of this article? Or, do we have a traditional tag and this tag?

  • How is {{setDomainName}} set in this example? Is that simply a placeholder for the domain this code is being implemented on?

  • Great article, thanks for these insights!

    I successfully implemented the solution for our client. I’ve a question about the why:

    “But if the visitor decides to go to another page, and like we said in this case there will be no page reload Google Tag Manager will spawn a new tracker and we’ll end having those values.”

    My question: Why and when does the GTM spawn a new tracker. Is this only the case for Ajax / Angular websites? Or is this alsways the case when you work with virtual pageviews?

    Thanks a lot!

  • Hi David !

    Thanks you so much for this post !
    However I have a simple question about the tracker name. Should the tracker name be the same for every Universal Analytics tag ?
    Or just be set differently for each of them ?


  • Hello, I am facing a Tracking issue after Implementing Google Tag Manager. My Google/CPC orders are getting tracked as Google/Organic.

    Please suggest something for the same

  • Hi David, thanks for this great article.
    I just go into an Ajax page website which is a Drupal 8 with JSONAPI for every page load.

    It comes with a dataLayer for every page load BUT I cannot get that as there is NO trigger for the page load. (there is a gtm.historyChange event BEFORE the dataLayer)
    And, the dataLayer will accumulate from the very first page.
    I’ve tried to google it BUT your article is the closest one.

    It will be great if you have any idea about that.
    Thanks alot.

  • Hi — thanks for the article! If we are tracking multiple subdomains, is the idea to use the same custom tracker name across all pages, or only the AJAX/SPA app? What are the negative ramifications of doing this given it is not the recommended approach?

  • Hi,

    Thanks for this article!

    I have a question about this: “But if the visitor decides to go to another page, and like we said in this case there will be no page reload Google Tag Manager will spawn a new tracker and we’ll end having those values.”

    What is the difference when pages reload? Doesn’t the location change when the page is reloaded and isn’t a new tracker generated even then?


  • Hi so great fix! it solved my issue of my paid traffic’s sessions breaking into 2 once they visit another page in the website.
    However, this doesn’t seem to solve the issue for cross domain tracking once a user hits a button that takes him to the checkout process on a different website- the session breaks into 2 and the traffic is attributed to organic.

    Does anyone have a suggested solution?

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.