Your Site Is a Single Page App and Your Tracking Only Ever Saw the First Page

By Jackson Kolinski · October 24, 2026 · 9 min read
Your Site Is a Single Page App and Your Tracking Only Ever Saw the First Page

There is a specific failure that produces one of the most confusing symptoms in tracking: a conversion tag that is correctly built, correctly published, passes every test you run on it, and still records almost nothing from real traffic. The usual cause is that the site is a single page application, and the tag is waiting for a page load that never happens.

This affects far more sites than people assume. It is not limited to obviously app-like products. Marketing sites built in React, Next.js, Vue, Nuxt, Gatsby, and SvelteKit behave this way, as do modern Shopify themes and a growing number of page builders. If the URL in the address bar changes without the page visibly reloading, you are looking at one.

What a single page app does differently

A traditional website works in whole documents. Click a link and the browser throws away the current page, requests a new one, and renders it from scratch. Every one of those loads is a clean slate, and every script on the page runs again from the beginning. Analytics was built on that assumption.

A single page application loads one document at the start and then never loads another. When you click a link, JavaScript intercepts it, fetches just the data it needs, swaps the content on screen, and updates the address bar using the browser’s History API. From the user’s point of view they moved to a new page. From the browser’s point of view, nothing navigated. The document that loaded twenty minutes ago is still the document on screen.

Your tags were loaded by that first document. They ran once, when it loaded. Unless something explicitly tells them that a route change happened, they have no idea that anything has changed since.

What this actually breaks

  1. Page views stop after the first one. Analytics records the landing page and then nothing, no matter how many pages the visitor actually reads. Every session looks like a single page visit, and every engagement metric built on page counts becomes meaningless.
  2. Thank you page conversions never fire.This is the expensive one. A conversion trigger that says “fire when the URL contains /thank-you” is watching for a page load. In a single page app the user reaches /thank-you without one, so the condition is never evaluated and the conversion is never recorded.
  3. Your pixel reports one event per session. The Meta pixel fires its initial PageView on the first document load and then sits there. Any standard event you expected on a later step, Lead, Purchase, CompleteRegistration, has to be fired deliberately.
  4. Page-scoped triggers measure the wrong page. Scroll depth, timers, and element visibility triggers are still scoped to the document that loaded, which is no longer the content on screen.

Why it passes every test you run

This is the part that makes it so durable. Testing almost always involves pasting the thank you page URL straight into the address bar to check the tag fires.

When you do that, the browser performs a genuine document load. The page is requested fresh, every script runs from the beginning, the URL condition is evaluated, and the tag fires perfectly. Tag Assistant shows green. The conversion appears in the debug view. You mark it working and move on.

A real user never does that. They arrive on a landing page, fill in the form, and get moved to the confirmation by the application without a document load. Your test and your user took two completely different paths through the site, and only one of them triggered the tag. The lesson generalises: test by navigating the site the way a customer does, never by pasting URLs.

How to confirm your site is one

  • Watch the page as you click.Click an internal link and watch for the browser’s loading indicator and the brief blank flash of a real navigation. If the content simply changes while the page stays put, it is a single page app.
  • Check the page count in analytics. If almost every session contains exactly one page view on a content-rich site, that is the signature.
  • Compare forms to conversions. Ask the client how many enquiries they actually received last month. If they say ninety and your conversion action says four, the four are probably people who bookmarked or refreshed the confirmation page.

Fixing it

The best option: a data layer event from the app

The most reliable fix is for the application itself to announce route changes by pushing an event into the data layer whenever it completes a navigation. This requires a developer, and it is worth asking for, because it is the only approach where tracking is told what happened by the thing that actually knows. Everything else is inference.

The same applies to conversions. Rather than watching for a URL, have the app push an explicit event when a submission genuinely succeeds, ideally after the server confirms it. A conversion fired on a real success callback cannot be triggered by a refresh, cannot fire on a failed submission, and does not care how the routing works.

Without a developer: the History Change trigger

Google Tag Manager has a History Change trigger that fires when the URL fragment changes or when the page uses the History API, which is exactly what these frameworks use to navigate. It is designed for virtual page views in single page apps, and it is the practical option when you cannot get engineering time.

Two things to watch. The built-in History variables need to be enabled before history-based triggers and variables work properly. And a History Change trigger is less precise than an application event: it fires when the URL changed, which is not always the same moment as the new content being ready, so a tag that reads something off the page can run a fraction too early.

The GA4 setting, and how to avoid double counting

GA4 enhanced measurement includes an advanced option on page views that controls whether the event is sent based on browser history events. Switched on, GA4 listens for history state changes and records page views for them, which handles the basic case without any tag manager work at all.

The trap is turning that on andalso firing your own page view tag on a History Change trigger. Then every route change is counted twice. Google’s own guidance for handling single page apps through tag manager is to turn off the browser-history page view setting when you are sending those page views yourself. Pick one mechanism. Whichever you pick, verify the count afterwards rather than assuming.

The Meta pixel needs the same treatment

Everything above is framed around Google because that is where URL-based conversion triggers are most common, but the Meta pixel has the identical problem and it is easier to miss. The base code fires PageView once when the document loads. That part is usually fine, and in a single page app you generally do not want a PageView on every route change anyway.

What does need attention is your standard events. A Lead, Purchase or CompleteRegistration event attached to a thank you page URL will not fire for the same reason the Google conversion does not. Fire those explicitly from the application when the action genuinely completes. If you also send the same events server-side through the Conversions API, make sure both sides share an event ID so the pair deduplicates rather than counting twice.

Verifying the fix like a user

Once you have made a change, test it the honest way.

  1. Open the site at the homepage, as a visitor would, in a fresh browser session.
  2. Navigate by clicking links only. Do not type or paste a single URL into the address bar at any point.
  3. Complete a real form submission end to end and let the app take you to the confirmation on its own.
  4. Confirm the page views appeared for each step, and that exactly one conversion was recorded, not zero and not two.
  5. Refresh the confirmation page and confirm that a second conversion does not appear. If it does, your conversion is still keyed on the page rather than the event.

Why it survives for so long

Everything that normally signals a problem looks fine. The container is published. The tag exists and is correctly written. The conversion action reads as active. A direct URL test fires it. The only thing that would reveal it is comparing recorded conversions against the number of enquiries the client actually received, and that comparison tends to happen in a quarterly review rather than the week the site was rebuilt.

It is also a problem that arrives with a redesign. A site that tracked perfectly for three years can start failing the afternoon it relaunches on a new framework, with no change to the tag manager container at all. The container is identical. The site underneath it is not.

Our free tracking scan reads the public source and the published container in about thirty seconds and shows you what is installed and which triggers fire which tags, which is where URL-based conversion triggers become visible. It is honest about the limit here: a scan reads configuration, so whether a trigger ever fires for a real user navigating the app is something only a live test like the one above can answer.

And the relaunch problem is exactly the kind of thing that happens when nobody is watching. Taglert rechecks every connected account every thirty minutes and emails you when conversion tracking changes, so a framework migration does not quietly take your conversion data with it.

Sources

Stop finding out about broken tracking from your client.

Taglert monitors your pixels and conversion tracking 24/7 and alerts you the moment something breaks.

Start your free trial
  • First scan in ~5 minutes
  • 30-day money-back guarantee
  • No credit card

Related reading