- Define which navigation counts as a pageview before debugging counts.
- Choose one owner for each pageview instead of stacking automatic and manual tracking.
- Compare accepted events on a deployed build; a queued browser send is not delivery proof.
Define the pageview you expected
Before removing code, describe the expected event for a full load, a client-side route change, a repeated same-path action, and a browser back navigation. A query-only change may represent a new screen in one application and a harmless filter update in another. Your tracking definition must make that choice explicit.
Reproduce one navigation at a time on a deployed build. Record the page path, time, expected count, and accepted records. Do not diagnose a production duplication problem from the number of render calls alone: a React render and an accepted analytics pageview are different events.
Find every place that can send a pageview
Inspect your shared layout, analytics provider, route hooks, tag manager, and any page-level script. A tracker installed in both the shared layout and an individual page can create two independent owners. A click handler that sends a pageview before routing can overlap with a later route observer.
For GA4 specifically, Google documents the risk of combining manual SPA measurement with automatic history measurement. Select the supported method for your setup and follow its verification instructions. Do not assume every analytics library needs a custom route-change effect simply because the application uses Next.js.
Separate lifecycle bugs from duplicate installation
A route observer can be attached repeatedly if its effect cleanup is missing. A development environment can also exercise component lifecycles differently from a deployed build. These are reasons to inspect listener ownership and cleanup, not reasons to suppress an arbitrary second event everywhere.
Check when the initial pageview fires relative to route initialization. If you own a manual implementation, document how it treats visibility, query changes, and a restored page. When a library already owns those transitions, use its documented behavior rather than adding another listener on top.
| Action | Question to answer | Evidence |
|---|---|---|
| Fresh visible load | Who sends the initial pageview? | Accepted initial record |
| Link to another screen | Does one owner detect navigation? | New path and expected count |
| Back / forward | Does restoration match the definition? | Recorded path and event time |
| Retry after a failed send | Can one event be accepted twice? | Event identity and collector behavior |
Verify what the collector accepted
A visible script proves that a script element exists. A browser request proves that the browser attempted a send. Neither alone proves that your collector accepted a valid event for the right registered website. Check the response where available and the corresponding accepted record or provider debugging view.
For a duplicated completion or a retry, use a stable event identity only when the receiving system supports it. Do not generate a new identifier for every retry and then expect downstream deduplication to recognize the original action. Also check timestamp and environment selection before concluding that two displayed rows are two production events.
Keep a regression check beside the integration
After the fix, repeat the navigation matrix without changing consent, browser, environment, or site settings halfway through. Include an abandoned navigation and a return to an already visited path. Save the expected behavior so the next analytics change has a concrete comparison.
VisitLift’s installation guide asks you to verify received tracking after publishing. Start there if you use VisitLift. For a broader signup plan, use the free generator to keep browser intent and confirmed outcomes separate; it does not supply a second pageview sender.