If you’ve used Google Analytics or Google Tag Manager much in the past few years, you’ve probably felt the frustration: Google product teams seemingly have a penchant for abruptly changing recommended implementation approaches, often without clear migration paths or adequate documentation.
Many of us who implemented GA4 early followed Google’s original guidance — set up the GA4 Config tag as a setup tag on all GA4 Event tags — only to later succumb to alert fatigue when logging into GA4 properties and GTM containers suggesting our implementations were now “broken.” If you’re looking for a way to get rid of these pesky warnings, join the club! However if you’re just a practitioner trying to divine Google’s current Best Practices, then also join me on this journey as I try to figure it out myself.
Shifting sands
Since the App + Web days, Google’s GA4 implementation guidance has evolved in pretty significant ways:
- Since the early days of Google Tag Mananger, Google has recommended using
dataLayer.push()
to send data to and trigger tags in GTM. - Google developed a library called the Global Site Tag (or gtag) to align tagging for their products.
- When Google released Analytics App + Web (now GA4), they recommended adding the GA4 Config tag as a setup tag (a tag that fires before other tags) for all GA4 Event tags.
- In 2022, Google renamed the Global Site Tag to just “The Google Tag”, and abruptly pivoted to recommending
gtag('event')
instead ofdataLayer.push()
to send data to GTM. - Google changed the name of the GA4 Config tag to “the Google Tag” and added new variable types.
- Google now recommends configuring the Google Tag to fire on the Initialization - All Pages trigger (the
gtm.init
system event that fires when GTM loads) instead of as a setup tag. - Implementations which followed the original guidance now trigger annoying “misconfiguration” alerts.
- GA4 and GTM documentation now defaults to using
gtag
instead ofdataLayer.push
.
I think it’s instructive to consider why Google made these changes in order to understand how best to implement GTM and/or the Google Tag correctly today.
The Google Tag, the Google Tag tag, gtag, Google Tag Manager
Google’s naming choices create confusion even among experienced practitioners. Here’s what each term means:
-
The Google Tag (aka
gtag.js
) is the JS library that sends data to Google Analytics, Google Ads, and Google Campaign Manager (via Floodlight). Previously called “Global Site Tag.” -
The Google Tag tag type is a specific tag template within Google Tag Manager that configures how
gtag.js
behaves for your properties. -
Google Tag Manager (GTM) is the platform that lets non-developers deploy and manage tracking tags (including the Google Tag!) without developer involvement.
Timeline: How We Got Here
Understanding the timeline helps explain why many implementations now show warnings:
- Starting in August 2022, Google renamed the Global Site Tag to just “The Google Tag”, and quietly started recommending using it to send ecommerce data instead of using the
dataLayer.push()
method, on which countless data layer specs and solution designs are based.wait ... what? - In early September 2023, Google renamed the GA4 Config tag to the Google Tag (…tag?). In their announcement, Google noted that “[y]our measurement and capabilities will work just as before and you don’t need to take any action.” I took this with just a pinch of salt.
no action will be required, eh? - Around May of 2024, Google started serving notification banners in GA4 properties and GTM containers flagging implementations that followed their previous guidance as “misconfigured.” (Note that these warnings are often vague or false alarms.)
jk surprise! - On March 10, 2025, Google sent an email to all users of Google Tag Manager that Google will auto-inject a Google Tag for each solution a container uses. This caused some kerfuffle in the community.
simpler and more efficient guys
Now, if you had earlier paid heed to Google’s “no action required” guidance, these warnings understandably caused concern. Google’s current documentation now recommends firing the Google Tag on the Initialization - All Pages trigger (gtm.init
system event) rather than as a setup tag for GA4 Event tags. So what happens to implementations that still use the old convention? Unfortunately, there’s no mention in the GA4 documentation about this — however it’s implied that you need to update your containers to accommodate this change.
Understanding Google’s Strategy
Google’s gtag
/Google Tag push came as an unwelcome surprise to many implementation engineers. After all, Google had invested heavily in building and documenting the dataLayer
approach for consistent data collection.
However, examining Google’s broader strategy reveals why these changes make sense from their perspective:
1. Google has been trying for years to unify tagging for their products
Before gtag
, every Google product (Analytics, Ad[Word]s, and Floodlight) had its own JS snippet and tagging requirements. It takes a lot of resources to manage three libraries, each with its own nuances and quirks. Three different sets of documentation, multiple teams, etc. Why not just have a single JS SDK? So, in 2017, Google released the Global Site Tag (gtag.js
). This was to be the One Tag to rule them all. But there was one which refused to bow. GTM had relied on dataLayer.push
since its inception to send data to it. So Google effectively has had to manage two different implementation approaches that at times conflicted with each other. The solution? Make gtag
a wrapper for dataLayer.push
. This allowed backwards compatibility with implementations using dataLayer
(which is most of them) while getting them closer to a single SDK to send data to all of its products.
2. The GA4 Config tag in GTM was basically just running gtag('config')
for your GA4 property
Most sites that have GTM containers don’t just have GA4 tags — they also have tags for Google Ads, Floodlight, etc. Some sites with GTM containers don’t have GA4; they use GTM for managing their Google Ads or Floodlight tagging. However, all of them use gtag.js
— even if the Google Tag type isn’t in the container.
Remember: the Google Tag is gtag
. When you add a Google Ads Conversion tag or Google Analytics tag to your container, gtm.js
will automatically load gtag.js
when it runs so it can configure the tags and orchestrate calls. This has been the case for years. Even if you hadn’t included the GA4 Config tag, gtm.js would still load gtag.js with Google’s preferred defaults. Adding the Google Tag tag (fuck) to the container does two things: 1) it tells gtm.js to load gtag.js sooner; 2) it allows you to change the default configuration for your Google products. When you think of it from this perspective, Google just picked a name for that tag (albeit an unhelpful one) that more accurately reflects what it is — although a better name might have been the Google Tag Config tag, since you don’t have to add a Google Tag tag to a container to load the Google Tag. (If you’re able to wrap your head around that, bravo!)
3. Reducing implementation complexity and errors
Data layer implementations often suffer from translation issues between analytics architects and engineering teams, even when detailed specifications are provided. One common sticking point is neglecting to clear the ecommerce object before new ecommerce events, which can lead to data pollution.
While developers understand the technical reasons for this requirement, it’s frequently missed or questioned during implementation. The gtag
approach simplifies this by automatically handling ecommerce data clearing, reducing a common source of tracking errors.
Yes, gtag
appears more proprietary than dataLayer
, but in practice, most dataLayer
implementations are proprietary anyway — you can’t simply swap out Google Analytics for Adobe Analytics and expect your dataLayer
implementation to “just work” (at least, not without gtag
).
While gtag
doesn’t replace change management or interpersonal skills, it does provide a single SDK that works for multiple Google products, and that can simplify things.
Should you update your implementation?
If you’re seeing warnings in GA4 or GTM about misconfigured Google Tags, they may not necessarily indicate a problem — many times, the “Urgent” issues with your GTM container are just that Google’s expecting you to tag your lower environments. (Google product owners: if you’re reading this, look up “alert fatigue”.) Even if you’re still using the old setup tag method, you may not necessarily need to update to the new approach. Anecdotally I’ve seen a number of implementations using the old approach that are still working fine.
Here’s how to actually decide whether to take action:
⚠️ Update immediately if you have:
- Attribution, sessionization, or audience issues in GA4
- Issues with Google Ads conversion goals, retargeting lists, or enhanced conversions
⏸️ Update when you can if you have:
- A working implementation
- No issues with sessions or attribution
- Complex custom implementations that would be difficult to migrate
- Limited development resources
Current good practices for Google Tag implementation
I’m not a fan of the term “best” practices, because what’s best for one site may not be for another. Regardless, whether you’re optimizing an existing implementation or starting fresh, here are some general guidelines I’ve found for implementing the Google Tag:
GTM Configuration
-
Fire the Google Tag on the Initialization - All Pages trigger. This is Google’s current recommendation. The Google Tag in GTM is basically the gtag(‘config’) command. If the config command happens too late, it can mess up attribution or sessionization.
-
Add one Google Tag tag for each Google product you use. That means if you have Google Analytics, Google Ads, and Floodlight, then add three Google Tag tags. If you have tags for more than one Google Ads account/conversion ID on your site, add one for each of them (you can share config options with the Configuration Settings variable). This is not required — Google will automatically inject
gtag.js
and the requisitegtag
commands for each product you use — however, adding one for each product/account allows you to configure them and givesgtag.js
more time to load. -
Use Enhanced Measurement whenever possible. My hot take (which I stole from Ken Williams). For better or worse, Google has done a lot of work with GTM to minimize the need for developer involvement. You can use Enhanced Measurement to track page views, form submissions, scroll depth, YouTube video views, and more. Even if you don’t use Enhanced Measurement, you can use an array of built-in trigger and variable types that reduce the need for bespoke cleverness. Engineers have a tendency to complicate, and complexity often results in brittle implementations. Only customize when you have a legitimate reason to do so.
When should you customize?
- If you have an ecommerce site, you need developers to add gtag or dataLayer.push calls for all ecommerce events.
- If you’ve enabled Form interactions, but the built-in
gtm.formSubmit
events don’t fire when you submit your forms. - If you embed videos from providers other than YouTube on your site.
- Custom events not covered by automatic or enhanced measurement
These are just a few situations where you need to ask a developer to add gtag('event')
or dataLayer.push()
calls. Knowing when to customize comes with experience. As a general guideline though, try starting with what comes out of the box and work from there.

