GTM Data Layer: Preventing Stale Data During SPA Navigation
6 min
google tag manager's data layer is a cumulative key value store every call to datalayer push() deep merges the pushed object into gtm's internal model it never removes existing keys on its own a key is only updated when a push explicitly includes it example // navigation to article a datalayer push({ purple { view event name "article view", parameters { article id "article a", publish date "2024 01 10", author "jane doe" } } }) // navigation to article b (author not provided this time) datalayer push({ purple { view event name "article view", parameters { article id "article b", publish date "2024 03 22" // author is missing jane doe is still in gtm's model } } }) after the second push, gtm's internal model for purple parameters author still contains "jane doe" from article a spa mode vs non spa mode in spa mode (the default purple storefront behavior), the javascript environment persists for the entire session the datalayer array and gtm's internal model are never reset every push accumulates on top of previous state in non spa mode (where each navigation causes a full page reload), the javascript environment is destroyed and recreated on each page datalayer is re initialized as an empty array, so stale data from previous pages is impossible if your storefront is configured in non spa mode, this guide does not apply the purple behavior when the purple storefront fires a view event via the gtm tracking plugin, it pushes to window\ datalayer under the purple namespace the parameters included in that push are the parameters listed in tracking config json for that particular view/event for example, if view a defines author as a parameter but view b does not, navigating from a to b leaves the author value from view a in gtm's model any gtm tag that reads purple parameters author will see stale data this is most visible with article/content views where fields like publish date, author, taxonomy, or image urls are defined inconsistently across different view configurations use explicit parameters in tracking config json list every parameter you want to track in the parameters section of every affected view , even if the value will not always be available when a placeholder like {{author}} cannot be resolved (because the current page has no author data), the storefront pushes an empty string "" for that key this overwrites the stale value from the previous view with an empty string, which is the correct behavior gtm reads no author for the current view in tracking config json , add the full set of content related parameters to every view that could follow a content view during spa navigation { "google tag manager" { "viewsenabledbydefault" true, "views" { "storefront home" { "templates" { "name" "storefront home" }, "parameters" { "view" "{{view}}", "content id" "{{content id}}", "content name" "{{content name}}", "issue id" "{{issue id}}", "issue name" "{{issue name}}", "issue categories" "{{issue categories}}", "issue tags" "{{issue tags}}", "publication id" "{{publication id}}", "publication name" "{{publication name}}" } }, "issue content" { "templates" { "name" "issue content" }, "parameters" { "view" "{{view}}", "content id" "{{content id}}", "content name" "{{content name}}", "issue id" "{{issue id}}", "issue name" "{{issue name}}", "issue categories" "{{issue categories}}", "issue tags" "{{issue tags}}", "publication id" "{{publication id}}", "publication name" "{{publication name}}" } } } } } the key rule if a parameter appears in any view, it should appear in all views that can be reached from it via spa navigation without a full page reload for project specific fields (stored as custom issue properties), the placeholder follows the pattern {{issue property \<key>}} for example, a custom property publish date would be {{issue property publish date}} available standard placeholders these placeholders are resolved by the storefront at the time the view/event fires content / article {{content id}} id of the current content item {{content name}} name/title of the current content item issue {{issue id}} , {{issue name}} , {{issue product id}} {{issue categories}} comma separated category list {{issue tags}} comma separated tag list {{issue price}} , {{issue price value}} , {{issue price currency}} {{issue purchasable}} , {{issue purchased}} , {{issue latest}} {{issue property \<key>}} any custom issue property publication {{publication id}} , {{publication name}} , {{publication type}} {{publication property \<key>}} any custom publication property navigation {{view}} the current storefront view name {{popup}} whether the view is open as a popup page (readmode) {{page id}} , {{page index}} , {{page number}} , {{page label}} , {{page title}} , {{page alias}} , {{page section}} subscription / purchase {{subscription id}} , {{subscription name}} , {{subscription type}} {{product id}} , {{product name}} , {{product price}} {{transaction id}} , {{currency code}} verification gtm preview mode (tag assistant) open the storefront in chrome with gtm preview mode active navigate to a content view (article a) inspect the data layer in tag assistant note all purple parameters values navigate to a different content view (article b) without reloading check purple parameters again all fields should reflect article b only, with empty strings for any fields not applicable to article b experience logs the purple storefront has a built in tracking debugger enable it in the browser console while in preview mode window\ purple tracking debug({ logging true }) or persistently via localstorage (survives page refresh) localstorage setitem('tracking debug logging', 'true') // then reload the page once enabled, every tracked event is printed to the console \[track] \[view] issue content parameters content id = article b, content name = my article b, issue id = issue 456, publish date = , author = {{author}} fields that resolve to their placeholder (because no value is available for the current view) show as empty this confirms they are being explicitly pushed and will overwrite stale values in gtm's model navigate between two articles and verify that all listed parameters reflect the current article on each view event and that fields not applicable to the current article appear as empty strings rather than retaining values from the previous article