Skip to content

Logic Variables

Logic Variables

CoreCategory45 Variables

GTM trigger conditions are AND-only. There’s no OR, no XOR, no nested logic. The if/else ladders that should be one configuration become tangled Custom JavaScript variables nobody wants to touch. Type checks and value validation get reinvented in every container. Logic is the decision-modeling layer — boolean operators that fix what GTM lacks, IF ELSE IF builders that replace JavaScript ladders, and a predicate library of 38 type and value checks that compose into them. No Custom JavaScript.

45 variables100% sandbox-safeWeb + ServerMIT licensed

Three structural problems show up in every GTM container:

GTM has no native OR. Trigger conditions in a single trigger use AND logic — all conditions must be true for the trigger to fire. To express OR — fire when any of several conditions is true — most analysts reach for workarounds: multiple triggers on the same tag, a RegEx Table variable, or a Custom JavaScript variable.

Source — Analytics Mania

”As GTM’s interface instructs, ALL conditions in the same trigger must be met, therefore, most likely, such a trigger will never be activated because it is not possible for the URL of a website to contain all 3 options.”

analyticsmania.com — OR Condition in GTM Triggers

Decision logic always becomes Custom JavaScript. Anything more sophisticated than equals/contains comparisons turns into a function() { if (…) return X; else if (…) return Y; } ladder. The rules live only in the code.

Type and value validation gets reinvented constantly. “Is this value a number?” “Does this dataLayer key exist?” “Is the input a valid Unix timestamp?” Each check is a small Custom JavaScript variable that nobody centralizes. The Logic category provides 38 named type and value predicates that you import once and reuse everywhere.

The category has two complementary halves. Understanding them separately is the fastest path to using them well together.

Routing & combinators

Decide what to do based on conditions

IF ELSE IF builders, AND/OR/XOR/NOT operators, value fallbacks. These take conditions or values as input and return whichever output matches.

Predicates

Test whether values satisfy a property

38 named tests for type (isString, isNumber, isArray), existence (isDefined, isNull, isEmpty), comparison (isGreaterThan, isInRange), and semantics (isURL, isJSON, isUnixTimestamp).

Fallbacks

Pick the first value that exists

firstDefined and firstTruthy take multiple inputs and return the first one that satisfies the test. The clean version of the JavaScript || chain.

The predicates compose into the routing variables. isURL can feed an IF (else), an OR, an IF ELSE IF (Advanced) row, or a 🧩 WEIGHTED condition (from Number). The same boolean primitive works in every consumer.

Forty-nine variables, organized by job. The hero group is small but high-impact: it’s where the routing and combination work happens.

Native GTM triggers fire only when all conditions in a single trigger evaluate true. To express OR — fire when any of several conditions is true — the Logic category offers a clean approach.

Recipe — OR condition for trigger
Variable: 🧩 OR · Direct mode · Use as trigger condition
FieldValue
Input 1{{Page Path}} contains /pricing
Input 2{{Page Path}} contains /signup
Input 3{{Page Path}} contains /demo
Trigger condition: OR equals true — fires on any of the three paths

Why not multiple triggers? Multiple triggers on the same tag also act as OR — but they multiply your trigger inventory and obscure intent. 🧩 OR in one trigger keeps the logic visible in one place.

Combine AND, OR, XOR, NOT for complex conditions

Section titled “Combine AND, OR, XOR, NOT for complex conditions”

The four boolean operators compose into arbitrary expressions. Need “(page is pricing OR demo) AND (user is logged in)”? Nest 🧩 OR inside 🧩 AND. Need “exactly one of two flags is set”? Use 🧩 XOR.

InputsANDORXOR
true, truetruetruefalse
true, falsefalsetruetrue
false, falsefalsefalsefalse
Recipe — Pricing or demo page, AND user logged in
Outer: 🧩 AND · Direct mode · Inner: 🧩 OR as one input
FieldValue
AND input 1{{🧩 OR — pricing or demo path}}
AND input 2{{DL - is_logged_in}} equals true
Use the outer AND as the single trigger condition

Replace if/else Custom JavaScript with IF ELSE IF

Section titled “Replace if/else Custom JavaScript with IF ELSE IF”

Anywhere a Custom JavaScript variable contains an if/else if/else ladder, an IF ELSE IF builder replaces it as configuration. Two variants:

  • 🧩 IF ELSE IF (Predefined) — every row tests the same input against different values. Like a switch statement.
  • 🧩 IF ELSE IF (Advanced) — each row tests its own arbitrary condition. Conditions can use any predicate.