-
Use a Google Tag: Configuration Settings variable to set all
gtag
config settings. Even if you only have one Google Tag in your container, having the config settings in a separate variable means you don’t have to touch the Google Tag when you just want to update config parameters. Be careful about sharing Configuration Settings variables between products — not allgtag('config')
parameters are supported by all products! -
Use a Google Tag: Event Settings variable to manage all global event settings. This variable should be included in all GA4 Event tags as well as the Google Tag. Then set any event-specific parameters on their respective GA4 Event tag.
Data Layer Best Practices
Google recommends that you use gtag('event')
instead of dataLayer.push()
, but it will still support dataLayer.push()
for the foreseeable future.
You absolutely don’t need to replace dataLayer.push()
with gtag('event')
in existing codebases — but when a product team starts talking about replatforming, weigh the pros and cons of using gtag
instead. Recall that gtag
is (among other things) a wrapper for dataLayer.push()
.
-
Make sure all page and user data is in the data layer prior to the
gtm.init
event. This makes e.g.user_id
available on Initialization when the Google Tag needs it. -
Only send a manual
page_view
event message if absolutely necessary. Start with Enhanced Measurement page views, and only send thepage_view
event separately if you need more control over when thepage_view
fires. If implementing on a site that uses a SPA framework, make sure to enable “Fire on history changes” in the Enhanced Measurement config screen. There are always tradeoffs, but manually instrumentingpage_view
is fraught with peril and can stymie even experienced teams. -
Clear the ecommerce object before each ecommerce event message. Use the following pattern to prevent data persisting across calls (note that you don’t need to do this with
gtag
):
window.dataLayer = window.dataLayer || [];
dataLayer.push({ ecommerce: null }); // Clear previous ecommerce data
dataLayer.push({
event: 'view_item',
ecommerce: {
item_id: 'ABC123',
item_name: 'The Google Tag Tag Type T-Shirt',
/* etc. */
}
});
Key Takeaways
For existing implementations:
- You don’t need to switch to the new approach unless you’re having issues with sessions or attribution
- Google maintains backward compatibility for
dataLayer.push()
implementations - The warning banners in GA4 and GTM don’t always mean you need to take action
For new implementations:
- Fire Google Tags on Initialization - All Pages, not as setup tags
- Use Google Tag: Configuration Settings and Event Settings variables
- Start with Enhanced Measurement and only customize when necessary
- Consider
gtag('event')
instead ofdataLayer.push()
for new ecommerce implementations
The bigger picture: Google’s changes, while disruptive and often leaving much to be desired in terms of communication, reflect a long-term strategy to streamline data collection across their products. Understanding the reasoning behind these changes helps us make better implementation decisions and anticipate future developments.
Whether you’re updating an existing implementation or starting fresh, focus on the fundamentals: accurate data collection, proper timing, maintainable configuration, and minimal complexity. The specific method matters less than getting these basics right.
Got suggestions or questions about your specific implementation? Reach out!