Doku
Die Dokumentation durchsuchen

Track single-page apps (SPAs)

How the tracker follows single-page app navigation with the History API or hash routing, what counts as a new page, and how to avoid counting pageviews twice.

Als Markdown anzeigen

Most single-page apps (React Router, Next.js, Vue Router, SvelteKit, Angular, Astro view transitions…) change pages with history.pushState. The tracker handles them with no configuration.

Modes (data-spa)#

Value Behavior
auto (default) Wraps history.pushState and listens to popstate (back/forward). Each navigation to a new path sends a pageview
history Same as auto
hash Listens to hashchange and keeps the #fragment in the path (/#/settings). For routers that use #/ URLs
off Only the initial page load is tracked. Use the JavaScript API for the rest

With auto, fragment changes aren't pageviews, and history.replaceState isn't tracked (routers use it for things like updating query strings).

What counts as a new page#

A pageview is sent when the path changes: the pathname, plus the fragment in hash mode. Changing only the query string (?tab=2) doesn't send one, and neither does navigating to the same path again. Call privatus.pageview() to force one.

For each SPA navigation:

  1. The engagement time and scroll depth of the page you're leaving are sent (with the engage module).
  2. The referrer of the new pageview is a URL on your own site, so internal navigation never shows up as a new traffic source. The visit keeps the source it started with.

Double counting#

If you also call privatus.pageview() in your router's navigation hook, or fire the tracker again from a tag manager on history changes, each navigation is counted twice. Pick one: the built-in SPA tracking, or manual mode with data-spa="off". See SPA double counting.