June 5, 2020
Pageview Trigger not working in GTM? Here’s How You Can Fix It
Update: in June 5th, 2020, the Pageview event was renamed to Container Loaded.
Is the pageview trigger not working in Google Tag Manager for you? I had this problem too and recently a student of mine contacted me regarding the same issue. He told me there was no clear answer on the web so I decided to publish a quick blog post explaining where the problem lies and how to fix it.
Note: the issue described in this blog post is a part of my larger and more comprehensive blog post – Most Common Google Tag Manager Mistakes. Make sure you check it out.
The problem
The issue is visible when you open a GTM Preview and Debug mode and reload the page. A P&D console appears, but only two events (instead of 3) appear in it – DOM Ready and Window Loaded. No Pageview. And what is that “Message”?

Why did it happen? Continue reading and you’ll find out.
Since we want to fire our Universal Analytics (or any other) tag as soon as possible, the best trigger is Pageview. DOM ready fires when website’s HTML is rendered and Window Loaded becomes available only when everything else (including Javascript) finishes loading.
If your website loads too long, visitors will abandon your website prior to DOM Ready of Window Loaded events, thus you have to implement web analytics tracking tools by using Pageview trigger.
The Investigation
Every solution starts with an investigation. Check the premise and try to identify the cause of the problem.
When my student asked for help, I opened his website and accessed the browser’s developer tools (in Chrome, go to Menu > More Tools > Developer Tools or click F12).
I was looking for the wrong implementation of dataLayer and saw this:

There are actually two mistakes made by a developer here and I’ll explain them in the next chapter of this blog post.
The Solution
According to Google’s recommendations, variables (that need to be passed over to dataLayer) must be added to a snippet above GTM container code:
<script> dataLayer = []; </script>
Here’s how it looks in action:
<script>
dataLayer = [{
'pageCategory': 'signup',
'visitorType': 'high-value'
}];
</script>
<!-- Google Tag Manager -->
...
<!-- End Google Tag Manager -->
However, I noticed that a number of developers (who have no experience with Google Tag Manager) do not follow Google’s recommendations (or just don’t know that such a thing exists). They implement dataLayer like this:
var dataLayer = [{...}];
As a result, this will:
- Potentially break GTM,
- Render the Pageview event non-functional.
In other words – things will go wrong.
Update: Special thanks to Simo Ahava for mentioning another issue. He recommends using neither dataLayer = [] nor var dataLayer = []. The most robust solution is to implement dataLayer like this:
<script>
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'customerType': 'vip',
'registrationCountry': 'United States'
});
</script>
Simo strongly recommends to always use the aforementioned syntax when working with dataLayer, regardless of where in the site you do it (before the container snippet, after the container snippet, another Javascript file):
Now let’s go back to my student’s case.