Recipe — Map device category to a tag value (Predefined)
Variable: 🧩 IF ELSE IF (Predefined) · Direct mode
Input value→ Output
”mobile""m"
"tablet""t"
"desktop""d”
Default”unknown”
Input {{DL - device_category}}; output flows to the destination tag
Recipe — User tier from arbitrary signals (Advanced)
Variable: 🧩 IF ELSE IF (Advanced) · Direct mode
Condition→ Output
{{DL - lifetime_value}} > 1000”vip”
{{DL - is_subscriber}} equals true”subscriber”
{{DL - first_purchase_date}} isDefined”customer”
Default”visitor”
First matching row wins; result becomes a custom dimension or audience signal

“User ID, fall back to guest ID, fall back to session ID, fall back to anonymous.” This pattern shows up in every container that handles authentication state.

Recipe — User ID with fallback chain
Variable: 🧩 firstDefined · Direct mode
InputValue
Input 1{{DL - user_id}}
Input 2{{DL - guest_id}}
Input 3{{DL - session_id}}
Input 4 (final fallback)“anonymous”
Returns the first input that is not undefined

For “first non-empty value” semantics — where you want to skip 0, "", null, false, and undefined alike — use 🧩 firstTruthy instead.

Input chainfirstDefinedfirstTruthy
0, “id”, “fb”0”id"
"", “id”, “fb""""id”
undefined, “id”, “fb""id""id”
null, “id”, “fb”null”id”

DataLayer values arrive as whatever the backend pushed — and backends ship bugs. A revenue field that’s supposed to be a number arrives as a string. A user ID that should always be present is sometimes undefined. Type and value validation in the container catches these before they reach a tag.

Recipe — Only fire conversion tag if value is a valid number
Variable: isValidNumber · Direct mode · Use as trigger condition
FieldValue
Input{{DL - ecommerce.value}}
true for a real number; false for NaN, Infinity, strings, undefined

Detect Unix timestamps, JSON, URLs, SHA-256 hashes

Section titled “Detect Unix timestamps, JSON, URLs, SHA-256 hashes”

Beyond raw type checks, the category includes semantic predicates that test whether a value looks like a known format:

  • isURL — value parses as a syntactically valid URL. Useful before sending to page_referrer or link_url.
  • isJSON — value is a parseable JSON string. Pair with parseJSON to safely process stringified objects.
  • isSHA256 — value is a 64-character hex string. Useful to confirm PII has been hashed before reaching a CAPI tag.
  • Unix timestamp variantsisUnixTimestamp, isUnixTimestampSeconds, isUnixTimestampMilliseconds. Distinguish 10-digit (seconds) from 13-digit (ms) timestamps.
Recipe — Route timestamps through the right conversion
Variable: 🧩 IF ELSE IF (Advanced) · Direct mode
Condition→ Output
{{DL - timestamp}} isUnixTimestampSeconds{{DL - timestamp}} (already correct)
{{DL - timestamp}} isUnixTimestampMilliseconds{{Apply - millisecondsToSeconds}}
Default{{Apply - currentTimestamp → millisecondsToSeconds}}
Output is always Unix seconds, regardless of input format

Predicates aren’t just for routing decisions — they’re also the building blocks of scoring pipelines. 🧩 WEIGHTED from the Number category takes condition-weight pairs and returns the sum of weights for conditions that evaluate true.

Recipe — Lead score from validated dataLayer signals
Variable: 🧩 WEIGHTED (Number) · uses Logic predicates as conditions
ConditionWeight
{{DL - email}} isURL equals false5 (real email, not a URL misuse)
{{DL - phone}} isStringifiedNumber3
{{DL - company}} isNotEmptyString10 (B2B signal)
{{DL - lifetime_value}} isInRange [100, 10000]15
Score that combines validation and behavioral signals

When a dataLayer key is sometimes missing, three different patterns serve three different needs:

Pattern 1: GTM’s native default-value setting

The dataLayer variable type in GTM has a “Default Value” field. Built into GTM, no Logic category required. Limitation: only triggers on undefined.

Pattern 2: 🧩 firstDefined

Multiple dataLayer variables coalesced to the first non-undefined one. Use when you have a hierarchy of fallback sources.

Pattern 3: 🧩 IF (else) with predicates

Explicit control over what counts as “missing.” Use isEmpty, isFalsy, or isNull.

Recipe — Default to “unknown” when a dataLayer key is empty
Variable: 🧩 IF (else) · Direct mode
FieldValue
Condition{{DL - user_segment}} isEmpty
Then return”unknown”
Else return{{DL - user_segment}}
Always returns a meaningful value, never undefined or empty

Validated user identifier

First defined ID across multiple sources, falling back to “anonymous.”

🧩 firstDefinedDirectuser_id, guest_id, session_id, “anonymous”

Conditional event firing

Tag fires only when input is a valid number AND in a sane range.

🧩 ANDDirect·isValidNumber·isInRange

Schema-routed timestamp

Detect timestamp format and route through the right unit conversion.

🧩 IF ELSE IF (Advanced)Direct·isUnixTimestampSeconds·isUnixTimestampMilliseconds

Cross-category scoring

Logic predicates feed Number’s WEIGHTED, which feeds clamp and scaleToRange.

Logic predicatesconditions🧩 WEIGHTEDApplyclampApplyscaleToRangeDirect

The dominant Custom JavaScript pattern for decision logic in GTM:

function() {
var ltv = Number({{DLV - lifetime_value}});
var sub = {{DLV - is_subscriber}};
var first = {{DLV - first_purchase_date}};
if (ltv > 1000) return ‘vip’;
else if (sub === true) return ‘subscriber’;
else if (first !== undefined) return ‘customer’;
else return ‘visitor’;
}

Named templates encode the rules as configuration:

  • The rules are visible. A reviewer can read an IF ELSE IF (Advanced) variable and know the policy without reading code.
  • Type coercion is explicit. Predicates validate before routing.
  • Server-side portable. sGTM’s stricter sandbox accepts named templates.
  • Composable. Reusing the “is VIP” check elsewhere means referencing one variable, not duplicating code.
GoalVariableMode
OR condition in a trigger🧩 ORDirect
AND condition with multiple inputs🧩 ANDDirect
Exactly one of N inputs is true🧩 XORDirect
Invert a boolean🧩 NOTDirect / Apply
One condition, two outcomes🧩 IF (else)Direct
Switch on one input value🧩 IF ELSE IF (Predefined)Direct
Ladder with arbitrary conditions🧩 IF ELSE IF (Advanced)Direct
First non-undefined value (coalesce)🧩 firstDefinedDirect
First truthy value🧩 firstTruthyDirect
Type checks (string, number, array, etc.)isString / isNumber / isArray / etc.Direct
Existence (defined, null, empty)isDefined / isNull / isEmpty / isEmptyStringDirect
ComparisonisEqualTo / isGreaterThan / isLessThan / isInRangeDirect
Format detection (URL, JSON, SHA-256)isURL / isJSON / isSHA256Direct
Unix timestamp shapeisUnixTimestamp / isUnixTimestampSeconds / isUnixTimestampMillisecondsDirect

How do I create an OR condition in a GTM trigger?

GTM trigger conditions use AND logic — all conditions must be true. To express OR, use the 🧩 OR variable as a single trigger condition. Pass the individual conditions as inputs. The variable returns true when any input is true.

How do I replace an if/else if Custom JavaScript variable in GTM?

Use 🧩 IF ELSE IF (Advanced) for ladders with arbitrary conditions per row, or 🧩 IF ELSE IF (Predefined) when all rows compare the same input to different values. The variable returns the output of the first row whose condition matches.

How do I return the first defined value from multiple dataLayer variables?

Use 🧩 firstDefined to take the first input that is not undefined. Use 🧩 firstTruthy when you want to skip empty strings, zero, false, and null in addition to undefined.

How do I validate that a dataLayer value is a number?

Use isNumber or isValidNumber as a trigger condition. isValidNumber additionally rejects NaN and Infinity. For numeric strings, use isStringifiedNumber and convert with toNumber from Number.

What’s the difference between firstDefined and firstTruthy?

firstDefined returns the first input that is not undefined — so 0, "", null, false all pass through. firstTruthy returns the first input that coerces to true — skipping all falsy values. Use firstDefined for ID fallbacks where 0 is valid; use firstTruthy for “give me any non-empty value.”

Are these variables sandbox-safe in server-side GTM?

Yes. All templates run inside GTM’s sandboxed JavaScript environment using only permissioned APIs. They work in both web and server containers.