English Posts

How to track Single Page Applications with Google Tag Manager

There are always more websites called Single Page Application (SPA). These websites have different advantages on the user side and therefore at the UX level, but they can create many problems in web tracking.

How they work

The first time the page is loaded, the SPAs load all the necessary resources while browsing the website. Each time the user interacts with the site and clicks on the different contents, the next content is loaded dynamically. So technically, the page doesn’t reload even if the contents are different.

Web tracking

The Google Analytics tag works good for traditional websites, as the code snippet is activated in every single page visited by the user. For Single Page Applications, however, the code is activated only once, precisely because the contents are loaded dynamically.

Practically, within Google Analytics we will only see one page view. It will therefore be necessary to manually capture the virtual page views when new content is uploaded to the site.

How do I know if I am working on a Single Page Application?

Is the website I’m working on a single page application? To answer this question, you can see how Google Tag Manager Preview and Debug (P&D) behaves.

preview and debug mode gtm

If when loading a new page, the P&D mode reloads, then you are not working with a SPA.

On the opposite, if, while navigating from one page to another, the GTM P&D mode remains unchanged or, in any case, tracks your interactions but not the pageviews, then you are on a Single Page Application.

preview and debug in single page application

Questo accade proprio perché nelle SPA non viene caricato il documento della pagina e dunque non verrà caricato il codice di tracciamento in JavaScript. Proprio per questo motivo, né GA né GTM tracceranno queste pagine dinamiche.

This happens because the page is loaded once, it does not reloaded during the user’s session and therefore the tracking code will not be loaded. For this reason, neither GA nor GTM will track these dynamic pages.

In concrete terms, with the simple Google Analytics tag, what we will see in the Real Time Report would be:

  • a page view (the landing page or the page that undergoes a manual refresh)
  • the URL where the first hit occurs
hit google analytics

The problem is that, while continuing to browse the site, all the interactions that continue to take place will not be sent to GA and, after a short time, we will see such a situation:

real time report google analytics

Only if the user refreshes (the classic F5) each time a new content is viewed, will the data be processed like a classic website. Obviously the F5 with every page view is impossible to imagine!

It is however possible to track the Single Page Applications, adding some specific configurations in Google Tag Manager.

There are two possible options:

  1. through the help of GTM only (as descrived below up to Step 5)
  2. through GTM and the developer

How to track Single Page Applications with Google Tag Manager

First Steps

The first question to ask is: does the URL change while browsing on the website?

If the answer is yes, then I can use the History Change trigger.


We need to create a new History Change trigger.

In GTM, click on Trigger New and set it as in the image:

Trigger Type: History Change

Trigger fires on: All History Changes

all history change trigger

Save it.


Go to Variables Built-in VariablesConfigure and activate all the items you find under “History ”:

built-in variables history tag manager

Let’s check now if the trigger created works on the site. Refresh the GTM Preview & Debug (P&D) mode and refresh the site.

Now, browsing through the contents, you should see the activation of several History Events:

history event google tag manager

By clicking inside the History Event and then selecting the Data Layer item I can find some information that will be useful for tracking the pageviews later.

data layer history

At this point you will be able to meet different options, depending on the site you are working on. For every option there is a tracking solution;)

Below are the possible situations you can meet and the guide to track each case.

SPA with change of URL while browsing


There are Single Page Applications, whose URL changes like on a normal website when browsing from content to content.

For example, when I am on the home page I have this link: ‘www.example.com‘ but when I move to another content I have a different one: ‘www.example.com/category1‘ and so on.

After following all the previous Steps, it is necessary to check the content collected by the Variables.

By clicking on the History Event here is what appears and in detail the description of each point:

history event gtm
  1. Find the History Events that appear after creating the trigger at the Step 1
  2. Click on Data Layer tab to see what information is collected
  3. under ‘gtm.oldUrl’ appears the first link I was in and in the ‘gtm.newUrl’ the second link I clicked on (in practice the history of my visit is saved)
  4. the name of the History Event that collects this information

I have the information that I need. I proceed to modify the trigger.

Step 4.1.1 – Modify the trigger created in Step 1

Dopo aver osservato le informazioni che vengono raccolte, devo modificare l’attivazione del mio trigger.

After looking at the information that is collected, I need to change my trigger activation.

It will no longer be necessary for it to be activated on all History Change events but only on some. Which? As seen above, I will be interested in wgat collects my browsing history collects: pushState .

Click on the trigger created in step 1 and modify it as follows:

Trigger fires on: Some History Changes

Conditions: History Source – equals – pushState

pushState google tag manager

I changed the name of the trigger, now calling it ‘All History Change – pushState‘.

The name of the inserted variable is History Source because it is the one that contains the value that interests me:

SPA with change of URL while browsing with hash #


There are several SPAs where the URL changes and the hash # appears, every time you see a new content.

For exampe: ‘www.example.com/#foto’ and then, when I see a new content: ‘www.example.com/#gallery‘ etc.

