UTM tracking in single-page apps
Single-page apps (React, Vue, and similar) load once and then route in the browser, which can drop UTM parameters before analytics reads them. This page explains capturing UTMs on the first request, persisting them across in-app navigation, and the SPA pitfalls that silently lose campaign attribution.
Why SPAs lose UTMs
An SPA serves one HTML document and then handles navigation in JavaScript. Two things go wrong:
First, if the router rewrites or normalizes the URL on load (stripping the query) before your analytics initializes, the parameters are gone. Second, virtual page views fired after navigation no longer carry the original UTMs, so only the landing capture has them.
Capture on first load, then persist
Read the UTM parameters as early as possible — ideally from the initial request, or in app bootstrap before the router touches the URL. Persist them for the session (e.g. in memory or sessionStorage) so later virtual page views can attribute to the original campaign.
Then you can safely strip the parameters from the visible URL with history.replaceState, since the campaign is already captured.
- Read utm_* before client-side routing normalizes the URL
- Persist for the session so virtual page views inherit attribution
- Strip from the address bar only after capture
Server-side capture is the safety net
Client-side capture races against the router and depends on JavaScript executing. Recording attribution server-side on the first request — before any SPA code runs — removes the race entirely, because the parameters are read from the request that delivered the document.
How it appears in analytics and logs
Campaigns showing as direct in an SPA often mean the parameters were lost during client-side routing before analytics initialized. Capturing on first load fixes the gap.
Diagnostic use case
Capture UTM parameters reliably in a single-page app, where client-side routing and rewritten URLs can drop the tags before they are recorded.
What WebmasterID can help detect
WebmasterID records attribution from the first server-visible request carrying the parameters, so SPA client-side routing that later rewrites the URL does not erase the campaign.
Common mistakes
- Initializing analytics after the router has stripped the query string.
- Firing virtual page views without persisting the original UTMs.
- Relying only on client-side capture, which the router can beat.
- Persisting parameters durably with personal data instead of transiently.
Privacy and accuracy notes
Persist only the campaign parameters, not the visitor. Store them transiently (for the session) and keep them free of personal data; they are public values from the URL.
Frequently asked questions
- Why do my SPA campaigns show as direct?
- The parameters were likely lost during client-side routing before analytics read them. Capture UTMs on first load (or server-side) and persist them for the session.
Related pages
- UTM stripping and clean URLs
UTM parameters do their job at the moment of capture; leaving them in the visible URL invites messy shares, duplicate-looking URLs, and accidental re-tagging. This page explains stripping UTMs with history.replaceState after your analytics has read them, and how to keep canonical URLs clean without losing attribution.
- UTM for app deep links
When a campaign link opens a native app instead of a web page, the UTM values have to survive a web-to-app handoff that does not always preserve query strings. This page covers passing campaign data into deep links and the caveats that can drop it along the way.
- UTM and analytics view filters
Analytics filters (internal traffic, developer traffic, bot exclusion, source overrides) can quietly change how UTM-tagged visits are reported. This page explains the safe ways to filter without dropping legitimate campaign data, and the filter mistakes that make UTM numbers look wrong.
- Website observability
Catch SPA campaigns falling into direct when routing drops the tags.
Sources and verification notes
- MDN — History.replaceState()Used to manage the URL after capturing UTMs in an SPA.
- MDN — URLSearchParamsReading UTM parameters from the URL on first load.
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.