How to Audit Google Tag Manager Container Weight

This guide is a hands-on audit within Third-Party Script Performance, part of JavaScript Bundle Optimization & Code Splitting. Google Tag Manager is often the single largest source of third-party JavaScript on a site, and the hardest to see: the container is configured in a web UI by people outside engineering, tags are added for campaigns and rarely removed, and every tag that fires on "All Pages" runs on every page view.

The container file itself grows with every tag, trigger and variable — 100–300KB is common for mature containers — and that is before the tags it injects load their own scripts. The execution cost lands on the main thread around page load and, through click and form triggers, inside user interactions. An audit makes the cost visible per tag, finds what can be removed, and moves the rest off the critical path.

Layers of GTM cost The layers that make up the cost of a Google Tag Manager setup, from the container file to the tags' own scripts and their triggers. Layers of GTM cost Container (gtm.js) size grows with every tag, trigger and variable — downloaded and parsed on every page Built-in tags GA4, custom HTML — execute inside the container's tasks Injected vendor scripts pixels, heatmaps, chat, testing tools — each with its own download and execution Triggers click, form, scroll and timer triggers attach listeners that run during interactions

Rapid Diagnosis

  • Measure the container file. Network panel: gtm.js?id=GTM-XXXX transfer and decoded size.
  • Count injected requests. Filter by initiator gtm.js or by third-party domains; note bytes and requests.
  • Profile main-thread cost. In a throttled trace, group Bottom-Up by domain; attribute googletagmanager.com and the vendors it loads.
  • Use Tag Assistant / preview mode to list tags fired on a page view and on common interactions.
  • Export the container JSON (Admin → Export Container) to audit tags, triggers and variables offline.

Root Cause Analysis

1. Tag accumulation. Campaign pixels, retired A/B tests and duplicate analytics tags pile up; nobody owns removal.

2. "All Pages" triggers. Tags needed on a few pages fire everywhere.

3. Custom HTML tags. Arbitrary script injected synchronously, often with document.write or heavy inline code.

4. Interaction triggers. "All Elements" click triggers and form listeners run on every click, adding processing time to interactions — directly visible in INP.

Main-thread time by GTM-managed tag (mid-tier phone, page load) Bar chart of main-thread time attributed to individual tags loaded through a Google Tag Manager container. Main-thread time by GTM-managed tag (mid-tier phone, page load) Container evaluation 88ms Heatmap / session replay 164ms Two ad pixels 72ms Legacy A/B test (ended) 58ms GA4 41ms 50ms

Step-by-Step Audit

1. Inventory tags with owners and purpose

From the exported JSON, list every tag with its type, triggers and last-modified date. Ask an owner for each; anything without an owner or purpose is a removal candidate.

javascript
// node audit-gtm.mjs container.json — list tags with triggers.
import fs from 'node:fs';
const c = JSON.parse(fs.readFileSync(process.argv[2], 'utf8')).containerVersion;
const triggers = Object.fromEntries((c.trigger ?? []).map((t) => [t.triggerId, t.name]));
for (const t of c.tag ?? []) {
  console.log([t.name, t.type, (t.firingTriggerId ?? []).map((id) => triggers[id] ?? id).join(' | ')].join('\t'));
}
// trade-off: the export does not show what each tag injects at runtime.
// Combine this list with a network capture per page type to see real cost.

Expected outcome: a spreadsheet of tags, triggers, owners and status.

2. Measure each tag's runtime cost

Use preview mode with a throttled trace, or temporarily pause tags in a workspace and compare traces. Record bytes and main-thread milliseconds per tag on your heaviest template.

3. Remove, narrow and defer

  • Remove tags for ended campaigns and experiments, duplicates, and tags without owners.
  • Narrow "All Pages" triggers to the pages that need them.
  • Defer non-essential tags to "Window Loaded" plus a timer, or fire them from a custom event dispatched after the page is idle.