Thanks to the Built-in Variables activated in Step 2, we will receive this information which is saved under the variable New History Fragment:

Usually, the Google Analytics tag collects values ​​within the URL to transfer them to GA. However, if there is a fragment inside the URL, a text string, which appears after the hash #, the values ​​after the # will not be collected automatically.

For this reason we have to modify the GA Settings Variable.

4.2.1 – Change the GA Settings Variable

Go to the GA Settings Variable and in the Fields to Set tab enter:

Field Name: page

Value: {{New History Fragment}}

Save, then refresh the GTM P&D mode and finally check on GA in the Real-Time report.

real time report ga

SPA with change of URL while browsing with hash # and Page Path


There are Single Page Applications which are similar to Step 4.2 but, in addition to having the hash # in the URL, they also have the variable Page Path. Here is an example:


In these cases, if we leave the rules set in Step 4.2, the only information that will be collected will be ‘#foto‘. You will lose all the rest of the value ‘/ page-one/category-2 /‘.

To avoid this situation, a value must be added to the GA Settings Variable.

In the Fields to Set tab seen in point 4.2.1 enter two values:

Field: page

Value: {{Page Path}}{{New History Fragment}}

gtm ga settings variable page path

Save it.

SPA with change of URL while browsing with hash # and query parameters


It is possible to meet Single Page Applications in which the URL has both the hash # and the query parameters.

Example: www.example.com/?q=categoria#foto

To collect this information, I have to add one more variable in the Ga Settings Variable than in the previous step.

4.4.1 – Creating the variable to collect the query parameters

Go to Variables New and select Java Script Variable.

The name to be inserted inside will be: window.location.search

4.4.2 – Insert the variable created in the GA Settings Variable

At this point we have to update the GA Settings Variable, inserting the Javascript variable of the previous step.

Within Fields to Set the values ​​will be the following:

Field: page

Value: {{Page Path}}{{js – window.location.search}}#{{New History Fragment}}

ga settings variable with hash

Save it all and check on GA.

hash real time

Case: Empty New History Fragment but New History State with Path value

STEP 4.5 – CASE OF VARIABLE New History Fragment EMPTY BUT VARIABLE New History State WITH FIELD Path

In Step 4.2 we saw that the New History Fragment variable collects the values ​​after the hash #.

It may happen that, by inspecting the History event both in the Data Layer and in the Variables tabs, the New History Fragment variable is empty:

new history fragment variable gtm

but the New History State variable contains several values. In this case, I’m interested in one thing specifically, namely the path item that collects the URL value:

new history state gtm path

To collect only the path key I have to create a new variable.

Step 4.5.1 – Collect the path value with a Data Layer Variable

We create a new Data Layer Variable to collect the path field inside the New History State Variable.

Go to Variables – New.

Select the Data Layer variable and enter the fields:

Data Layer Variable Name: gtm.newHistoryState.path

gtm newHistoryState path
4.5.2 – Enter the variable created in the GA Settings Variable

Now let’s modify the GA Settings Variable to send the data collected in the previous step to Google Analytics.

Open the GA Settings Variable and enter these values ​​in Fields to Set:

Field name: page

Value: {{dlv – gtm.newHistoryState.path}}

Save and check on the Google Analytics Real-Time report.


Verify that there is the right trigger to trigger Pageviews.

The GA – Pageview tag must have the trigger: All History Changes (except if your case is like the one described in Step 4.1.1).

trigger all history change gtm

ATTENTION to the type of trigger. Do not insert the All Pages trigger with the All History Changes activator.

You risk that the first page of the SPA that loads is counted once through All Pages as soon as the Page View Event is activated and a second time with All History Changes as soon as the History Event is activated.

ONE TRICK: there are websites where more than one History event is activated even if the user has not navigated to a new page. The activation of a new History Event would lead to a new pageview. To avoid this risk, the trigger can be structured in this way:

history source does not contain replace gtm

Simo Ahava has written a technical post about it that you can find here.

How to track SPAs with Google Tag Manager and the help of a developer


If you have had the patience to read the guide up here, it may be that you have solved the problem of tracking the Single Page Application you are working on.

In this chapter I will explain instead how to track the SPA in case of the solutions proposed previously did not adapt to your case.

You will have to ask the help of a developer and ask her/him to insert in the site a code to push more information into the Data Layer, using the dataLayer.push.

By forcing this information on the website, it will therefore be possible to find several values ​​that can be collected as we did previously and sent to Google Analytics through the tools that Google Tag Manager provides us.

STEP 4.6.1 – Provide the code to the developer

The dataLayer.push script is as follows:

window.dataLayer = window.dataLayer || []; 
'event': 'Pageview',
'pagePath': 'https://www.sito.it/category/about-me',
'pageTitle': 'Who am I' 

