History-change double counting
Single-page apps signal navigation through the History API, and GTM's History Change trigger listens for it. Some routers call pushState and then emit their own navigation event, or fire popstate alongside a manual push, so the trigger fires more than once per route change. Each firing sends a virtual pageview, inflating counts. This page explains history-change double counting and how to make the trigger fire once.
Why it fires twice
The GTM History Change trigger reacts to pushState, replaceState, and popstate events. Many SPA routers manipulate history and then dispatch their own change event, and some call replaceState immediately after pushState to normalize the URL. Each of these can satisfy the trigger, so one user action produces two or more history events — and two or more virtual pageviews.
The duplicates share the same page path and arrive within milliseconds, which is the signature to look for.
- Trigger listens for pushState, replaceState, popstate
- Routers may push then replace, or emit extra events
- Each firing sends another virtual pageview
Making it fire once
Constrain the trigger to the specific change you want — for example, fire only when the New History Fragment differs from the Old, ignoring replaceState normalizations. Better, drive the pageview from one explicit dataLayer push your router emits on a confirmed route change, instead of inferring it from raw history events. Then verify in preview that one navigation yields exactly one tag fire.
This is distinct from generic SPA tracking gaps where the route fires too few times; here it fires too many.
How it appears in analytics and logs
Two identical virtual pageviews milliseconds apart for one navigation usually mean the History Change trigger fired twice, not that the user navigated twice.
Diagnostic use case
Stop inflated single-page-app pageviews caused by a History Change trigger firing multiple times for one route transition.
What WebmasterID can help detect
WebmasterID can record route changes from a single explicit signal, avoiding the duplicate firings that History-based triggers produce.
Common mistakes
- Firing pageviews on every raw history event without a guard.
- Counting a replaceState URL normalization as a navigation.
- Not testing one route change end-to-end in GTM preview.
Privacy and accuracy notes
Deduplicating virtual pageviews uses event timing and URLs, not identity. WebmasterID applies privacy-safe language and stores no personal identifiers for this.
Related pages
- Duplicate pageviews in SPAs
Single-page apps often send a pageview on initial load and another from a route-change listener, and on the very first view both can fire for the same URL. Strict-mode double renders, mounting effects that run twice, and duplicate listeners add more. The result is inflated pageviews concentrated on entry pages. This page explains duplicate SPA pageviews and how to fire exactly one per route.
- Single-page-app tracking gaps
In a single-page application, the browser loads once and the framework swaps views via the History API without a new document load. Analytics that depend on the load event therefore record only the first screen. This page explains the gaps — missing virtual pageviews, stale page paths, and broken referrer chains — and how SPA-aware tracking closes them.
- Double-counting pageviews
Double-counting happens when a single page load fires the analytics tag more than once. Two snippets on the page, a tag added in both the site and a tag manager, or an SPA that fires a virtual pageview on top of the full-load one all do it. The result inflates pageviews and drags engagement and bounce metrics. This page covers detection and the fixes.
- Events documentation
Emit one explicit route-change event per navigation.
Sources and verification notes
Last reviewed 2026-06-24. Facts are checked against primary/official sources where available; uncertain specifics are marked “Data not yet verified” rather than guessed.