javascript
// Fire a custom event after load + idle; use it as the trigger for non-critical tags.
addEventListener('load', () => requestIdleCallback(() => {
  window.dataLayer = window.dataLayer || [];
  window.dataLayer.push({ event: 'idle_ready' });
}, { timeout: 4000 }));
// trade-off: tags on idle_ready miss users who leave within the first seconds.
// Keep conversion-critical tags on page view; move only non-critical ones.

Expected outcome: fewer tags at load, and the remaining non-critical ones out of the startup window.

4. Replace interaction triggers that run on every click

Swap "All Elements" click triggers for narrowly scoped ones (specific CSS selectors), and push explicit dataLayer events from your own code for important interactions. That removes container work from every unrelated click's processing time.

GTM audit sequence Four steps of a Google Tag Manager container audit from inventory to scoped triggers. GTM audit sequence Inventory and assign owners Export container JSON; every tag needs an owner and purpose Measure cost per tag Throttled traces with tags paused one at a time Remove, narrow, defer Dead tags out; page-scoped triggers; idle_ready for non-critical Scope interaction triggers Selector-specific clicks or explicit dataLayer events 1 2 3 4

Verification

Compare the container size, injected requests and main-thread time before and after on the same templates. Lab TBT should fall, and in the field, LoAF attribution should show fewer slow interactions with googletagmanager.com or vendor scripts as the top script — see finding slow scripts with LoAF script attribution. Confirm with marketing that required data still arrives.

Worked Example: A Retail Container With 74 Tags

A retailer's container held 74 tags, 31 firing on all pages. The audit found 19 tags for ended campaigns, two duplicate GA configurations, a session-replay tool licensed for a pilot that had ended, and an "All Elements" click trigger used by a single tag. After removals, narrowing and deferral, 12 tags fired at page load. Container size dropped from 280KB to 160KB, injected third-party bytes at load fell by 540KB, lab TBT on product pages dropped by 310ms, and field INP p75 improved from 290ms to 210ms. Marketing reporting was unaffected.

Common Mistakes

  • Auditing once. Containers regrow; schedule quarterly reviews with the tag owners.
  • Deferring conversion pixels. Some tags must fire on page view for attribution; agree on the list with marketing.
  • Leaving Custom HTML tags with document.write. It is blocked or slow in modern browsers; replace them.
  • Testing only the homepage. Templates differ; audit the top templates by traffic.

Edge Cases

Server-side tagging. Moving collection to a server container reduces client-side vendor scripts, though the web container and some tags remain. It is the most effective structural change for heavy setups.

Consent mode. With consent mode, tags may load but behave differently before consent; measure both pre- and post-consent states.

Multiple containers. Sites sometimes load two containers (agency and in-house). Each adds its own base cost; consolidate where possible.

SPA page views. History-change triggers can fire tags on every client-side navigation, re-running heavy tags far more often than expected.

FAQ

Does loading GTM async make it harmless?

Async prevents it from blocking HTML parsing, but its evaluation and the tags it fires still run on the main thread during load, and click triggers still run during interactions. Async addresses only one of several costs.

Can GTM run in a Web Worker with Partytown?

Partly. Partytown can run many tags off the main thread, but compatibility varies and some tags need synchronous DOM access. Evaluate per tag; Partytown vs async script loading for tag managers compares the approaches.

Who should own the container's performance?

Shared ownership works best: marketing owns which tags exist and why, engineering owns performance budgets and reviews additions. A simple rule — every new tag needs an owner, a removal date and a performance check — prevents most regrowth.

Is the container size itself a problem?

Yes, modestly: it is downloaded and parsed on every page. Removing unused tags, triggers and variables shrinks it, and each removal also removes runtime work, which matters more.

What metrics should the audit report to stakeholders?

Report a before-and-after table for the top templates: container size, injected third-party bytes, number of tags firing at load, lab TBT, and field INP p75. Add the list of removed tags with their former owners' sign-off. Concrete numbers and named decisions make the next audit easier to justify and harder to undo.