The guide
A tag container is a small program, so plan it like one before anyone builds it
Tags, triggers and variables look like settings, but together they decide what a site sends and when. Writing the plan down first is how the next person gets to read it.
- 3 building blocks tags, triggers and variables make up every container
- 1 blocking trigger is enough to stop a tag, however many firing triggers are true
- 120 against 100 the order value and the threshold in the worked example
- 65536 paths a naive matcher can try for one short pattern and 16 letters
Read the full guide Fold the guide
A tag manager exists to take a chore away from developers. Before such systems appeared in the late 2000s, adding or changing a tracking snippet meant editing the source code of the site, which was slow and made every campaign wait for a release. The answer was a single container tag placed on every page. Inside it, individual tags are fired according to rules that someone sets in a web interface, with no further code release.
That convenience hides what a container really is. It is a program, written by several people over several years, in a language made of dropdowns. It has inputs, conditions, an order of execution and side effects that reach other companies. Unlike most programs, it usually has no specification, no comments and no author who still works there. A container deserves a written plan for the same reason any program does.
The 3 building blocks
A container has 3 building blocks. A tag is the thing that does something: it sends an event to an analytics product, requests a tracking picture, or injects a piece of custom markup. A trigger says when. A variable supplies a value, either to a trigger that needs something to test or to a tag that needs something to send. A saved version of a container is described by the platform’s API reference as exactly these lists, plus folders, built-in variables and a few specialised kinds.
Variables are the quiet part. Most of them read something that already exists on the page. A page address has a scheme, a host, a port, a path, a query and a fragment, and a variable can pick out any one of them. A cookie is a name and a value, sent as pairs, so a variable can ask for one by name. A constant holds a measurement id in one place. A lookup table turns one variable’s value into another, and custom code can compute anything at all.
Triggers come in kinds that follow the life of a page. The page starts to load. The markup has been completely parsed, which happens before images and subframes have finished. The whole window has loaded. Someone clicks, or submits a form. A timer ticks. The address changes without a page load, as it does when a script pushes a new entry onto the session history. Or the page itself announces a custom event by name.
On top of its kind, a trigger carries a list of conditions, and it fires only if every one of them is true. Each condition has a variable on the left, an operator in the middle and a value on the right. The reference lists the operators: equals, contains, starts with, ends with, matches a regular expression, and four numeric comparisons. Any of them can be negated, and the regular expression can be told to ignore case.
The tag’s own rule is short enough to memorise. A tag fires when any one of its firing triggers is true and none of its blocking triggers is. So 1 blocking trigger is enough to stop a tag, however many firing triggers are true. Tags also carry a priority, where the higher number goes first, and an option that limits them to once per event or once per page load.
The data layer is a contract
Tags could scrape what they need from the page. The better arrangement is a data layer: one object that the site fills in an organised, predictable way, so that tags do not have to parse values scattered around the markup. The site pushes a message, the container reads it. Messages are handled first in, first out, one at a time, and when a message is an event, the tags whose trigger conditions are met fire before the next message is looked at.
That makes the data layer a contract between two teams. The site promises a key called something like order value, with a number in it, at the moment an event named purchase is pushed. The container promises to do something sensible with it. Its shape is plain: text, numbers, true and false, null, objects and lists, the same handful of types that JSON defines. Its failures are plain too. The name of the data layer object is case sensitive, and a key spelled two ways on two pages is, to a tag, two different keys.
Two timing rules cause most surprises. A value pushed under a name that already exists overwrites the earlier value. And a value lasts only as long as the visitor stays on the page, so anything needed on the next page has to be pushed again there. A trigger that reads a key nobody has set yet is not wrong, exactly. It is simply reading nothing, and what an operator makes of nothing is a detail worth knowing before launch.
A worked example: 120 against 100
Take a small shop. A purchase tag fires on a custom event named purchase. An advertising pixel fires on a second trigger: the same event, with the condition that the order value is greater than or equal to 100. Both tags are blocked for staff, who carry a cookie. Both are marked as needing consent, the purchase tag for analytics storage and the pixel for the advertising types.
Now walk a visit through it. The visitor grants analytics storage only and completes an order worth 120. Both triggers are true, since the event name matches and the test is 120 against 100. The purchase tag fires. The pixel does not, because consent for advertising storage has not been granted. Later the visitor grants everything and orders again, for 45. The purchase tag fires once more. The pixel stays silent again, for a different reason: 45 is not at least 100, so its trigger was never true.
Three outcomes, three different causes, and none of them visible in a list of tag names. This is the case for simulating a plan on paper.
The interesting question is rarely whether a tag fires. It is why it did not, and whether that reason is the one the author intended. The example above loads when the tool on this page opens, and changing 45 to 145 changes the answer.
Why containers rot
Nothing in a container ever objects to being left behind. A campaign ends and its trigger stays. A variable is created for a tag that is never built. Someone who cannot find the existing purchase trigger makes a second one with a slightly different name, and from then on half the tags use one and half the other. A trigger no tag uses, a variable nothing reads, two triggers with identical settings: each is harmless alone, and together they make the container unreadable.
Names are the cheapest defence. A rule as simple as a fixed prefix for each kind of item, applied without exception, lets a reader sort a hundred items by eye. The platform offers folders and a free notes field on every tag, trigger, variable and folder. Used consistently, the notes field is where the reason for a blocking trigger survives the person who added it.
Regular expressions deserve a warning of their own. They are the most flexible operator and the easiest to get wrong. Common engines try alternatives by backtracking, and a careless pattern can make the work explode. The security literature gives a small case: the pattern of a repeated group of repeated letters, tested against sixteen of the letter a followed by an X, leaves a naive matcher with 65536 paths to try, and the number doubles with each extra letter. Engines also add extras beyond the basic syntax, such as look-ahead and named groups, and those extras are where patterns stop being portable. A pattern in a plan should be simple, anchored and tested.
Custom markup and custom code are the other soft spot. They can do anything, which means nobody can tell from the outside what they do. The platform’s own documentation warns against using custom markup tags to deploy its tag library or to configure consent settings. A plan should record such code as text, name its owner, and treat it as unreviewed until someone has read it.
Consent is a gate, not a label
In the European Union, the privacy directive for electronic communications allows storing information on a person’s device, or reading information already stored there, only when the person has consented after clear and comprehensive information. The exception is storage that is strictly necessary for a service the person asked for. Many tags store or read exactly that kind of information. Other places have other rules, and none of this is legal advice.
The platform expresses consent as named types that are each granted or denied; its guide shows four, covering advertising storage, advertising user data, advertising personalisation and analytics storage. A tag can be marked so that the runtime checks the types it names before the tag is allowed to fire. Writing the need next to each tag in the plan, and simulating a visit where consent arrives late or never, shows which tags depend on the gate and which were simply forgotten.
Writing it down for the next person
A useful build sheet reads in the order someone would build it: variables first, then triggers, then tags, each with its settings in plain words. It says what fires each tag, what blocks it and which variables it reads. It includes the expected result of a test visit. If it travels as a spreadsheet file, quotes and line breaks must be enclosed properly, and any cell that starts with an equals sign, a plus, a minus or an at sign needs guarding, because a spreadsheet will read it as a formula.
What a plan cannot do should be said as plainly. A simulation follows the rules its author wrote down, and the real container follows the platform’s engine, whose every detail the public reference does not spell out. The API reference names the operators without saying how each one treats case, numbers or a missing value, so any planner has to state its own assumptions. It cannot run custom code, see the live site, or know what a vendor does with what it receives. The platform’s own preview remains the final check.
A plan earns its keep anyway. It turns a pile of dropdown choices into something that can be read in order, argued over and handed on. It makes the gaps visible before they become missing data. And it leaves, for whoever opens the container two years from now, an answer to the only question they will really have: what was this supposed to do?
Sources
Every source consulted for this guide. Links checked October 11, 2026.
- Tag management system Wikipedia
- The data layer Google for Developers
- REST Resource: accounts.containers.workspaces.tags Google for Developers
- REST Resource: accounts.containers.workspaces.triggers Google for Developers
- REST Resource: accounts.containers.workspaces.variables Google for Developers
- Condition Google for Developers
- REST Resource: accounts.containers.versions Google for Developers
- REST Resource: accounts.containers.workspaces.folders Google for Developers
- Set up consent mode on websites Google for Developers
- Directive 2002/58/EC of the European Parliament and of the Council of 12 July 2002 (Directive on privacy and electronic communications), consolidated text of 19 December 2009 EUR-Lex, Publications Office of the European Union
- Regular expression Denial of Service - ReDoS OWASP Foundation
- Regular expressions MDN Web Docs
- Document: DOMContentLoaded event MDN Web Docs
- HTML Standard WHATWG
- URL Standard WHATWG
- RFC 6265: HTTP State Management Mechanism RFC Editor
- RFC 8259: The JavaScript Object Notation (JSON) Data Interchange Format RFC Editor
- RFC 4180: Common Format and MIME Type for Comma-Separated Values (CSV) Files RFC Editor
- CSV Injection OWASP Foundation