Building and Testing a GTM Custom Template, From Code to Gallery
Build a GTM custom tag template in one workflow: sandboxed JavaScript, narrow permissions, unit tests with mocks and the files the template gallery needs.
6 min readBranislav Mateas
Copy post as Markdown
On this page
A custom HTML tag can do anything the page can do. That is the problem. A custom template runs in a sandbox, declares what it is allowed to touch, and can carry its own tests. Google describes templates as a safer alternative to custom HTML tags and custom JavaScript variables.
This guide builds one small tag template from start to finish. The example is hypothetical: a pixel that sends an account ID and an event name to https://collect.example.com/p. The code and tests below were written against Google's documentation but were not run in a live container, so treat them as a starting point and run them yourself.
Sandboxed JavaScript: what changes compared to a custom HTML tag
Template code runs in sandboxed JavaScript, a subset based on ECMAScript 5.1 with a few ES6 features such as arrow functions, const and let. It does not run in the normal global environment, so window, document and setTimeout are not available. There is no new keyword and functions cannot use this.
Everything the code needs comes from require(). Field values arrive in a single data object, keyed by field name. A tag template must report its outcome by calling data.gtmOnSuccess() or data.gtmOnFailure(). Tag Manager uses these for tag sequencing and preview mode.
Digital analyst and developer helping teams turn unreliable measurement into evidence they can use.
In the editor, create a new tag template under Templates. Fill in the Info tab (name, a description of 200 characters or less, icon). On the Fields tab, add two text inputs: accountId and eventName. Then write the code:
The getType check matters. Google's own tests guide shows a template that crashes on an undefined field until a type check is added. A template should fail cleanly, not throw.
Permissions: let the editor detect them, then narrow them
You do not write permissions by hand. When you save, Tag Manager detects the required permissions from the APIs the code uses. This template needs two:
API
Permission
Setting for this example
sendPixel
send_pixel
Allowed URL match pattern: https://collect.example.com/p
logToConsole
logging
Keep the default: log only in debug and preview
encodeUriComponent and getType need no permission. Narrow each setting to what the template actually needs. A pattern for one host and path is easier to review than a broad one. The permissions page does not document wildcard matching rules in detail, so test any pattern you widen.
Permissions are also the most readable summary of what the code can do, for anyone who reviews the template later. A template that asks for access_globals or inject_script without an obvious reason invites questions.
Writing tests with mocks
The Tests tab holds unit tests in sandboxed JavaScript. Test APIs such as runCode, mock, assertThat and assertApi need no require() (API reference). runCode(data) runs the template with the object you pass as data. mock(apiName, returnValue) replaces an API only in test mode. Mocks reset before each test. If the return value is a function, it runs in place of the API.
// Test 3: missing account ID never sends a request
runCode({eventName: 'sign_up'});
assertApi('sendPixel').wasNotCalled();
assertApi('gtmOnFailure').wasCalled();
// Test 4: missing event name falls back to page_view
let sentUrl;
mock('sendPixel', (url, onSuccess, onFailure) => {
sentUrl = url;
onSuccess();
});
runCode({accountId: 'A1'});
assertThat(sentUrl).isEqualTo('<https://collect.example.com/p?id=A1&ev=page_view>');
The expected URL in test 1 is worth checking by hand. encodeUriComponent('ABC 123') turns the space into %20, giving ABC%20123. sign_up has no reserved characters, so it stays the same. The gtmOnSuccess and gtmOnFailure assertions follow the pattern in Simo Ahava's guide to template tests, which recommends that every tag template test whether gtmOnSuccess was called.
These four tests cover each exit from the code: success, endpoint failure, invalid input and the default value. That is a useful rule for small templates. Count the places where the code calls gtmOnSuccess, gtmOnFailure or returns, and write at least one test for each.
What the tests cannot prove
Google lists two limitations. Unit tests do not check field validation rules, so use Run Code for those. Permission checks do not happen on mocked APIs. Test 1 passes even if the send_pixel pattern is wrong, because the mock never asks for permission.
So the tests check logic, not deployment. Close the gap with two more steps:
Click Run Code in the editor and read the Console for errors, as the template tutorial describes.
Add the tag to a workspace, fire it in Preview mode and confirm the request in the browser's network panel. The steps in debugging with Preview mode and container exports apply here. A blocked request in Preview, while the tests pass, usually points to the permission pattern. This is an inference from the documented limitation, not a documented error message.
Preparing the template for the Community Template Gallery
Publishing is optional. If you plan to share the template, the gallery documentation sets these requirements:
In the Info tab, agree to the Community Template Gallery Terms of Service.
Export the template and name the file template.tpl. Add a categories array with one to three values, such as ANALYTICS, to its ___INFO___ section.
Put template.tpl, metadata.yaml and LICENSE at the root of a Git repository, on the main branch. One template per repository.
LICENSE must be in capitals and contain only the Apache 2.0 licence, with your year and name filled in.
Enable Issues on the repository. Each gallery entry links to them for bug reports. Turn on email notifications too, so you hear about issues raised during review.
The metadata.yaml file points to a specific commit of the template:
Because sha must be the commit that contains the template.tpl you publish, the practical order is two commits: first the template, then metadata.yaml with that commit's SHA. Submit the repository URL at tagmanager.google.com/gallery under Submit Template.
For updates, add a new sha entry at the top of the versions list. Google says an update usually appears in the gallery within 2 to 3 days. Before each release, run all tests and the Preview check again. A change to the code can need a new permission, and the tests will not catch that.