Let’s see the items that make it up:

  • ‘event’: ‘Pageview’: is the name of the event you want to push in the Data Layer
  • ‘pagePath’: ‘https: //www.sito.it/category/about-me’: is the path of the page that will be collected. You need to ask the developer to make this parameter dynamic, so that it changes every time the URL changes
  • ‘pageTitle’: ‘Who am I’: the parameter that collects the title of the page

Remember that your purpose in this case is to provide the code to the developer so that there is the correct information that you can retrieve with Google Tag Manager. It will be the developer who knows where to put the code on the site and how to dynamically collect some values ​​through the script.

STEP 4.6.1 – Collect the parameters with GTM

Now we have to recover the values ​​entered in the Data Layer through the use of Google Tag Manager.

We create two data layer variables to collect the ‘pagePath‘ and ‘pageTitle‘ fields.


Create a new variable and enter the following fields (the name must be the same as in the script, so pay attention to upper / lower case):

Data Layer Variable Name: pagePath

data layer variable pagepath

VARIABLE pageTitle

Now let’s create another variable to collect the pageTitle parameter, inserting the following values:

Data Layer Variable Name: pageTitle

data layer variable pageTitle
STEP 4.6.2 – Trigger creation

Finally we create a trigger to activate the Google Analytics tag.

Create a Custom Event trigger with the following fields:

Event name: Pageview

STEP 4.6.3 – Update the GA Settings Variable

Finally, to pass all the information to GA, we need to enter the following fields in the GA Settings Variable.

Within Fields to Set the values ​​will be the following:

Field Name: page – Value: {{dlv – pagePath}}

Field Name: title – Value: {{dlv – pageTitle}}

google analytics settings variable gtm
STEP 4.6.4 – Associate the trigger with the Google Analytics tag

Associate the trigger created previously with the Google Analytics tag.

Save it, refresh it and chek it in the Real-Time Report in Google Analytics

The ‘referral rouge’ problem

There is one last but important thing to know if you implement tracking with the help of a developer.

In the event that you have paid traffic on the SPA you are working on, the solution just proposed in steps 4.6 will not be 100% suitable.

The problem: if you have paid traffic that leads to the Single Page Application you are working on, what happens is that when a user lands on the website from an ad and continues to browse the site, the paid traffic becomes organic. Exact! You read that right.

If this problem is not solved, Analytics makes a mistake calculation on the attributions of the sessions between Paid and Organic and therefore the session data but also the Bounce rate and time on the website are all staggered and not useful for your analyzes.

This problem is called ‘rouge referral‘ and well described in detail by Simo.

Per risolverlo bisogna sempre inserire il seguente codice nel sito web, nella parte <head> e prima dello snippet di codice di GTM:

To solve it, you must always enter the following code on the website, in the <head> and before the GTM code snippet:

window.dataLayer = window.dataLayer || [];
originalLocation: document.location.protocol + '//' +
                                document.location.hostname +
                                document.location.pathname + 

In this way we can pass two parameters to the Ga Settings Variable:

  • location: which will retrieve information from the data layer, remaining constant;
  • page: which will retrieve the URL where the user is located

The steps to be taken will be as in the previous point, that is:

  • creare una variabile di tipo data layer il cui name sarà: ‘originalLocation’
  • create a data layer variable whose name will be: ‘originalLocation
  • create a custom Java Script variable with the following code inside:
function() { 
return document.location.pathname + document.location.search; 
current location gtm
  • Change the GA Settings Variable by inserting the fields with the newly created variables:

Save and check that everything is working;)


If you have reached the end of this article safely, you will have seen that there are several solutions for tracking Single Page Applications.

There are two ways:

  1. by yourself
  2. you and the developer

All this can be done thanks to Google Tag Manager which will allow you to send all information to Google Analytics in order to better analyze the website and make the best Data-Driven decisions.

If you liked the post, please share it!

Good analysis 🙂

You may also be interested in these articles:

How to solve the 5 million cell limit in Google Sheets

Cardinality dimensions in Google Analytics

The value (other) in Google Analytics

Query Parameters and Google Analytics

6 pensieri su “How to track Single Page Applications with Google Tag Manager

  1. Hi Fatih, You are right bc there is no history change on first page so u should use 2 triggers on ur pageview: 1st history change and 2nd usual pageview trigger (You need to put this triggers with “or” not with “and”). Usual pageview trigger will work only at landing page and history change trigger will make rest of job

  2. Hi Luca Sanità, great guide! However, in my case the first page view of the website (landing page) is not tracked because there is no history change. Do you have an idea how the trigger should be extended?
    Greets from Germany 🙂

    1. Hi, thanks for your comment! You have another option: with the help of a developer, you can push an event into the data layer with this kind of structure:
      dataLayer.push({’event’: “pageview”})
      Then you can simply create a custom event trigger, where the event name will be “pageview”. This trigger will activate the tag type Google Analytics that will have “Page View” as a track type. The structure I shared with you is a basic JavaScript code. You can add addition information inside the snippet, such as the URL, the hostname, the page title, the path etc. But with this code and with the custom event trigger you can solve your issue.
      Liebe Grüße 🙂

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *