• GTM Courses
  • Blog
  • Services
  • Resources
    • Youtube channel
    • E-books and Guides
    • GTM Recipes
    • View All Resources
    • GTM Community
  • About
    • About
    • Contact
  • GTM Courses
  • Blog
  • Services
  • Resources
    • Youtube channel
    • E-books and Guides
    • GTM Recipes
    • View All Resources
    • GTM Community
  • About
    • About
    • Contact

May 30, 2020

How To Track Single Page Web App with Google Tag Manager

Updated: May 30th, 2020. Tracking page views on regular websites is fairly easy: you just add a tracking code to every page and done! Every time a visitor clicks an internal link, a browser window refreshes, and a new page view hit is sent to Google Analytics.

But when it comes to single page websites (or web applications), the default tracking stops working. Regardless of what does the user/visitor does on your website, only one page view is tracked (the first one). Does this situation sound familiar?

Why does this happen? Let me show you.

In this blog post, I’ll explain how to track Single Page Web App with Google Tag Manager (including SP websites). The blog post is extensive so get ready.

 

Table of Contents

+ Show table of contents +

  • What is a Single Page App / Website?
  • Install Google Tag Manager
  • Multiple ways of tracking SPA + How to read this guide
  • Do you need this guide at all?
  • Option No.1 – History change trigger:
    • Does the page URL change (when you navigate from page A to B)?
    • Let’s find out whether History Change listener will help
    • Do you see History events in the GTM Preview and Debug mode?
    • When do you navigate from Page A to B, how many History events do you see?
    • Do any of History variables in GTM return useful data?
    • Track Single Page Application with Google Analytics tag
      • If URL change involved URL Fragment (#)
      • If URL change involved URL Fragment and the link might contain Query Parameters
      • If New History Fragment variable is empty and you’re using New History State
      • Trigger
  • Option No.2 – Track Single Page Web Apps with Google Tag Manager and developer’s help
    • Create variables
    • Create a trigger
    • Update the GA Settings Variable
  • Check the reports
  • Rogue referral issue
  • Final words

 

What is a Single Page App (SPA) / Website?

Single Page Application (SPA) is a web application or website that loads all of the resources required to navigate throughout the site on the first page load. As the user clicks links and interacts with the page, subsequent content is loaded dynamically (and the page technically does not reload, even though it might look differently).

The default Google Analytics tracking works well with traditional websites because the snippet code is run every single time users load a new page.

However, in single-page website/app, all of the necessary code is loaded once and the page does not reload during the entire user session. That is why you see only one page view in your GA reports. The URLs might change but that is just to give the perception of separate pages.

A lot of web tracking tools won’t work as expected with SPAs because they’re written for traditional websites where the tracking codes can detect when a new page loads.

But everything is possible and you can track such pageviews with Tag Manager as well. Continue reading and I’ll show you how deep the rabbit hole goes.

 

Install Google Tag Manager

If you haven’t yet, install the Google Tag Manager to a single page web app or website. I have published a blog post with several options for your consideration. Also, check whether your Google Tag Manager is working properly.

Done? Great, let’s proceed.

 

Multiple ways of tracking SPA + How to read this guide

Spoiler: there are two techniques explained in this guide, working with a History Change listener (within GTM) and asking a developer to push pageview data to the Data Layer every time a visitor navigates from one page to another. The first half of the blog post is dedicated to History Change (and finding out whether it will work for you) while the 2nd half will explain how to work with a developer.

But before we jump right into the configuration part, first we need to evaluate the context and see find out which tracking option to choose. Just like in my other popular blog post about the form tracking, to make complex things a bit more understandable, I’ve created a flow chart which can help you decide which tracking method to choose (or, in many cases, there will be only one method possible for you).

If both options are possible for you, you should go with the developer’s help + dataLayer.push (as it was proven to be more robust). Continue reading.

First, let’s take a quick look at it, and then we’ll dive deeper into each step.

IMPORTANT: In October 2020, the preview mode in GTM has changed. Hence the very first step in the workflow (below) has changed a bit as well. Preview mode no longer disappears. Instead, start the flow (below) with the following question: “When you navigate from page A to page B, do you see the new page loaded in the GTM preview mode?

google tag manager single page app

 

Do you need this guide at all?

The first diamond in the flow chart above asks a question: When you navigate from one page to another (within the same website), does the GTM Preview and Debug mode quickly disappear and then reappear?

I know, this question might sound stupid, but just to be 100% sure I want you to think whether this guide is exactly what you need.

What does this question mean? By default, Google Analytics pageview tag fires every time a page is loaded (meaning that the page must load completely from scratch). If you navigate from one page to another, page B is loaded from zero while page A is gone.

Together with the page’s code, the Tag Manager JavaScript snippet is also loaded from scratch. So if you navigate from page A to B, the GTM Preview and Debug console (if it’s enabled) disappears together with Page A (because the page is now abandoned), and then it is loaded from scratch on page B.

So if on every pageview you notice that the GTM Preview and Debug console reloads as well, you’re not working with the Single Page Application. Read my GTM guide for beginners, instead. I’ve explained there how to track your first GA pageview with Tag Manager.

On the other hand, if you’re really working with an SPA, every time you navigate from page A to page B, the Preview and Debug console stays as it is and continues tracking your interactions (e.g. clicks), but not pageviews. Why?

That’s because in web tracking, “pageview” usually is when the page document is loaded and JavaScript tracking codes are loaded together with it (including Pageview tags). However, in SPA, all the subsequent “pageviews” happen dynamically, without loading/reloading the page document. That’s why by default, neither Tag Manager nor GA track such “dynamic” pageviews.

Because of this, you need to do some additional configuration (and maybe even with the help of a developer).

Alright, so the first question is answered. If you are indeed working with an SPA, let’s move to the next question in our flow chart.

Does the page URL change (when you navigate from page A to B)?

If the URL does not change, you will have to ask the developer’s help. Immediately skip to chapter “Track SPA with GTM and developer’s help” of this blog post and continue reading from there. If the URL indeed changed, this means that you still have chances to use the History Change listener on your own.

URL changes might look differently based on a website you’re on. On some websites, the URL fragment changes (that’s the part of the URL after the hashmark #). On others, the URL change might look casually, like on a regular website (e.g. from /home/ to /home/contact-us), etc.

If you confirm that the URL changes, let’s move to another step.

 

Find out whether History Change listener will help

First, you need to have enabled at least one History Change Trigger on a page. Theoretically, it should be able to catch such URL changes. When the trigger is enabled on a page, it activates the History Change listener, a function (more like a set of functions) that is lurking somewhere in the background and waiting for a moment when the URL dynamically changes.

Once the listener spots the change, it pushes the History Event to the Data Layer (and you will see it in the Preview and Debug mode).

To enable a trigger, go to Triggers > New and enter the following options.

google tag manager single page app

Next, head over to Variables. Google Tag Manager offers a bunch of built-in variables related to the History Change Trigger. In the Variables section, click Configure (under the Built-in Variables) and enable all the History-related variables.

google tag manager single page app

Proceed to another question from the flow chart.

 

Do you see History events in the GTM Preview and Debug mode?

Now, let’s enable Google Tag Manager Preview and Debug mode and see whether the listener works. An orange notification bar should appear in your account.

Preview and Debug mode

Once the preview mode is enabled, navigate to the site where the container is implemented and you will see a debug console window at the bottom of your browser showing detailed information about your tags, including their firing status and what data is being processed.

Click (or scroll) through various sections of the single page web app (or website) to make the Page URL (in other words, just browse your single-page app/website). After the change happens, take a closer look at the event stream in the Preview and Debug console.

Did the History event appear? If yes, that’s good news! If not, skip to the chapter where I explain how to cooperate with developers. You will have to cooperate with developers.

Every time a visitor navigates to a certain section of your app/website, History event will fire. This is a perfect trigger to fire the Universal Analytics Pageview Tag.

 

When you navigate from Page A to B, how many History events do you see?

The reason why I asked this question is that the answer isn’t always 1. You should expect 1, however, sometimes Single Pages Applications are coded in a way where the History listener catches two or more events. For example, I’ve seen some cases where a visitor navigates from /home/ to /pages/contact/.

As a result, two History Events happened:

  • One tracked the change from /home/ to /pages/ 
  • And the other one captured the change from /pages/ to /pages/contact/.

This means that with Tag Manager, you would have tracked two pageviews instead of one. Not accurate.

Another example was in a project where I saw the same History events appearing multiple times. Every “pageview” resulted in 2 or 3 History Events. Not accurate either.

In that case, if you use the History Change listener without any modifications to your History trigger, your data will be bloated and unrealistic.

You have three options here and two of them involve a developer.

  • Option A: Consult with a developer and ask whether it’s possible to fix the double or triple-history event issue by reducing it always to 1.
  • Option B: Once again, skip to the chapter where you have to implement this tracking together with a developer.
  • Option C: Try to filter out some of the History Events.

 

Do any of History Variables return useful data?

Since there are several ways how a URL can be dynamically changed (Simo has mentioned that) there are different ways how you can retrieve the history information about the page that was just loaded. Let’s take a look at two examples.

When the History event appears in the GTM Preview and Debug console, click it and go to the Variables tab. Scroll down until you see History-related variables (if you don’t, you need to enable them in the Tag Manager Container by going to Variables > Built-in Variables > Configure).

What do you see?

Does the New History Fragment contain some data? If the URL change involves the hashmark #, then this variable indeed should return some value. That’s great! We’ll later need to use that variable in the Google Analytics Settings Variable.

google tag manager single page app

If the URL (with #) changed but the New History Fragment variable returns undefined, try this solution.

If the URL change does not involve URL Fragment (#), then check another variable which is New History State. Does it contain some information? An example could look like this:

google tag manager single page app

While this is a good sign, we don’t need all of its value. We’ll just need one key, path. However, the built-in GTM variables don’t allow you to access that particular parameter, therefore, we’ll need to create a Data Layer Variable with the following settings:

We’ll need this variable in a minute.

However, if no History Variables return useful data about the page to which a visitor has navigated, consult with a developer or skip to this chapter.

Honestly, I haven’t seen a situation where no History Variable contained something useful, maybe it’s even impossible. I am not sure 🙂

 

#7. Use History Listener to Track Single Page Application with Google Analytics

Alright, so now we have arrived at the step where we will combine everything together and will send those pageviews to Google Analytics.

 

If URL change involved URL Fragment (#)

Normally, Universal tag fetches the value of the Full Page URL automatically and transfers it to Google Analytics servers. But if your URL changes involve the hashmark (#), Google Analytics will not catch it by default.

google tag manager single page app

We’ll need to do some additional configuration in the GA Settings Variable in order to send URL Fragment value over to Google Analytics (that’s why it was important to check whether History variables contained some useful data).

Open your GA Settings Variable (which is used by your Universal Analytics tags). Go to More Settings > Fields to Set > page and enter {{New History Fragment}}.

google tag manager single page app

 

 

If website’s/app’s address contains not only URL fragment but Page Path as well (e.g. https://www.example.com/category1/product2/#contact-us), the solution I’ve described above will only send contact-us to Google Analytics and category1/product2 will be ignored.

So if your single-page website/app address contains not only URL Fragment but also Page Path, you should update the Universal Analytics settings to this (either in all Google Analytics tags or in the GA Settings Variable):

  • Instead of page >>> {{New History Fragment}}
  • Enter page >>> {{Page Path}}{{New History Fragment}}

google tag manager single page app

In both ways, we tell Google Analytics to ignore the Full Page URL value it fetches by default and use the new value of page that we are sending.

 

If URL change involves URL Fragment (#) and also might contain Query Parameters

In some single-page applications, it’s possible that URLs might also include some query parameters (e.g. example.com/?q=product#search). If that’s the case for you, you could pass an additional variable with the page field to Google Analytics. That variable will return all the query parameters that are present in the URL.

Create a JavaScript Variable with the following settings window.location.search.

Then go to the Google Analytics Settings Variable and insert this variable before the Fragment Variable. Final result: {{Page Hostname}}{{js – window.location.search}}{{New History Fragment}}.

P.S. if the only possible query parameters in your SPA are UTM parameters, then don’t worry. There is no need to additionally send it in a page field. GA will take care of your traffic attribution in another way.

 

If New History Fragment variable is empty and you’re using New History State

As I have mentioned in one of the chapters before, it’s completely possible that the New History Fragment variable will have no value. If that’s your case, you need to dig deeper into what is present in the Data Layer. Here is an example of one History Event in the Preview and Debug mode. After I clicked the History event, I switched to the Data Layer tab.

google tag manager single page app

As you can see, gtm.oldUrlFragment and gtm.newUrlFragment are empty. But there is plenty of other useful information, for example, one of these two:

That’s why we should create a Data Layer Variable that accesses one of those values in the Data Layer. I have no preference here, so it’s up to you which one to choose. If you go with the gtm.newUrl, create the following Data Layer Variable.

If you decide to go with the submitUrl, you need to enter the entire path from the top level to the bottom. In this case, it would be gtm.newHistoryState.props.submitUrl (remember, it’s case-sensitive).

Use it in the page field (of your Google Analytics Settings Variable), instead of {{Page Path}} and {{New History Fragment}} (and query parameters if you entered them).

If you gave your DLV a different name, then that other name will be visible in the Value field of the screenshot above.

 

Trigger

If you have thoroughly followed this guide, you already have created a History Change trigger that listens to all events. If yes then keep it as it is. If you haven’t created it, do it now.

Assign the History trigger to your GA Pageview tag.

You might also need to assign the default All Pages trigger to the Google Analytics Pageview tag if the History event does NOT appear in the Preview and Debug mode when the initial page load happens.

What does this mean? In some projects, I’ve seen a History event occurring in the GTM debug console together with the initial page load.

First, you see Page view, then DOM Ready event, and then History. If that’s your case, you should be using only the History Change trigger in your page view tracking (and avoid the All Pages). Otherwise, the first page view would be tracked twice, 1 time because of the All Pages trigger and the 2nd time because of the History event.

Now, save the Universal Analytics tag, refresh Preview and Debug mode, and try interacting with various parts of a single page app/website. Every time you navigate somewhere, a Universal Analytics Pageview Tag should fire.

Don’t forget to check the data in GA Real-time reports.

 

The Alternative Option: Track Single Page Applications with Google Tag Manager and Developer’s help

Welcome to the 2nd large chapter of this guide. Most likely, you’ve reached this point because your History Change tracking attempts failed or you are simply curious to learn more.

If (for some reason) History Change trigger isn’t working for you (or there was another reason which brought you to this section), there’s another option on how to track single page web app with Google Tag Manager. Ask a developer to activate a dataLayer.push code whenever a user navigates between pages/states of a website/web application.

Here’s a sample code snippet that the developer could use:

<script>
 window.dataLayer = window.dataLayer || [];
 window.dataLayer.push({
 'event': 'Pageview',
 'pagePath': '/something/contact-us',
 'pageTitle': 'Contact us' //some arbitrary name for the page/state
 });
</script>

Note: The  ‘pagePath’ and ‘pageTitle’ parameters (in that code snippet) should be dynamically changed to the address (and title) of the page a visitor is currently viewing. A developer should take care of that.

Anyway, what does that code mean?

Every time a user goes to a particular section of your page, a developer should activate that little piece of JavaScript code. It indicates that a “Pageview” has occurred and the new page URL is /something/contact-us. If the page address contains some query parameters that are important to your reporting (excluding UTM parameters), ask a developer to include those parameters in the pagePath key as well.

Anyway, you should use this dataLayer.push as a triggering condition (that activates the GA Pageview tag) and then send the title and page path (page over to GA.

To achieve this, complete the following steps:

  • Create two variables (and include them in the GA Settings Variable)
  • Create a trigger (and assign it to the Universal Analytics Pageview tag).

 

Variables

  • Title: dlv – pagePath (dlv stands for “Data Layer Variable”)
  • Variable type: Data Layer Variable
  • Data Layer Variable Name: pagePath
    This variable will read the value of the pagePath that was pushed to the Data Layer by a developer.

Create a 2nd variable:

  • Title: dlv – pageTitle 
  • Variable type: Data Layer Variable
  • Data Layer Variable Name: pageTitle

 

Trigger

  • Title: Custom – Pageview
  • Type: Custom Event
  • Event name: Pageview
  • This Trigger fires on: All events

 

Update the GA Settings Variable and Universal Analytics tag

Go to the Google Analytics settings variable that you’re using in your SPA’s GTM container and include the pagePath and pageTitle variables.

  • Fields to set: page = {{dlv – pagePath}} and title = {{dlv – pageTitle}}

After that, go to the Universal Analytics Pageview tag and add the recently created trigger, Pageview.

 

Check the reports

Once you implement one of these options of single-page app/website tracking, it is crucial to check the GA reports. First of all, start with realtime reports.

On the left side of the GA interface, click Realtime > Overview. You should be seeing your own pageviews there. If you can’t read this troubleshooting guide.

Once you start seeing your own pageviews, you’ll need to wait for a while (from 10-20 minutes up to 24 hours) until you start seeing those pageviews in standard reports as well. By saying “Standard”, I mean Behavior > Site Content > All pages.

 

Rogue referral issue

Working with a single-page application and sending virtual page views to Google Analytics might mess your referral data up. Simo Ahava has posted a solution on how to fix the rogue referral issue. So after you follow my tips on how to track a single page web app with Google Tag Manager, head over to Simo’s blog and implement his solution too. That solution is not an alternative to what you’ve learned here. It is a supplement.

This is especially important if you have paid traffic (e.g. from Google Ads) coming directly to the SPA.

 

Track Single Page Web App with Google Tag Manager: Conclusion

The problem with single-page web apps or websites is that regular pageview tracking does not work. All of the necessary code is loaded once and the page does not reload during the entire user session. This is why you see only one page view in your GA reports.

However, this is not a dead-end. With certain configurations in Google Tag Manager (and possibly some input from your developer), it is fully possible to track even them. In this blog post, I’ve explained two options on how to track single page websites: use GTM’s built-in History Change trigger or cooperate with a developer.

If you’re not sure which method to choose, here’s a rule of thumb:

  • If you have development resources, choosing the path with a developer is a more robust option (in terms of tracking quality). But if those resources are unavailable right now, check the flow chart I’ve included at the beginning of this guide. However, there’s still a chance that, eventually, you’ll end up asking for the developer’s input. (if the flow chart says so).

By the way, if you found this post about tracking a single page web app with Google Tag Manager useful, consider subscribing to my newsletter.  You’ll get various bonuses and useful stuff related to GTM.

Did I miss something or do you have any questions? Let me know in the comments.

Julius Fedorovicius
In Google Tag Manager Tips
107 COMMENTS
MarcusRB
  • Feb 11 2018
  • Reply

Hi Julius, in that case it's same with fragment parameter through custom variable, and send the value as virtual page in custom field in google analytics tag?
Because, really I don't understand the importance of history change with fragment if I have a custom variable created, and I think both are same things, no?

Thank you for your reply!,
Marcus

    Julius Fed
    • Feb 11 2018
    • Reply

    Hey Marcus,

    What do you mean by saying "Custom variable"? Is it a Custom JavaScript variable? If yes, then sure.

    History change with fragment is a solution for non-developers who can easily track URL changes without writing additional JavaScript. If you know JavaScript, then you can achieve pretty much the same result.

    I hope I understood your question correctly.
    Julius

Julius Fed
  • Feb 13 2018
  • Reply

Hey, that's a good question. Since you're passing the pageview data with an additional "page" parameter, custom dimensions will also be accepted and processed by GA.

In other words, Custom Dimensions will work in the same manner just like with regular pageviews.

    Julius Fed
    • Feb 13 2018
    • Reply

    Should work as well.

      Julius Fed
      • Feb 15 2018
      • Reply

      Honestly, I'm not sure :) I still believe they should work with all scopes. Julian from MeasureSchool has posted an interesting video how to debug the data collected by GA with different scopes. Feel free to check it out, maybe it will give you additional insights.

pratama
  • Feb 19 2018
  • Reply

Hi, nice tips. thank you.

But i just wondering i i use the Plan B with declaring datalayer 'url' as page url do i also get page path, hostname, automatically or i should also declare them id datalayer?

have you tried that?
thanks

    Julius Fed
    • Feb 19 2018
    • Reply

    It depends you on how you declare that URL in the data layer. The best way to find out is to try. Give it a try and see what happens :)

Alex
  • Feb 26 2018
  • Reply

Hi Julius,

first of all: Helpful article, thank you! I use your event + data layer push method (for page / title) for a web app (no hashes, no reload) but unfortunately it only works in about 60 % of the cases. In 60 % of the cases it shows the correct page / title, in 40 % it shows the default uri / meta title.

When I check GTM's debug mode, the data layer is filled correctly.

Any idea?

    Julius Fed
    • Mar 4 2018
    • Reply

    Nope, no ideas yet. If you want, you can send me more details to email julius [at] analyticsmania.com.

    The information I'd need:
    - The URL of the page where the title is wrong
    - The URL of the page where the title is right
    - Send me a link to the shared Preview and Debug mode.

    Cheers

Johan
  • Mar 5 2018
  • Reply

Hello Julius,

Great article!

If we only want to track the page paths and not work with fragments. Can we skip creating the new history fragment and skip this part?:

"Choose Google Analytics Settings Variable. This time, I will not go into details, how it works. You can read more about it in my other blog post. This time, I’ll choose to override the GA settings variable.
Enter Tracking ID. The best practice here is to add it as a Constant Variable.
More Settings > Fields to Set > page >>> {{New History Fragment}}"

    Julius Fed
    • Mar 5 2018
    • Reply

    Yes, looks like you can skip that part.

harbo
  • Mar 20 2018
  • Reply

Hi Julius,

I have using this method for a year. and i have my ultimate problem.

my case is an SPA using react,

when "histroyChange" is triggered, variable on "Page Title" or "isJWPlayerFoundOnPage" is still have value from earlier page. basically, any variable which would have a correct value on window loaded on conventional website, will not updated yet on "historyChange".

so, i end up with correct page value but often have inconsistent on page title on google analytic.

do you have solution for that? thanks in advance.

    Julius Fed
    • Mar 21 2018
    • Reply

    Yes, talk with developers. Ask them to fire a dataLayer.push every time a state (page) changes. That .push should contain Page title and other useful information, e.g.:
    window.dataLayer.push({
    'event': 'Pageview',
    'pageTitle': 'This is a page title',
    'someUsefulInformation': '123abc'
    })

    After that, create Custom Trigger, Data Layer Variables and use them. History change trigger and variables will not be useful in this case. Using dataLayer.push is a very robust solution which increases the quality of your data.

yanfei
  • May 25 2018
  • Reply

Thank you for the great article.

I have a question, we have multiple subdomains on single domain, using 1 GAID. Only one subdomain is a single-page app. Is that a safer approach to create a new page view trigger to only fire on this SPA (subdomain) with your solution, and create a blocking trigger for current pageview tag not fire on this SPA (subdomain), or it really does not matter?

    Julius Fed
    • Jun 1 2018
    • Reply

    Hi, it depends on the structure of the single-page website + the structure of other sites, their settings, etc. Both approaches can work fine. I'd prefer using one pageview tag and set history change triggers to fire only on that landing page.

      Yanfei
      • Jun 4 2018
      • Reply

      Thank you very much Julius, that makes perfect sense! I am going to try with your suggestion !

Max
  • Jun 22 2018
  • Reply

Hi, and thanks for the great article!

But we have an issue: before login or registration we have SEF URL, like /category/page. After registration or login user goes in "SPA part of site" with #! urls. The problem is that on first page after login, analytics tracking fire twice:
1 - like pageview (because page reloaded after click on "register" button)
2 - like history change (because we have #!thankyou url fragment after successful registration)

So - i don't know how to get rid of this f*cking doubled data. Maybe you know something decision?

    Julius
    • Jun 22 2018
    • Reply

    Hi, One of the ways would be update your history change trigger to not fire when url fragment equals to "login" (or whatever the value is at that moment).

      Max
      • Jun 22 2018
      • Reply

      Ok, thanks, i will try it.

Dipen Patel
  • Jul 17 2018
  • Reply

Hello, I've gotten almost all of our SPA working with GTM, only problem I'm having is before when our site used to be a regular site, we were able to be deliberate about certain pages not even having any tracking scripts (For example, the login page) for obvious reasons. But in a SPA, once a GTM trigger has been fired and a JS script is already on the page, if a user goes to the login page, is there any way to remove any tracking script on that page without a full reload of the SPA?

    Cody Swartz
    • Jul 17 2018
    • Reply

    If you search the comments for "login" there's a similar situation above from Max with an answer to not fire when the url fragment = "login".

    There's also similar logic to fire off a custom HTML tag which you can then filter out whether or not you want to fire off the next event which will actually fire off the analytics or modify the data layer you're passing over. For example we test that the last URL isn't the same as the new one, you could also test that it's not the login page. If the conditions are met though then you can push an event into the data layer with whatever information you want (we push the lastUrl into the data layer) then we fire the analytics from that event.

      Dipen Patel
      • Aug 6 2018
      • Reply

      I understand when scripts won't get fired on certain SPA pages, but if a script is already on the DOM, any script can hook on to let's say a window event. Now if I go to that login page, that event would still be there even if the script isn't executed again, any solution?

    Julius Fedorovicius
    • Jul 19 2018
    • Reply

    GTM should not be considered as a tracking script if the container does not fire anything on that moment. Therefore, if a visitor goes to the login page, just set your tags not to fire there by adding a blocking trigger. Blocking trigger's rules:

    Event name: .* (enable Regex checkbox)
    Condition: New History Fragment contains /login/ (or something like that).

    Add this trigger as an exception to all the tags which you do not wish to fire on a login page.

Hector
  • Sep 14 2018
  • Reply

Hi Julius!

Thank's for the post. It was really useful. But i need your help. My website has the # between // like this www.page.com/#/page/us ... I've already did everything you said in the post in the tag fires everytime but i can't see the result in Analytics

Can you help me please?

Best Regards!

    Julius Fedorovicius
    • Sep 15 2018
    • Reply

    Hey, if the tag fires but you still cannot see pageviews in the GA Real-time reports, there are several general ways how to troubleshoot this and fix it https://www.analyticsmania.com/post/google-analytics-real-time-reports-not-working/.

Adam
  • Sep 18 2018
  • Reply

I have an SPA that's being used on a touchscreen at a congress and multiple users will be using it throughout any given day. I can easily track pageviews as stated above however how can I tell GA that a new session has started when another user comes to use the app? I don't refresh the browser at any point between users sessions however i can do this if needs be. My main question is: will GA/Tag Manager know intuitively that it's a single page app based on the pushing of custom pageview events (i'm using plan B) and register a new session if the page is refreshed?

    Julius Fedorovicius
    • Sep 19 2018
    • Reply

    Hey, in your case, you could use "sessionControl" field in the Google Analytics Pageview tag. Every time a user comes in, you'll need to refresh the page. On that refresh, you could pass the additional field "sessionControl":"start" (in the "Fields to set" field of your GA Pageview tag). The Pageview tag must fire only on All Pages, DOM ready or Window Loaded trigger (definitely not on history change or custom dataLayer.push).

    Here's more about the session control parameter https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#sessionControl

Jean
  • Sep 29 2018
  • Reply

Hi Julius,
Thank you for your useful guide. In your answer to Liam on JANUARY 3, 2018 AT 6:38 PM, You asked him to see if the UA tag fired. (as he saw the history change fire but does not see the variable in Analytics)
In the Variables tab of google tag manager I see the New history fragment Data Layer Variable string: 'pricing'
but in the tags tab, it tells me that the UA tag does not fire. Is this normal or could this be the issue why I don't see the label "pricing" in analytics -> behaviour -> site content?
Thank you
Jean

    Julius Fedorovicius
    • Oct 6 2018
    • Reply

    If the tag does not fire although it's supposed to, then it's definitely not an expected behavior. If the tag did not fire, it looks like your trigger is misconfigured. In Preview and Debug Mode, click the history change event, then click the tag that was supposed to fire (but did not). You'll see the conditions of your trigger and which ones were met. This should give you more clues of what's wrong.

Vladimir
  • Oct 5 2018
  • Reply

thank you so much

Dan Blum
  • Nov 9 2018
  • Reply

Hi - this post is incredibly helpful and is exactly what I'm looking for. I do have one question. My site has a mixture of pages with and without anchors but I'd like to track the anchors. I currently have GTM configured to trigger on "page views." I want to make sure that whatever I do doesn't result in double counting page views, so do I:
1. Keep my existing trigger and add a history change trigger,
2. Remove my existing trigger and replace it with a history change trigger set to page >>> {{Page Path}}{{New History Fragment}}, or
3. Something else?

Thanks so much

    Julius Fedorovicius
    • Nov 11 2018
    • Reply

    Hey, you need to combine both options:
    1. Keep the original Pageview trigger and add the History Change trigger.
    2. Set the "page" parameter to {{Page Path}}{{New History Fragment}}

    If the {{New History Fragment}} is empty (meaning that there is no fragment), then the variable will return an empty string "" (and that is harmless to your data).

      Till
      • Apr 17 2019
      • Reply

      Hi Julius,

      the problem of {{Page Path}}{{New History Fragment}} is that {{Page Path}} is not including params (?example=true). So information will be lost.

      https://www.domain.com/trousers?sort=newest will be logged as https://www.domain.com/trousers if overwrite the standard pageview-tracking with a field page setting it to {{Page Path}}{{New History Fragment}}

      So the correct way can two tags or a javascript-variable

      Or am I'm missing anything?

      Cheers Till

Mikel Azkarate
  • Nov 19 2018
  • Reply

Hi Julius! Thank you very much for the post. Our website it has been build under Angular (SPA) and the URLs look like this: https://app.fasttrack.menu/#/shops/16 I followed every step but when I tried to enable Preview and Debug mode, the orange notification didn´t appear.

Do you have any clue, what´s going on?

Thank you very much in advance from Spain.

Mikel Azkarate

    Julius Fedorovicius
    • Nov 19 2018
    • Reply

    Hey Mikel, most likely, your browser or a 3rd party extension (AdBlock, Ghostery) blocks 3rd party cookies. Read this guide https://www.analyticsmania.com/post/google-tag-manager-preview-mode-not-working/

      Mikel Azkarate
      • Nov 19 2018
      • Reply

      You´re right! Thank you very much!!

Alexandre
  • Dec 6 2018
  • Reply

Thanks for the well explained article! Really good!

Keshan D
  • Dec 11 2018
  • Reply

Hi Julius,
Quick question. I have done set up as you suggested. And its working fine. But the problem I have is, in all pages of the SPA the history change fires twice. For example from the landing page, when I navigate to inbox it will send two page views. This happen across all pages. Any idea how I can overcome that.?

Thanks a million

Regards,
Keshan

    Julius Fedorovicius
    • Dec 11 2018
    • Reply

    Hey, in this case, I'd consult with a developer:
    - Either ask to take a look at why there are two URL changes at once and ask to fix it and make it only one.
    - Ask a developer to push every URL change to the data layer (just make sure that a developer does not send duplicate events). This guide might be useful https://www.bounteous.com/insights/2018/03/30/single-page-applications-google-analytics/

    Also, check if there aren't multiple history change triggers or multiple containers on a page with enabled history change triggers.

Paweł
  • Dec 31 2018
  • Reply

Hi,

If you have a website where the url does not change (only the content dynamically reloads) is it better to push virtual pageviews or track navigating through the app as events? The best practice?

Best Regards

    Julius Fedorovicius
    • Dec 31 2018
    • Reply

    Hey, the best practice then is to ask a developer to push the data layer events when the state of your app changes and also with that event he/she should push the "fake URL", for example "/dashboard/". Then you could fetch that fake URL with the Data Layer Variable and send it to GA together with the virtual pageview.

Klaudia
  • Jan 11 2019
  • Reply

Hey! Great article but I've a problem with configuration. I'm trying to add chat on every page but without cart page. I added variables with "path page does not contain cart" and it's still not working. It's good when I navigate by clicking on url path and loading again all app. How to configure GTM in dynamically routing path?

    Julius Fedorovicius
    • Jan 11 2019
    • Reply

    The issue with the SPA is that if a script fires it will be active as long as the page is not fully reloaded (with F5 or Refresh). So if a chat has already loaded in, say, product page, it will also be visible in the cart as well (because the page does not reload).

    You should consult with the developers/creators of your chat solution and ask if there is some sort of JavaScript API that allows showing/hiding a chat by firing a little snippet of code.

      Klaudia
      • Jan 11 2019
      • Reply

      Thank you!

Catalina
  • Jan 17 2019
  • Reply

Hi Julius,

I actually have a website which is done on Wix and the GTM code is place according to their instructions, the GTM account ID was just introduced in the CMS.

Problem is that pageviews do not fire when you change the page from the menu. If I reload the page it works fine but if I press on anything from the menu, like if I press on blog, or about page, no pageview is triggered. The URL of the page changes as it should (no hash, the URL acually changes) and below the menu the content changes. But my guess is that when the content changes the page is not reloaded. None of the history change variable are showing anything and I just can't figure out what's going on.

Could you please help?

Thanks,
Catalina

    Catalina
    • Jan 17 2019
    • Reply

    Hi,

    I just found out a solution. My URL is changed without hash and even though the history variables are not showing anything the Page Path does change, which is great because that can be caught with a History Change trigger -> Fire on Some History Changes -> Page Path matches RegEx :* and this trigger can be added to the Pageview Tag. Just in case anyone needs this as well :).

      Julius Fedorovicius
      • Jan 17 2019
      • Reply

      There is a better way to track Wix pageviews. Read this

Bastien
  • Jan 28 2019
  • Reply

Hi Julius,

Thanks a lot for sharing such great content.

One of my client has a ReactJS app and want to track events and pageviews in it.

I read your post and it seems that I can do it without any specific library. But I saw on some other websites that with REactJS, it seems mandatory to use specific library like :
https://github.com/faouzioudouh/react-tracker
https://www.npmjs.com/package/react-google-tag-manager

Did you already implement some Google TAg MAnager and Google Analytics tracking on a ReactJS app ?

Do you think the implementation you provide can work on this environment ?

Thanks a lot

    Julius Fedorovicius
    • Jan 28 2019
    • Reply

    For React apps, it's better not to use my provided solution and go with either some custom libraries or just as a developer to code a solution. Every time a state changes in the React app, a developer should push that change to the Data Layer, e.g.
    window.dataLayer = window.dataLayer || [];
    window.dataLayer.push({
    'event': 'pageview',
    'pageUrl': '/something-something/',
    'pageTitle': 'Page Title'
    });

      Bastien
      • Feb 1 2019
      • Reply

      OK. Thanks a lot :)

JK Baseer
  • Feb 1 2019
  • Reply

Thanks Mate! Never know there is a variable called HistoryChange. This is the first time am doing GTM setup for one page application. Keep sharing!

Till
  • Apr 11 2019
  • Reply

Thanks for this excellent article! Been of big help!

Our URL-Pattern looks like
https://www.domain.com/de-de/shop/suche/#/q/regenhose/

I have set "page" to "{{Page Path}}{{New History Fragment}}"

Now there are two trailing slashes between {{Page Path}} and {{New History Fragment}}:

/de-de/shop/suche//q/regenhose/

How would you solve this?

    Julius Fedorovicius
    • Apr 11 2019
    • Reply

    Hey,

    This is not a problem, just cosmetics. You can enter # between {{Page Path}} and {{New History Fragment}} in your "page" parameter, but this will add the # when there is no fragment in the URL, e.g. homepage.com/#

Till
  • Apr 25 2019
  • Reply

Hint: If you overwrite the normal pageview with a field and the value "{Page Path}}{{New History Fragment}}" the tracking will miss all possible attached query strings

    Julius Fedorovicius
    • May 2 2019
    • Reply

    Thanks, Till! This guide indeed needs some updates. I hope I'll do them this year (including your suggestion).

Franc
  • Apr 25 2019
  • Reply

HI Julius,

I have a situation as follows.

SPA url lesco.com/myproductrange/#/range;

If I reload the URL, it is being captured as just "/myproductrange/" (as there is no history change, just "Window Loaded"). Could you advise as to how it would be possible to solve it.

    Julius Fedorovicius
    • May 2 2019
    • Reply

    Hey. Yes, I have. One of the possible solutions could be to try create your own URL variable (where you set it to return only URL fragment). That could be used in your tracking.

    More on URL variable -> https://www.analyticsmania.com/post/url-variable-google-tag-manager/

Jeremy
  • May 31 2019
  • Reply

Julius - thanks you so much for the helpful post! I have a 3 step registration process (2 sign ups + 1 thank you page) and I was able to finally get virtual pageviews and goals to fire in my SPA!!

While these pageviews and goals are now firing, one thing unexpected was my GA account started showing pageviews from a subdomain it didn't before.

Did this get picked up because of the history change? If so, how would I exclude history change from firing on specific URL's or make sure history change is only firing on the 3 pages I want it to?

    Julius Fedorovicius
    • May 31 2019
    • Reply

    Yes, change the History Change trigger from "Fire on all History Change events" to "Some History Change events" and then enter the condition you need (e.g. Page URL contains xxx)

adrian
  • Jun 25 2019
  • Reply

Hi Julius,

Trought you guide i have been able to track a pretty complicated website. However, i cant measure the first page with interaction (imagine a new user jumps in the home, due there are not history changes, i cant get data until the visit other url). If i use other options, pageviews are duplicated.

¿There is any thing that could be done?

Thank you

    Julius Fedorovicius
    • Jun 25 2019
    • Reply

    Just use the "All Pages" trigger too.

GTM Enthusiast
  • Aug 15 2019
  • Reply

Hi Julius,
I have a question regarding single page web app. When it is necessary to contact developer regarding custom events, do we ask them to insert also other information such as name or text or anything else so I can catch those virtual pageviews in GTM.Or maybe you have some texts regarding single page web app beside your article.

All the best

    Julius Fedorovicius
    • Aug 15 2019
    • Reply

    Hey, it's up to you what you ask. You should definitely ask for a page URL and page title. If you think something else is also useful, go for it and ask.

Charana
  • Sep 18 2019
  • Reply

Hey. Great article. I was able to set this up and I can see the URL fragments in the real time view. How do I setup a conversion goal for each?

    Julius Fedorovicius
    • Sep 19 2019
    • Reply

    Hey. By using the destination goals https://support.google.com/analytics/answer/1116091?hl=en

Serena
  • Sep 19 2019
  • Reply

HI! Thanks for your article.
I have traced my SPA with the History Change trigger method, on GA I see my pageviews correctly but I have a problem with the sessions.
Let me explain.
My website is made up of:
- pages where there is an actual page loading
- the SPA part.
On GA the sessions of the pages with loading are correctly counted, but the page count of the SPA is zero.
Thank you.

    Julius Fedorovicius
    • Sep 20 2019
    • Reply

    Hey, you haven't provided a lot of information in this comment so it's hard to guess. There are many reasons for that, for example:
    - You are using the wrong GA ID for the SPA
    - You implemented the History Change wrong and your GA tag is still not firing.

    Start with the GA Real-time reports and see whether you see the SPA pageviews there.

Ethan
  • Sep 23 2019
  • Reply

Alright, so I never leave messages on websites, but damn, this article is so good and it also helped me set up GTM for my React website.
Thank you so much the high quality article !

    Julius Fedorovicius
    • Sep 23 2019
    • Reply

    Glad to help!

Sean G
  • Oct 24 2019
  • Reply

This walkthrough was so helpful! Thanks so much for providing this valuable resource.

I implemented this on our website where we're running infinite scroll in our blog. What I've noticed is that in our pages report, URLs are being display like so:

www.example.com/www.example.com/blog-post-name.

We are using a GA filter to put the hostname in the pages report. We haven't had the issue described above until modifying GA for infinite scroll.

Any advice you can share would be much appreciated!

Thank you,
Sean

    Julius Fedorovicius
    • Oct 24 2019
    • Reply

    If you already have a "show full url" filter, then don't pass the hostname with the "page" field in the Google Analytics Settings Variable.

Nico B
  • Dec 18 2019
  • Reply

Julius, very helpful thank you. I will be referring to your guides more in the future and refer to people that could benefit.

Robert B
  • Jun 3 2020
  • Reply

Hi Julius, I'm struggling to get this working correctly for my site.
I enabled the history listener and they worked correctly in that step, and only fired once when url changes.

During the step "Do any History Variables return useful data?"
All of my history variables do not have useful data.

The page path variable is updating though..

Since this is the case -
Under my google analytics variable:
Can I use {{Page Path}} for the page field value? or do I have to use data from one of the history variables as described?

Thanks in advance for the amazing content.

    Julius Fedorovicius
    • Jun 3 2020
    • Reply

    Since page path is updated properly in your case, then you don't need to do any configurations on the GA settings variable level. Just fire that pageview tag on every history change and GA tag will do the rest. You can check that in your realtime reports.

    Robert B
    • Jun 3 2020
    • Reply

    Note - I set up my GA pageview trigger to take place on history changes and pageviews as described.

    When history changes I am getting a page view to trigger and under my google analytics settings on the tag I am seeing

    fields to set:[{fieldname: 'page', value: '/signup-preferences'}]

    So it seems to be working properly, but I want to make sure I'm not missing something! Thanks again

      Dan
      • Oct 12 2020
      • Reply

      Got a similar experience but my pageview trigger fires only on the first page on Container Load. Is it supposed to fire that early? If not how do you prevent that?

Robert B
  • Jun 3 2020
  • Reply

Thanks for the response Julius!
I was hoping to use these new history listeners to help create exceptions.

I have a popup that triggers when the dom loads on my homepage. It already has rules set so that it will not appear on pages other than /.
The issue - If a user loads the dom on the homepage and then navigates to another page - the popup will still trigger.

Any advice on how to not have the popup show?

    Julius Fedorovicius
    • Jun 4 2020
    • Reply

    Update your triggers. If a visitor navigates to another page (that is a virtual pageview), and you have ONLY DOM ready trigger, then your popup should not appear. So this means that you have more triggers added to that popup. Remove them.

Bruno Santos
  • Jun 9 2020
  • Reply

Brazilian greetings Julius! Your article helped a lot and I managed to solve it using history only. However, I'm trying to create a click event, but GA doesn't recognize the interactions.

I tried to set it up to take the history too, but without success. Do you have any tips on how I can collect my click events at my SPA?

    Julius
    • Jun 9 2020
    • Reply

    Click tracking should work. Most likely, your setup is somewhere incorrect.

Pravin Patil
  • Jun 19 2020
  • Reply

React Native web routing button click event not get in GTM

Adrzej
  • Jul 1 2020
  • Reply

Hi, one question. When I have implemented dL for Enhanced Ecommerce, I must clean array every time once user change page? For example, Offer page >> lising - some dimensions may be present in the layer (from offer page on listing page) and be sent with event on listing.

What's best practise for dL and SPA?

    Julius
    • Jul 1 2020
    • Reply

    If some keys in the array/object may change, clearing is an option.

Alex
  • Jul 2 2020
  • Reply

Hello Julius,

Thank you for this useful article 🙏

You said: "If the page address contains some query parameters that are important to your reporting (excluding UTM parameters), ask a developer to include those parameters in the pagePath key as well."

It's still unclear for me if I should do that if I need to pass values of custom query parameters to GA.

For example, a visitor comes to our website at the following URL: https://www.acme.com/?ref=E6GTJQLXT&utm_campaign=summer+sale&utm_medium=email&utm_source=newsletter, all query parameters are preserved in the URL while browsing the site, should I have something extra send to dataLayer if I want to pass 'ref' parameter value to Google Analytics custom dimension? And if so, why shouldn't I do the same for UTM parameters?

    Alex
    • Jul 2 2020
    • Reply

    I have to correct my words - query parameters are NOT preserved in the URL while browsing the site, this only happens with redirects.

    Julius Fedorovicius
    • Jul 3 2020
    • Reply

    If developer is pushing to the data layer all the virtual pageviews, you could ask him/her to send all query parameters as well.

    Instead of

    dataLayer.push({
    'event' : 'custom-pageview',
    'pageUrl' : '/page/',
    'pageTitle' : 'sometitle'
    });

    you should ask for this:

    dataLayer.push({
    'event' : 'custom-pageview',
    'pageUrl' : '/page/?ref=E6GTJQLXT&utm_campaign=summer+sale&utm_medium=email&utm_source=newsletter',
    'pageTitle' : 'sometitle'
    });

      Alex
      • Jul 3 2020
      • Reply

      Julius,

      Should the developer send a full URI or just an URN in pageUrl variable? In the article you indicated a full URI, but in the comment above it's just the URN. All I know is that when GA script sends a hit, the hit contains a full URI, for example, it may be &dl=https%3A%2F%2Fwww.acme.com%2F.

      What exactly should we use to pass to GA?

        Julius Fedorovicius
        • Jul 3 2020
        • Reply

        Actually, both should work. dl (document location) will be caught by GA automatically in order to take, for example, UTMs. But if you want to see some other query parameters in your reports, they could be included in the pageUrl data layer variable.

        alternatively, a developer can push the pageUrl without query parameters and you can pass them with the window.location.search javascript variable. Check the "If URL change involves URL Fragment (#) and also might contain Query Parameters" chapter for inspiration.

        To sum up: there are several ways to achieve the same things here. The result will be the same. Choose the most convenient for you.

        Julius Fedorovicius
        • Jul 3 2020
        • Reply

        And sorry if the previous comment was misleading/confusing.

          Alex
          • Jul 3 2020

          Many thanks! I'll re-read your article again 👍

Bram
  • Jul 8 2020
  • Reply

Thank you very much for the useful article.

The only thing i dont seem to get is, how to setup conversion tracking in GA.
People land on /app#/thankyou , so this means i can setup just hitting URL /app/thankyou in GA as a goal.
As GA sees landing on /app/thankyou as a pageview.

Or am i thinking to simple? Always seem the case with GTM haha.

Thanks again.

Best regards

    Julius
    • Jul 8 2020
    • Reply

    Yes, you are correct.

Bram
  • Jul 14 2020
  • Reply

Hi Julius,

Excuse me for my late responds.

Its working thanks!

Only issue we are having is that in like 1.3x of the cases it looks like conversion has been triggered.
So too many times, why this is happening is quite hard to figure out.

Did you experience this before?

Thank you!

Best Regards,

Bram

Raqi
  • Aug 12 2020
  • Reply

Thank you for the useful article.

I have one question about my issue in my website.

Since the pageview uses a custom event trigger, can I use the same trigger to fire my Google Ads conversion tracking tag? or is it require another trigger to fire my tag?

    Julius Fedorovicius
    • Aug 12 2020
    • Reply

    You can add multiple tags to the same trigger.

Jignesh Desai
  • Nov 16 2020
  • Reply

Thank you very much for the detailed article and series on GTM. It is very helpful.
I am trying to setup GTM on my Angular SPA. I am a developer. I tried both setups the one with just history based and other with the developer need. I see the hits in GA for both solution when I am in preview mode of GTM with debugger on and also see a network call to collect.js on each navigation. However, I don't any hits on GA when I try to browse the SPA without GTM Preview mode. Any idea what could I be missing?

Thank you!

LaWarn
  • Jan 12 2021
  • Reply

Hello Julius,

thank you very much for this article, very useful as usual!

I am working on a SPA website but need to wait the page to be fully loaded before firing my tag. How can I do since I cannot use the Page view - Window loaded trigger?

I have a custom event on each "page" in the datalayer ("screen_load") which I usually work with but which prevents me from waiting the full charging of the page.

Please can you help?

Charu Mishra
  • Feb 1 2021
  • Reply

If I'll fire custom pageview event with PageTitle parameter.
window.dataLayer.push({
'event': 'Pageview',
'pagePath': '/abc/contact-us',
'pageTitle': 'Contact us'
});

will it also update the value of page title in any other tool except GA.
Kindly confirm.

    Julius
    • Feb 1 2021
    • Reply

    No, unless you manually send it to those tools.

Joe Privett
  • Feb 14 2021
  • Reply

As part of new PCI compliance laws coming in, it will be necessary to exclude scripts on 'payment' pages. How can we exclude GTM specifically on payments pages within an SPA?

    Julius
    • Feb 14 2021
    • Reply

    You should discuss this with your developers.

      Joe Privett
      • Feb 15 2021
      • Reply

      I am the developer :)

        Julius
        • Feb 15 2021
        • Reply

        Then don't activate GTM on the payment pages if you need this. Before redirecting a visitor to the checkout, refresh the page. Then GTM will not be loaded.

Erika Araujo
  • Feb 25 2021
  • Reply

Hi, Julius!
I'm having a different problem.
I have an "History" event showing in tag assistant, however there's no variable, trigger or tag active related to History on my GTM. How can i deactivate this event?

    Julius
    • Feb 25 2021
    • Reply

    Disable history events in GA4 enhanced measurement

      Erika Araujo
      • Feb 26 2021
      • Reply

      Well, thats weird! I'm not using GA4!

        Julius
        • Feb 26 2021
        • Reply

        Then this requires deeper audit

Leave a comment Cancel reply

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

 

Hi, I'm Julius Fedorovicius and I'm here to help you learn Google Tag Manager and Google Analytics. Join thousands of other digital marketers and digital analysts in this exciting journey. Read more
Essential resources


Popular articles
  • 🔥 GTM Form Tracking: 7 Effective Methods
  • 🔥 dataLayer.push: The Guide
  • 🔥 GTM vs Google Analytics
  • 🔥 99 Things You Can Do with GTM
  • 🔥 Common GTM Mistakes
  • 🔥 Data Layer: Ultimate Guide
  • 🔥 60+ Custom JavaScripts for GTM
Analytics Mania
  • Google Tag Manager Courses
  • Google Tag Manager Recipes
  • Google Tag Manager Resources
  • Google Tag Manager Community
  • Login to courses
Follow Analytics Mania
  • Subscribe to newsletter
  • RSS feed
Recent Posts
  • Scroll tracking with Google Analytics 4 and Google Tag Manager
  • How to Exclude Internal Traffic in Google Analytics 4
  • 5 Ways to Track Site Search with Google Tag Manager and Google Analytics 4
Analytics Mania - Google Tag Manager and Google Analytics Blog | Privacy Policy
Manage Cookie Settings