My website is a single page app (SPA) that never changes browser history or the page title. Therefore, it’s my understanding that I have two options if I want to capture page_view events:
- Add browser history and a unique title for every virtual page.
- Manually fire a page_view in SPA code.
I’m going with option 2.
I’ve read numerous articles on how to do this, and they tend to converge on similar advice, but they’re always a little different from each other. This is probably because most were written soon after GA4’s release. After trying most of the steps in these articles, I’m not convinced I’ve set up virtual pageviews correctly. Unfortunately, the official documentation provides little guidance:
Alternatively, you can always explicitly set page parameters when sending a page_view event to ensure accuracy and clarity.
When it comes to the official documentation, I’ve only been able to find guidance on doing this with gtag.js, but I’m not using that. I’m using Google Tag Manager (GTM). Here are the steps I’ve taken and my rationale for taking them (besides an article telling me to do so). After this list, I’ll explain why I think it’s not working correctly.
- Login to GTM
- Create a Google Analytics GA4 Configuration but uncheck “Send a page view event when this configuration loads”. I do this because, if I’m going to manually send
page_view
events, I don’t want this configuration to send a duplicate. - Create a custom event trigger named “Page Loaded”. This allows me to control when I fire this trigger. There’s nothing special about it yet. Here’s how it looks:
- On my SPA, I add this line above the GTM tag in the
<header>
:<script> window.dataLayer = window.dataLayer || []; </script> <!-- Google Tag Manager -->
- Elsewhere in my code, I manually use that
dataLayer
to fire my trigger:dataLayer.push({ 'event': 'Page Loaded', 'page_url': ..., 'page_title': ..., });
- Back in GTM, I create data layer variables for those two
page_*
fields: - I create a
page_view
tag that reacts to this trigger and uses those variables. - I click the preview button to see how it’s working.
Now, as far as I can tell, this tag is firing at the right time and passing all the right information. But here’s why I think there’s something wrong:
If I view my other tag, the google analytics hit information has Page Location
and Page Title
values of the actual page, not the virtual page (aka the custom event I created). This other tag’s trigger looks like this:
The tag looks like this:
And the Google Analytics Hit looks like this (on the Tag Assistant page):
Those red arrows have the value of the actual page.
Is there some step/configuration I’m missing? I would expect all the tags to use the Page Title
of my page_view
tag.
Advertisement
Answer
Right, that’s cuz either the fields aren’t being inherited from the settings variable (I have noticed that behavior in GA4 before) or the values of your DL variables are not set at that point (which is unlikely).
An obvious fix for it would be just adding your fields to the click tag and be done with it.
The way I do GA4 tags is by making one single tag. For everything. All its content are variables, including the name of the event. And all the logic for it is either in regex lookup tables or in CJS. Or in both: CJS that uses rLUTs.
Now it may seem complicated and overengineered, but now the size of your GA4 set up is small (remember: the size of the container is limited), it’s easy to manage if you love your JS (all logic is in one place) and you don’t need to iterate through all your dimensions every time you need a new event to fire.
So I basically treat a GA4 event tag as a config variable. In your case, you can even merge the pageview and the click events into one.
Also, not having history changes is a really poor practice and I would switch the site engine completely. It will cause dramatic issues in other places, like SEO.