scalpel@labs: ~/glossary/gtm-vs-gtag.mdx5 sections

GTM vs gtag.js

`gtag.js` is Google's tag that sends data straight to products like GA4 and Google Ads. Google Tag Manager puts a container in between, so you configure tags in an interface rather than in code. Plenty of sites run both at once.

extension: Scalpel Tagsupdated: 2026-08-14read_time: 2 min
less gtm-vs-gtag.mdx

Why it matters

The difference changes how you manage tags. With gtag alone, every change to what you're tracking means editing code and deploying. With GTM, you configure tags in an interface and they take effect immediately. But GTM adds complexity; gtag is simpler if you only track GA4 and Google Ads.

Many sites use both because GTM might manage most tags while gtag handles a critical event that needs to fire independently of the container.

How it works

gtag.js is Google's direct tracking tag. You paste it into your site, and it sends events straight to Google Analytics, Google Ads, Floodlight, and other Google products. Each event becomes a request to Google's collection endpoints. Configuration happens in your code, not in an interface.

Google Tag Manager is a container that wraps around your data and makes decisions. You paste the GTM snippet, which reads your dataLayer, and uses the rules you've configured in the GTM workspace to fire tags. GTM can load gtag under the hood, load third-party vendor tags, or use custom HTML and JavaScript. The container is the decision maker; gtag is one of many tools the container can use.

Many sites load GTM first, and GTM itself loads gtag as one of its tags. The two can coexist because they're independent. GTM doesn't replace gtag; they just serve different purposes.

What does not matter

You don't have to choose one or the other. Running both is normal and usually fine. In fact, GTM loading gtag is probably the most common setup in large organisations.

The gtag command API (gtag('config', 'G-XXXXX')) is different from GTM's configuration. They don't conflict; they just work on different layers.

Code example

A direct gtag.js snippet:

<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'G-XXXXXXX');
  gtag('event', 'page_view', {page_title: document.title});
</script>

This sends events straight to Google Analytics with ID G-XXXXXXX.

A GTM snippet for comparison:

<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXXX');</script>
<!-- End Google Tag Manager -->

This loads a container; the container decides which tags fire based on your workspace rules.

On the wire, a gtag hit to Google Analytics looks like:

GET /g/collect?v=2&tid=G-XXXXXXX&cid=123&en=page_view HTTP/1.1
Host: www.google-analytics.com

A request from GTM:

GET /gtm.js?id=GTM-XXXXXXX HTTP/1.1
Host: www.googletagmanager.com

GTM downloads its container configuration first, then fires tags based on rules.

How Scalpel shows it

Scalpel Tags detects both gtag hits and GTM container loads. If it sees gtm.js?id= requests, it identifies GTM. If it sees /g/collect?v=2 requests, it identifies gtag for GA4. You can run both; Scalpel decodes tags from both sources.

Sources