Single-page applications do not issue new HTTP requests on navigation, so analytics SDKs see exactly one pageview per session — the landing page. Every subsequent route the user visits is invisible. Funnel analysis breaks: you cannot tell which internal pages drive conversion, which articles users read, or where they drop off. Bounce rate looks artificially perfect because no second pageview ever fires. This is the most common analytics failure mode in Next.js, Remix, and SvelteKit apps.
High because missing route-change tracking silently discards the majority of pageview data in any SPA.
Fire a pageview event on every client-side route change. In Next.js App Router, create components/analytics-provider.tsx with usePathname() and useSearchParams() inside a useEffect that calls your SDK's pageview function whenever either value changes. Mount the provider inside a <Suspense> boundary in app/layout.tsx. For Pages Router, attach router.events.on('routeChangeComplete', ...) in _app.tsx.
ID: marketing-analytics.core-analytics.page-view-tracking
Severity: high
What to look for: In single-page applications (React, Next.js, Vue, etc.), a page view event must be explicitly fired on each client-side navigation — it does not happen automatically like in traditional server-rendered sites. Look for:
usePathname or useSearchParams hook in a client component that fires an analytics event when the value changesrouter.events.on('routeChangeComplete', ...) listener<script> in the page template that fires on load is sufficientPass criteria: Page views are tracked on every navigation via at least 1 mechanism, either automatically by the chosen SDK, or via an explicit event listener on route changes. Pass if the SDK handles it natively (Plausible, Fathom, Vercel Analytics, or GTM with history trigger). Count the number of route-change listeners or page-view calls found and report the count even on pass.
Fail criteria: An SPA or hybrid framework is used, analytics is present, but no route change listener or page view call is found beyond a single initialization — meaning only the first page load registers a view. A static gtag('config', ...) call with no usePathname or router.events listener does not count as pass in an SPA.
Skip (N/A) when: No analytics script is present (caught by script-present check). Skip also if the project is a fully server-rendered app where each navigation is a full HTTP request (traditional Rails, Django with no client-side routing).
Detail on fail: "Next.js App Router detected but no usePathname/useSearchParams route change listener found. Only the initial page load will register a page view in analytics — all client-side navigations are invisible."
Remediation: For Next.js App Router, create an analytics provider component:
// components/analytics-provider.tsx
'use client'
import { usePathname, useSearchParams } from 'next/navigation'
import { useEffect } from 'react'
import { pageview } from '@/lib/analytics'
export function AnalyticsProvider() {
const pathname = usePathname()
const searchParams = useSearchParams()
useEffect(() => {
pageview(pathname + searchParams.toString())
}, [pathname, searchParams])
return null
}
Add this component inside a <Suspense> boundary in your root layout. For Pages Router, add a router.events listener in _app.tsx.