There are several problems which need to be addressed:
- You don’t need to initiate empty dataLayer (there’s no data between the brackets []) because Google Tag Manager container does that automatically for you. In the screenshot above, the empty dataLayer = [{}]; has reset (and damaged) the data layer because it is placed below the GTM container.
- Let’s imagine that the developer has to put some data to the dataLayer – “registrationCountry”. Instead of this:
script> var dataLayer =[{ 'registrationCountry': 'United States' }]; </script>It should have been done like this:
<script> window.dataLayer = window.dataLayer || []; window.dataLayer.push({ 'registrationCountry': 'United States' }); </script>Always use window.dataLayer.push instead of declaration dataLayer = []. It will not break the dataLayer (regardless of where the push happened (below or above the GTM container).
Conclusion
Is Pageview trigger not working in Google Tag Manager Preview and Debug mode? In that case, check whether the developer added the dataLayer snippet to the website correctly. In this blog post, I explained how you can easily check whether the developer did his job well.
This mistake is also described in my comprehensive guide – Most Common Google Tag Manager Mistakes. It will save you a lot of hassle so don’t forget to check it out.

19 COMMENTS
Hi Julius,
Thanks for the article - the issue is very real, indeed. However, I have some nits to pick :)
I don't really follow your logic of "must not be defined as a JavaScript variable". Both
dataLayer = []
and
var dataLayer = []
are identical statements in a non-function scope. Both initialize JavaScript variables (there are no non-JavaScript variables in JavaScript..), and both are the same as executing
window.dataLayer = [];
The difference between "var" and no "var" is apparent in function scope. In function scope, "var dataLayer" would scope the variable locally to the function, where as "dataLayer" would climb up the scope chain, trying to find a previously declared instance of the dataLayer variable. If none is found, it plants its roots in the global window scope. Since dataLayer already exists in the global window scope, "var" vs. "no var" is not really relevant.
Also, you should use neither, ever.
First, you never have to use an empty dataLayer initialization if you use GTM, since the container snippet does that for you. Including the empty initialization is redundant and can lead to problems like the one described in this article.
Second, when working with a global structure that might be governed by multiple parties (developers, GTM users, other TMS users), it's very risky to initialize dataLayer with dataLayer = []; even if it's before the container snippet. It's possible that the web server has already created a base dataLayer (e.g. ecommerce data), and having that initialization statement removes any trace of this first object push from dataLayer.
So, I recommend to always use the following syntax when working with dataLayer, regardless of where in the site you do it (before the container snippet, after the container snippet, another JS file):
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
key: 'value'
});
The first line creates dataLayer IF it hasn't already been created. If it already existed, it simply creates a reference to itself. After that, all key-value pairs are added with .push().
So I agree with your observations in this article, but I don't really agree with the suggested course of action :)
Simo
Hey, Simo,
Thanks for the detailed comment.
First of all, that's where my VERY limited knowledge of Javascript kicked in (which is very close to "zero"), thus this blog post contains several errors (I will update them asap) :) I really should have consulted my coworkers/developers before posting it.
Secondly, since I am no developer, I can be in regular marketer's shoes very easily and see the entire situation from his/her perspective (that's actually my vision behind this blog). What bothers me here is Google's documentation.
I completely trust your recommendation not to use neither "dataLayer = []" nor "var dataLayer = []". However, if I take a look at Googles's docs it clearly states that I need to paste a dataLayer snippet above the container like this:
Since it's the official developer guide, everyone should trust it as well, right? (this applies for newbies, especially).
Let's say I am just starting to work with GTM. In this case I will definitely follow Google's recommendation because I am not aware of Simo Ahava's or any other expert's blog, yet.
Maybe we should ping some guys at Google in order to fix the problem. Because that documentation looks like the source of the problem, don't you think so?
P.S. as for empty dataLayer above the container - that's totally my mistake. I accidentally skipped mentioning that point in this blog post (although I really knew it). I'll update it as well.
Thanks again for watching my back.
Yeah, in a perfect world Google's documentation would be correct, and in a perfect world they would listen to the suggestions of the user base recommending them to fix the documentation :) This isn't the only thing that's at fault in the current docs (check the tag setup recommendations in GTM's Enhanced Ecommerce documentation if you really want your jaw to drop).
That's why as a content creator and someone who does a lot of support and outreach I feel obligated to do my best to increase awareness about this. Typically when people use var dataLayer = []; and find their GTM setup breaking completely, they reach out in the Product forums for assistance, or perhaps they'll google for help and find articles such as this one.
So I completely agree - Google's documentation should be up-to-date and not full of dangerous pitfalls like this!
Thanks again for the article!
Simo
Yeah, "Jaw dropping" is a perfect term for GTM Enhanced Ecommerce documentation :)
Thanks for supporting the GTM community and raising awareness of various issues!
Hi.
I have set up Google Tag Manager for view, out bound link, and timer.. and it work supposed to be. However, when I added other trigger like form submission and Youtube Video , no trigger have been detected on preview mode.
FYI, the blog is new and no change coding have been modify since I start using Tag manager.
Do you know why only my first 3 triggers is working and why the other is not seem to work on my blog.
Never seen that behavior before. Would you mind sharing a link to that page so that I could check it out? Also, if possible, share a link to the GTM preview mode with all triggers enabled.
Hi Julius,
Thanks. Can I contact you via email? I will send for you to review it. I just scare that I do it wrongly for other triggers.
Regards
Sure, julius [@] analyticsmania.com
Done. :)
This article really helped me out of a bind. The purchase tag was firing but didn't come over with the purchase value. I had to play with the trigger settings to have it fire when the DOM Object was ready because the template variable didn't load yet.
Thanks for the help!
I am having trouble pushing custom variables to dataLayer, since they are available and hence set much lower on the page after the HEAD tag ( where the Tag Manager code is) is generated. The pageview tag is unable to read the dataLayer variables set, since it is fired before the dataLayer push happens.
I would like to know what is the best way to handle this issue.
In that case, create a Pageview - DOM Ready Trigger and use that.
We have implemented Tag Manager on our PDF Converter sales web page and always no data was send on that page. And we have been following TM documentation where it is said that dataLayer = [{}] must be added before TM container. We have replaced with push and now everything works fine! Thank you for your blog post!
Glad to help! Yeah, dataLayer.push is always the way to go. It's much more versatile and robust.
Thanks. That helped a lot.
I had var dataLayer = []; in my .js file. It was working fine.
Suddenly all events stopped firing except dataLayer.push events which I was sending through JavaScript. Page view event wasn't firing as well. I don't know why it wasn't working as the code was mostly same. Adding <script>var dataLayer = [];</script> before gtm script in head helped :)
Just want to let you know this saved me from a huge headache trying to figure out what was going on. My variables were all capturing except under "Pageview" scenarios and I was looking at the option of switching to "DOM Ready" which would cause me to compromise by losing some Analytics stats (particularly bounce detection).
I have a feeling most people who use GTM don't implement much other than sticking it on a page along with desired tags. The annoyance of not having this stuff clearly documented by Google creates is hard to put into words.
It's times like these I sometimes consider creating my own version of something. Ultimately all GTM is doing is orchestrating tag injection and custom JS into pages with a management interface for non-techs. It's not rocket science but their implementation makes it feel like it they purposefully made it that way.
Hey, thanks for the post! I have the same issue, but i dont know how to fix it, because i use the gtm4wp plugin, i am not a developer. My events are fine. But the pageview trigger does not appear in the preview and debug mode. All my pageview tags are firing in the container load trigger, this is good? Should i worry about it?
Thanks Julius!
I thought I had the same issue Ezequiel.
But checked another blogpost by Simo Ahava, and no need to worry about it, as he mentioned in comments:
"This article is a bit outdated, as GTM renamed "Page View" to "Container Loaded" a while ago."
Probably Julius should update this blog accordingly too.
Hi, the first sentence of the blog post says that :)