Custom JavaScript Variables in GTM: 10 Practical Recipes | Adslytics

Google Tag Manager Setup & Audit Listicle

Custom JavaScript Variables in GTM: 10 Practical Recipes

By Muhammad Farooq · January 18, 2026 · 7 min read
Custom JavaScript Variables in GTM: 10 Practical Recipes

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 →
← Back to Blog
Muhammad Farooq

Author

Muhammad Farooq GTM & Analytics Expert · Adslytics Founder

Tracking specialist with 10+ years of experience in Google Tag Manager, GA4, Server-Side Tracking, and Google Ads. Founder of Adslytics — a dedicated analytics agency with a 98% success rate across 232+ projects on Upwork.

Top Rated Plus LinkedIn Visit the author's profile →