GTM Container Best Practices: Architecture That Scales Beyond the Person Who Built It
We inherit a lot of GTM containers. Agency containers, client containers, containers that have been through five developers and three rebrands. The ones that cause the most pain share the same problems: no naming conventions, tags named "Custom HTML 47", triggers that fire on everything, and variables nobody remembers creating.
A well-structured GTM container saves you hours of debugging, makes handovers painless, and prevents the kind of mistakes that break tracking silently. Here is how we structure containers for our clients, and how you should too.
1. Naming conventions first. Everything else second.
The single biggest difference between a good container and a bad one is whether anyone can look at a tag, trigger, or variable and immediately know what it does without opening the configuration. That requires a naming convention that everyone follows.
Our convention is simple: Type - Detail - Condition. Examples:
- Tags: "GA4 - Pageview - All Pages", "GA4 - Purchase - Thank You Page", "Google Ads - Remarketing - All Pages"
- Triggers: "Pageview - All Pages", "Click - Add to Cart Button", "Form Submit - Contact Form"
- Variables: "DLV - transactionId", "DOM - pageTitle", "Constant - GA4 Measurement ID"
Prefix variables by type (DLV for data layer variable, DOM for DOM element, Constant for constants, JSV for custom JavaScript variables). You will thank yourself when you have 80 variables and need to find the right one.
2. Folder strategy: mirror your measurement plan
GTM folders are not just for tidiness. They should reflect your measurement plan. Every major tracking category gets its own folder structure:
- GA4 - Core (pageview, session config, user properties)
- GA4 - Ecommerce (product views, add to cart, purchases, refunds)
- GA4 - Engagement (scrolls, video, file downloads, site search)
- GA4 - Conversions (form submissions, signups, leads)
- Google Ads (conversion tracking, remarketing)
- Other Platforms (Meta, LinkedIn, Hotjar, etc.)
- Utility (custom HTML helpers, consent, data layer management)
If a tag does not fit cleanly into a folder, your measurement plan might need another look. Every tag should have a clear purpose that maps to a business question.
3. One GA4 configuration tag. One.
We see containers with ten GA4 configuration tags, each firing on different conditions. This is almost always wrong. A single GA4 configuration tag that fires on All Pages is the correct setup. Every GA4 event tag should reference that single configuration tag. Multiple config tags create duplicate pageviews, inflate user counts, and make debugging impossible.
The only exception is when you need different measurement IDs for different environments. Even then, use a lookup table variable, not multiple config tags.
4. Triggers should be specific, not lazy
"All Pages" is a valid trigger for your GA4 config tag and your Google Ads remarketing tag. That is about it. Everything else should be targeted.
Bad: a Google Ads conversion tag firing on "All Pages" with a blocking trigger for the thank-you page URL. Works, but inefficient and hard to read.
Good: a trigger called "Pageview - Purchase Confirmation" that fires only on /thank-you and /order-confirmed. Clean, obvious, and self-documenting.
For click triggers, always use the built-in Click Classes or Click ID conditions rather than Click Text. Click Text breaks when the button copy changes. A CSS class is more stable.
Form triggers deserve extra care. The default form submission trigger in GTM fires on the browser's native form submit event. If your site uses JavaScript to handle forms (which most modern sites do), the default trigger will not catch it. You will need either a custom event pushed to the data layer on form submission, or a click trigger on the submit button. Test both approaches and use whichever fires reliably. The data layer approach is almost always more dependable.
For single-page applications, history change triggers replace pageview triggers for route changes. If your site uses React, Vue, Angular, or any SPA framework, make sure you have a History Change trigger configured and firing your GA4 pageview event on every route transition. Without this, GA4 will only record the initial page load.
5. Variables: fewer is better than more
We regularly audit containers with 100+ variables, half of which are unused duplicates created by different developers who did not check what already existed. Before creating a new variable, search the container. Chances are someone already built it.
Custom JavaScript variables are powerful but should be the exception, not the rule. Every custom JS variable runs on every event that fires, adding processing cost. If a lookup table or a regex table can do the same job, use it instead. It is faster and easier to debug.
6. Built-in variables: audit them
Go to Variables → Configure and scroll through the list of built-in variables. At minimum, enable:
- Click Classes, Click ID, Click Target, Click Text, Click URL
- Form Classes, Form ID, Form Target, Form Text, Form URL
- Page Hostname, Page Path, Page URL
- Referrer, New History State, Old History State
If your container has Click triggers that are not working, the first thing to check is whether the relevant built-in click variable is enabled. It happens more often than you would think.
7. Test before publish. Always.
GTM has a Preview mode. Use it. Every time. Before publishing any container change, open Preview mode, trigger every tag that could be affected, and verify that data layer pushes, consent states, and tag firing are all correct.
For larger changes, create a separate container version, test it in a staging environment, and publish only after verification. GTM does not have built-in rollback, so a bad publish is immediately live. You cannot undo it. You can only publish the previous version and wait for CDN caches to expire.
8. Document inside the container
GTM has a notes feature on every tag, trigger, and variable. Use it. Write a one-line description of what the item does and why it exists. When someone inherits your container six months from now, they will spend minutes understanding it instead of hours.
For the container as a whole, keep a README in the client's documentation. It should explain: the measurement ID, the folder structure, any non-standard implementations, the consent setup, and who to contact with questions. This is the document you wish you had received. Write it for the next person.
9. Version control and publishing discipline
GTM does not have a built-in approval workflow or branching system like Git. That makes publishing discipline even more important. Every container change should follow a consistent workflow: make changes in a new workspace, test in Preview mode, create a version with a descriptive name, and only then publish.
Version names should describe what changed, not just increment a number. "Added purchase conversion tracking" is useful. "Version 47" is not. When something breaks in production, the version history is your debugging tool. Good version names make rollbacks faster and reduce panic.
If multiple people work on the same container, use GTM workspaces to isolate changes. Each person works in their own workspace, tests independently, and merges when ready. GTM will flag conflicts if two workspaces modify the same tag, trigger, or variable. Resolve conflicts before publishing.
Keep a changelog outside of GTM as well. A simple shared document or spreadsheet that records the date, the person who published, what changed, and why. This is invaluable when auditing a container six months later and trying to understand why a particular tag was added or modified.
10. Custom templates over Custom HTML
GTM's Community Template Gallery contains pre-built tag and variable templates for most common platforms. Before writing a Custom HTML tag, check whether a template exists. Templates are sandboxed, reviewed by Google, and far easier to maintain than raw JavaScript pasted into a Custom HTML tag.
Custom HTML tags have several problems. They run with full access to the page DOM, which creates security risks. They are difficult to audit because you have to read the JavaScript to understand what they do. They do not respect GTM's built-in consent checks unless you manually add consent logic. And they are the most common source of container bloat and performance issues.
If a template does not exist for your use case, consider building a custom template yourself. GTM's template editor provides a sandboxed JavaScript environment with restricted APIs. Custom templates are more work to create initially, but they are safer, more maintainable, and reusable across containers. For agencies managing multiple client containers, a library of custom templates pays for itself quickly.
Container audit or rebuild?
If your GTM container has grown organically over years and nobody is sure what half the tags do, we can audit it. We will map every tag, trigger, and variable, identify what is active versus dead, and give you a prioritised cleanup plan. Or we can rebuild the container from scratch if the mess is too deep. Either way, you end up with a container your team can actually use.