Built-in GTM variables cover the basics, but the moment your tracking gets serious, you hit their limits. Custom JavaScript variables — the function() { return ... } blocks you can write directly in the GTM interface — are how experienced implementers solve problems that would otherwise require developer help. Here are 10 recipes you can copy, paste, and adapt today.
What Is a Custom JavaScript Variable?
A Custom JavaScript variable in GTM is a self-invoking function. GTM calls it at runtime and uses whatever value it returns. The function has access to the global window object, the DOM, cookies, and anything else available in the browser scope at the moment the tag fires.
You create one under Variables → New → Custom JavaScript. The boilerplate looks like this:
function() {
return /* your value here */;
}
Keep them focused: one variable, one value. Debugging becomes a nightmare when a single variable tries to do five things at once.
The 10 Recipes
1. Read a Cookie Value
GTM's built-in 1st Party Cookie variable only handles simple cases. When you need to parse a cookie from a string that may contain encoded characters or multiple values, do it yourself:
function() {
var name = 'your_cookie_name';
var match = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;]*)'));
return match ? decodeURIComponent(match[1]) : undefined;
}
2. Extract a URL Query Parameter
GTM has a built-in for this, but it fails when the URL is encoded or when you need a parameter from a non-current URL (like a referrer stored in a data layer). Roll your own when that happens:
function() {
var param = 'utm_campaign';
var url = window.location.search;
var match = url.match(new RegExp('[?&]' + param + '=([^&]*)'));
return match ? decodeURIComponent(match[1].replace(/\+/g, ' ')) : undefined;
}
3. Get a Value from localStorage
User preferences, A/B test assignments, and session identifiers are often stored in localStorage. Retrieve them like this:
function() {
try {
return window.localStorage.getItem('your_key');
} catch(e) {
return undefined;
}
}
Always wrap localStorage access in a try/catch — some browsers in private mode throw exceptions on access.
4. Detect Login State
If your site adds a class to the body or sets a global JS variable when a user is logged in, you can expose that to GTM:
function() {
if (window.currentUser && window.currentUser.id) {
return 'logged_in';
}
return 'guest';
}
Use this to segment your analytics events by authentication state without asking your dev team to push anything to the data layer.
5. Read a Meta Tag Value
CMS platforms and e-commerce platforms often inject page metadata into <meta> tags. This recipe grabs a named meta tag's content attribute:
function() {
var el = document.querySelector('meta[name="page-type"]');
return el ? el.getAttribute('content') : undefined;
}
6. Calculate Days Since a Date
Useful for lifecycle tracking — how long has a user been a customer, or how many days since signup:
function() {
var signupDate = new Date(window.dataLayer[0].user_signup_date);
var today = new Date();
var diff = today - signupDate;
return Math.floor(diff / (1000 * 60 * 60 * 24));
}
7. Get the Active Experiment Variant
If you're running A/B tests via a third-party tool that exposes variants globally, this pulls the active variant:
function() {
try {
var experiments = window.google_optimize && window.google_optimize.get('EXPERIMENT_ID');
return experiments || 'control';
} catch(e) {
return 'unknown';
}
}
8. Sanitise and Normalise a String
Data layer values are often inconsistent — mixed case, extra spaces, special characters. Before passing them to your analytics platform, clean them:
function() {
var raw = {{DL - product_name}};
if (!raw) return undefined;
return raw.toString().toLowerCase().trim().replace(/\s+/g, '_');
}
9. Detect the Device Type
GTM has no built-in device type variable. Build one with a simple user-agent check:
function() {
var ua = navigator.userAgent.toLowerCase();
if (/tablet|ipad/.test(ua)) return 'tablet';
if (/mobile|android|iphone/.test(ua)) return 'mobile';
return 'desktop';
}
10. Get the Current Scroll Percentage
Useful when you need to read scroll depth at the exact moment a tag fires (rather than tracking it as an ongoing event):
function() {
var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
var docHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight;
if (docHeight === 0) return 0;
return Math.round((scrollTop / docHeight) * 100);
}
Best Practices for Custom JS Variables
Always Return a Value or Undefined
If your function can't find what it's looking for, return undefined explicitly. Returning null or an empty string can cause tags to fire with bad data or trigger unexpected conditions.
Test in GTM Preview Mode
Open Preview mode, navigate to the page where your variable should fire, and check the variable values in the Variables tab. You can inspect what each variable returned at the time each event fired — invaluable for debugging.
Don't Reference Other Custom JS Variables Directly
You can reference other GTM variables using the {{Variable Name}} syntax inside Custom JavaScript, but be careful with circular dependencies. If Variable A calls Variable B which calls Variable A, GTM will return undefined for one of them.
Keep Performance in Mind
Custom JavaScript runs synchronously in the browser at tag fire time. Expensive DOM operations — querying large node lists, deep recursion, heavy loops — can slow page rendering. Keep your logic lean.
When Not to Use Custom JavaScript Variables
If the data you need is readily available in your data layer, use a Data Layer variable instead — it's faster and easier to maintain. Custom JS variables are for bridging the gap when the data exists on the page but hasn't been pushed to the data layer.
Similarly, if the same logic is needed across many variables, consider asking your developers to push a cleaned, consolidated data layer object rather than scraping values from the DOM in ten different places.
Conclusion
Custom JavaScript variables are one of GTM's most powerful features precisely because they're so flexible. Master these ten patterns and you'll be able to solve the vast majority of tracking challenges without waiting for developer tickets to be resolved.
If your GTM container has grown unwieldy or your custom variables are returning inconsistent data, Adslytics can audit your setup and implement clean, maintainable tracking solutions. Get in touch to discuss your requirements.
Need expert tracking setup?
Our Google Tag Manager experts have delivered 500+ tracking setups with a 98% success rate.
Get a Free Consultation →