scalpel@labs: ~/glossary/datalayer-diff.mdx5 sections

What is the dataLayer diff?

The dataLayer diff compares the accumulated state before and after a push. It lists what the push added, what it changed (with the old and new values), and what it removed, using dot-paths like `ecommerce.value`, so overwrites are obvious.

extension: Scalpel Tagsupdated: 2026-08-14read_time: 2 min
less datalayer-diff.mdx

Why it matters

This is the biggest debugging feature in Scalpel Tags. Imagine you push ecommerce data, then two seconds later the whole ecommerce object vanishes from the dataLayer. You'd only catch that if you read the entire state after each push. The diff shows you instantly: "This push removed ecommerce.items". It's the difference between spotting a data bug in five seconds and spending an hour tracing state changes.

How it works

The diff compares the dataLayer state before a push and after a push. It shows three categories:

  • Added (green): keys that now exist but did not before.
  • Changed (amber): keys whose values shifted.
  • Removed (red): keys that existed before but are gone now.

It uses dot-path notation. So if you had {ecommerce: {items: [{id: 1}]}} and you push {ecommerce: null}, the diff shows ecommerce removed and all the nested properties under it gone. If you push {ecommerce: {items: [{id: 2}]}}, the diff shows ecommerce.items changed from [{id: 1}] to [{id: 2}].

The diff respects GTM's merge semantics. Objects merge recursively, but arrays replace wholesale. So if your state has {promo_codes: ['SAVE10']} and you push {promo_codes: ['SAVE20']}, the whole array gets replaced, not merged.

What does not matter

The visual order of keys in the diff does not reflect any ordering rule; GTM does not preserve object key order. Also, the diff only shows changes to the accumulated state, not the internal firing of tags or which triggers matched. For that view, you'd look at the tag inspector or the request log.

Code example

// Initial push
dataLayer.push({
  event: 'view_item',
  ecommerce: {
    items: [
      { item_id: 'SKU123', item_name: 'Shoe', price: 79.99 }
    ],
    value: 79.99
  }
});

// Second push: modifies the price, leaves items alone
dataLayer.push({
  ecommerce: {
    value: 69.99  // Discount applied
  }
});
// Diff shows: ecommerce.value changed from 79.99 to 69.99
// ecommerce.items stays (objects merge recursively)

// Third push: wipes out the entire ecommerce object
dataLayer.push({
  ecommerce: null
});
// Diff shows: ecommerce removed (and everything under it)

How Scalpel Tags shows it

In the dataLayer tab, click any push to expand it. The diff panel appears on the right. Added fields show in green, changed fields in amber with old and new values side by side, removed fields in red. Hover over any field to see the full dot-path. The diff makes it obvious when a later push has overwritten or cleared data you expected to stay.

Sources