Object Variables
Object Variables
Modern dataLayers don’t push flat key-value pairs. They push deep JSON structures: ecommerce.items.0.price, user.profile.preferences.locale. GTM’s built-in DataLayer Variable handles dot-notation but throws “Cannot read property of undefined” the moment a single intermediate key is missing. Tag payloads need to be assembled, not just read. Object is the payload-shaping layer — 23 sandbox-safe variable templates for safe nested access, conditional payload assembly, deep merging, and key/value reshaping.
Why this category exists
Section titled “Why this category exists”Modern web stacks push complex JSON to the dataLayer. The dot-notation pattern works when every level exists. The moment a backend deploys a partial payload — user object missing on logged-out sessions, shipping missing on digital orders — the variable throws a runtime error, and your tag fires with the wrong shape.
”If the data is nested inside another object in the Data Layer (e.g., attributes: {pagePostAuthor: ’…’}), you must use dot notation in the GTM variable: attributes.pagePostAuthor.”
The Object category solves three problems:
- Defensive nested access. Read deep paths safely, with
undefinedas the worst-case return value rather than thrown errors. - Tag payload composition. Build outbound payloads — Meta CAPI
user_data, GA4 custom parameter blocks, server-side tag bodies — with conditional logic that decides which fields to include. - Object reshaping. Merge multiple sources, apply defaults to incomplete pushes, transform key formats between vendors.
Three jobs: access, compose, reshape
Section titled “Three jobs: access, compose, reshape”Read deep paths without breaking
getValueByPath, pickNested, hasNested — read nested dataLayer properties safely. Worst case is undefined, never a thrown error.
Build the exact shape vendors require
Three generators. OBJECT Smart for conditional assembly. OBJECT Transformer for key-by-key reshaping. OBJECT From Arrays for zip-style construction.
Combine and adapt object shapes
assignObjects, mergeConcatDeep, assignDefaults. Combine user traits with session traits. Fill in missing keys without overwriting existing ones.
The variables — at a glance
Section titled “The variables — at a glance”Twenty-five variables across two hero subgroups (generators, defensive access) plus supporting primitives.
undefined on missing keys.pickNestedPull multiple nested paths into a new clean object.hasNestedBoolean: does a nested path exist all the way down?getValueRead a top-level key. The simpler sibling of getValueByPath.hasBoolean: does a top-level key exist?hasKeyEqualToBoolean: does a key exist AND equal a specific value?[key, value] pairs for iteration.toObjectPairsArray of {key, value} objects for iteration with Items category.zipObjectBuild an object from a keys array and a values array.parseJsonStringSafely parse a JSON-formatted string into an object.applyFromObjectApply a function to each property using the object as configuration.Safely access nested dataLayer properties
Section titled “Safely access nested dataLayer properties”The most common runtime error in any GTM container is “Cannot read property of undefined.” It happens when a variable expects ecommerce.items.0.price and a partial dataLayer push omits one of the intermediate keys.
The pattern that breaks: A backend pushes a cart-abandonment event without ecommerce. A GTM Custom JS variable accesses ecommerce.items.length — throws TypeError: Cannot read properties of undefined. The error blocks the tag from firing. Other tags that depended on this variable get cascade-blocked.
getValueByPath is the safe alternative. It walks the path step by step, returning undefined at the first missing key rather than throwing.
| Field | Value |
|---|---|
| Source object | {{Apply - getDataLayerCurrent}} |
| Path | ecommerce.items.0.price |
49.99 when the full path exists; undefined when any intermediate key is missing — never throwsPair with falsyTo from the Value category to substitute a default when the path doesn’t exist:
Pick a subset of nested fields into a clean object
Section titled “Pick a subset of nested fields into a clean object”Reading several nested paths into a new object — building a Meta CAPI user_data block from a sprawling user profile — is pickNested.
| Source path | Target key |
|---|---|
user.contact.email | em |
user.contact.phone | ph |
user.profile.firstName | fn |
user.profile.lastName | ln |
user.address.city | ct |
user.address.country | country |
{em, ph, fn, ln, ct, country} object — keys missing in source are absent in resultBuild conditional tag payloads with the Smart generator
Section titled “Build conditional tag payloads with the Smart generator”⚡ OBJECT › Smart packages conditional payload assembly into three property modes:
- Fixed properties — always included, regardless of state.
- Conditional properties — included only when a global condition evaluates true.
- Rule-based properties — each property has its own individual condition.
| Mode | Property | Value / Condition |
|---|---|---|
| Fixed | page_type | {{DL - page_type}} |
| Fixed | currency | ”EUR” |
| Rule-based | user_id | If {{isGrantedAnalyticsStorage}} |
| Rule-based | customer_lifetime_value | If {{isGrantedAdUserData}} |
| Rule-based | experiment_variant | If {{hasNested experiment.variant}} |
Cross-category composition. The conditions in rule-based mode can reference any predicate from Logic or any boolean variable elsewhere in the framework.
Merge user traits with session traits
Section titled “Merge user traits with session traits”| Slot | Source |
|---|---|
| Object 1 (lowest priority) | {{Apply - getPersistedDataLayerValue user_traits}} |
| Object 2 (middle priority) | {{Apply - getGA4SessionDataLayerValue session_traits}} |
| Object 3 (highest priority) | {{DL - event_traits}} |
Deep-merge nested objects without overwriting branches
Section titled “Deep-merge nested objects without overwriting branches”Shallow merge has a subtle failure mode. Two sources both have a user key with different nested fields. assignObjects takes the second one wholesale — id and email from source A are gone. mergeConcatDeep recursively merges at every level, preserving all keys from both sources.
Apply default values to incomplete payloads
Section titled “Apply default values to incomplete payloads”| Field | Value |
|---|---|
| Target | {{DL - ecommerce}} (whatever the backend pushed) |
| Defaults | { currency: “EUR”, value: 0, items: [] } |
Transform every key or value with the Transformer
Section titled “Transform every key or value with the Transformer”| Source key | Target key | Apply transform |
|---|---|---|
email | em | {{Apply - normalizeEmail → hashSHA256}} |
phone | ph | {{Apply - normalizePhone → hashSHA256}} |
firstName | fn | {{Apply - trim → toLowerCase → hashSHA256}} |
lastName | ln | {{Apply - trim → toLowerCase → hashSHA256}} |
Parse JSON strings from cookies and storage
Section titled “Parse JSON strings from cookies and storage”| Field | Value |
|---|---|
| Cookie name | cmp_consent |
| Cookie raw value | ’{“analytics”:true,“marketing”:false}’ |
{ analytics: true, marketing: false } as an actual objectBuild objects from arrays of keys and values
Section titled “Build objects from arrays of keys and values”| Field | Value |
|---|---|
| Keys | [“name”, “email”, “company”] |
| Values | [“Jane Doe”, “[email protected]”, “Acme Inc”] |
{ name: “Jane Doe”, email: “[email protected]”, company: “Acme Inc” }Test for key existence before using values
Section titled “Test for key existence before using values”has— does a top-level key exist? Use for shallow gates.hasNested— does a deep dot-notation path exist all the way down?hasKeyEqualTo— does a key exist and equal a specific value?
Convert between objects and key-value pair arrays
Section titled “Convert between objects and key-value pair arrays”getKeysreturns just the keys as an array.valuesreturns just the values as an array.entriesreturns[[key, value], [key, value]]nested arrays.toObjectPairsreturns[{key, value}, {key, value}]objects — works with the Items category.zipObjectandassignObjectsFromPairsrebuild objects from these intermediate shapes.
Composed patterns — chaining variables
Section titled “Composed patterns — chaining variables”Defensive deep read with default
Read a deep path safely, substitute a default when missing.
Layered traits assembly
User traits, session traits, event traits — merged with later sources winning on conflict.
Vendor-specific payload reshape
Pick relevant nested fields, reshape keys to vendor convention, normalize values.
Cheatsheet
Section titled “Cheatsheet”| Goal | Variable | Mode |
|---|---|---|
| Build conditional payload (fixed + conditional + rule-based) | ⚡ OBJECT › Smart | Direct |
| Reshape keys with per-key transforms | ⚡ OBJECT › Transformer (Advanced) | Direct |
| Build object from keys and values arrays | ⚡ OBJECT › From Arrays | Direct / Apply |
| Read deep path safely | getValueByPath | Direct / Apply |
| Read top-level key | getValue | Direct / Apply |
| Pick multiple nested paths into clean object | pickNested | Direct / Apply |
| Test deep path existence | hasNested | Direct |
| Test top-level key existence | has | Direct |
| Test key exists AND equals value | hasKeyEqualTo | Direct |
| Pick / omit top-level keys | pick / omit | Direct / Apply |
| Add or update single property | set | Direct / Apply |
| Shallow merge (later wins) | assignObjects | Direct / Apply |
| Deep merge with array concatenation | mergeConcatDeep | Direct / Apply |
| Fill missing keys only | assignDefaults / assignDefaultsDeep | Direct / Apply |
| Get keys / values / entries | getKeys / values / entries | Direct / Apply |
| Convert to / from key-value pair arrays | toObjectPairs / zipObject | Direct / Apply |
| Parse JSON string into object | parseJsonString | Direct / Apply |
| Apply function to each property | applyFromObject | Direct / Apply |
How do I safely access a nested dataLayer property in GTM?
Use getValueByPath with a dot-notation path. Returns undefined when any intermediate key is missing rather than throwing. For multiple nested fields, use pickNested to extract a subset of paths into a clean object.
How do I build a conditional payload for Meta CAPI or GA4?
Use ⚡ OBJECT › Smart. Three property modes: fixed (always added), conditional (added when a global condition is true), rule-based (each property has its own condition).
How do I merge user traits and session traits into one object?
assignObjects merges shallowly with later values winning. For deep nested structures use mergeConcatDeep. assignDefaults handles the inverse: fill missing keys only.
How do I check if a dataLayer key exists before using it?
has for top-level, hasNested for deep paths, hasKeyEqualTo for both presence and a specific value. Each returns a boolean for trigger conditions.
How do I transform every key or value of an object?
Use ⚡ OBJECT › Transformer (Advanced) for key-by-key transformation with arbitrary per-key logic. For uniform transformations get keys with getKeys, transform with Array’s map, rebuild with zipObject.
How do I parse a JSON string from a cookie into an object?
parseJsonString safely parses JSON-formatted strings. Returns undefined on malformed input rather than throwing.
Are these variables sandbox-safe in server-side GTM?
Yes. All templates run inside GTM’s sandboxed JavaScript environment. Object variables are particularly valuable in sGTM where reshaping incoming event payloads to match destination shapes is the main job.