Using Google Analytics 4 with Single Page Apps

User overlap by platform in the tech overview report.

Google Analytics 4 was designed to handle Single Page Apps (SPA's) gracefully. In this post I will explain how to modify your Google Analytics 4 page_view event in Google Tag Manager to function properly across both traditional websites and SPA's.

LAST UPDATE (Sept 25, 2021)

Expanded some of the concepts in this article.

If you've attempted to install a legacy version of Google Analytics on a single page application, you have probably encountered two problems:

  • Your page views do not consistently fire as the user navigates the SPA portion of your site.
  • And, if you use Google Tag Manger, your User Explorer report shows sessions abruptly starting within a minute of each other for the same user. This also artificially inflates your bounce rate and decreases other engagement metrics.

These issues occur because the last version of Google Analytics was released in 2013 (Universal Analytics), and single page apps were extremely uncommon at this time (React JS was released later that year). These issues have been resolved with the launch of Google Analytics 4.


How Google Analytics 4 Handles Single Page Apps

There are three new features that have rolled out in Google Analytics 4 to improve tracking on websites that include single page apps.

The page_view event will fire on history change as well as page load by default

The most common and obvious problem with legacy versions of Google Analytics on SPA's is the missing pageview hit. This happens because the legacy versions of Google Analytics sent a pageview when a page fully reloaded, and SPA's are built to prevent a page from fully reloading.

Google Analytics 4 resolves this problem with the History API, which allows it to fire the page_view event when the URL changes even if the page does not fully reload. You can see this feature under your stream settings in "Enhanced measurement".

Enhanced measurement

In most cases, SPA's will update the URL as the user interacts with them. As a result, if your website includes a SPA there is nothing special that you need to configure to track these pages the same way you track the rest of your site.

The session ID is determined by the browser

Google Analytics 4 sets a unique identifier for your session with each event (called "sid"). This is important because it means that sessions are determined in the browser rather than by Google's processing rules, and therefore you have more control and visibility into how they are created.

Single Page Apps using Google Tag Manager with the legacy versions of Google Analytics had a complex bug that is known as the rogue referrer problem. Simo Ahava has documented the issue well, but the important thing to know is that Google Analytics 4 does not suffer from this issue.

The configuration tag can be unlinked from the page_view event

The configuration tag has a very different purpose from the page_view event. By default, the configuration tag will run every time a page loads, and then fire a page_view event when it is complete. But this will cause a problem if you need to run the configuration tag again before the next page_view should be tracked.

To disable this behavior, you can uncheck the checkbox shown below in your configuration tag.

page_view checkbox in config tag

So who should do this? It’s really all about timing, and there are two things to consider:

  1. When should my pageview event fire?
  2. What parameters and user properties should be set with my pageview event?

(question1 == “any time a page loads” && question2 ==“none”) ? Use the checkbox : Keep reading;

Consideration #1: When Should My page_view Event Fire?

With Single Page Apps, how we define a “page”, can get a little tricky.  As mentioned above, Google Analytics 4 will fire a page_view when a page loads, or when the URL changes by default. However, if your SPA needs to fire a page_view at a different point in time (maybe because the URL is not changing) then you will need a more custom solution.

The best practice in this situation is to collaborate with your dev team to have them fire a "Page Loaded" event to your data layer at the desired point in time:

1dataLayer.push({'event': 'Page Loaded'});

You can then create a trigger using the “Custom Event” type, like so:

Page Loaded event tag

If you are unable to work with your dev team to create a data layer event, you may be able to deploy an AJAX event listener or poll until an element exists. These are fragile and not the best practice.

Consideration #2: What Fields, Event Parameters, and User Properties Should Be Set with My Events?

When you create Analytics tags you need to be very thoughtful about what business questions can be answered by Google Analytics data and how that data will be visualized.  This is the only way to determine which fields and user properties should be included with your events.

There are many scenarios where you will need to unlink your page_view event from the configuration tag so that you can use the configuration tag to set fields or user properties without triggering a new page_view event.

For example, sometimes you cannot determine the user's authentication status until long after the page has loaded.

Manually Setting Up a page_view Event

If you’re still reading I’m going to assume it’s because the checkbox will not apply to you (probably because of one of the reasons listed above).  So, you’re going to need to create a new “Google Analytics: GA4 Event” tag in GTM to pass your page_view event.  Here’s how to do it:

  1. First of all, you’re going to need that configuration tag.  Just make sure that the “Send a page view event…” checkbox is unchecked (obviously).

  2. Create a new tag, and select “Google Analytics: GA4 Event”

  3. Make sure your event name is “page_view”, because this is the Recommended Event that unlocks some additional features in the UI (although it’s not documented).  Avoid “pageview”, or “pageView” or anything else.  

  4. If you’re setting a User ID for your authenticated users, you’ll set this as a Field in your configuration tag (not in the event tag).  If you would like to learn more about the User ID and why you should use it, read THIS post.

Add your trigger to this tag according to the notes we discussed in consideration #1 above, and you’re ready to go.