How to Track Page Views in a React SPA with Google Analytics 4 (GA4)

Introduction

Adding Google Analytics 4 to a traditional multi page site is simple: drop the gtag snippet into the document head and every navigation triggers a page view. In a React Single Page Application using Vite and React Router this default behavior breaks. Route changes do not reload the page, so GA4 will not detect subsequent navigations unless you explicitly tell it to. This article explains a reliable, production ready approach to track page views in a React SPA with GA4, avoid double counting, and validate your setup.

Overview of the approach

The pattern has three parts: 1) store your GA measurement ID in an environment variable, 2) load gtag.js but disable the automatic initial page view, and 3) send a manual page_view event on every route change inside React. This ensures every virtual navigation in the SPA is recorded as a page view with the correct URL and title.

1. Environment setup (do not hardcode)

Why: Keeping the Measurement ID out of source files prevents accidental exposure in public repos and makes it easy to use different IDs for dev and prod. Add your GA4 measurement ID (looks like G-XXXXXXXXXX) to your Vite .env file. For example, in .env add a line like VITE_GA_TRACKING_ID=G-XXXXXXXXXX and access it from index.html with the Vite html replacement syntax.

2. Load gtag and disable automatic page view

What to do: Keep the standard gtag.js script in index.html, but when calling gtag(‘config’, …) include the option send_page_view: false. That prevents the default automatic page_view that gtag would fire on initial load. If you do not disable it you risk double counting the first view when your React router triggers a manual page view later.

Example behavior to include in index.html (conceptual): include the gtag script tag that references your VITE_GA_TRACKING_ID and call gtag(‘config’, ‘G-XXXX’, { send_page_view: false }). Use Vite replacement for the ID so you do not hardcode it.

3. Send a manual page_view on every route change

What to do: Inside your React app create a small hook or component that listens to React Router location changes and calls gtag with a page_view event. Use the location object so the event fires on initial mount and every navigation thereafter. This guarantees GA4 receives the correct page_path, page_location, and page_title.

  • Listen to the full location including pathname and search so query strings are tracked.
  • Send page_location with window.location.href to capture the full absolute URL.
  • Use document.title or a route-level title to populate page_title.

Conceptual example: inside a hook or component use useLocation from react-router-dom and a useEffect that runs when location changes. In the effect call window.gtag(‘event’, ‘page_view’, { page_path: location.pathname + location.search, page_location: window.location.href, page_title: document.title }). Wrap the call with a guard that window.gtag exists and only run in production.

Troubleshooting and validation

  • Use GA4 DebugView to see incoming events in near real time. Enable debug mode in the GA4 debug view or use the Google Analytics Debugger extension.
  • Check the browser Network tab to verify requests to Google Analytics are being sent after navigation. Filtering network calls for gtag or google-analytics can help.
  • If you see intermittent misses, confirm your listener runs after the router updates the URL and document.title. If your title updates asynchronously you may want to delay the page_view call until title is set.
  • Do not load both gtag and Google Tag Manager to send the same page views without coordinating since this can cause duplicates.

Alternative approaches and considerations

You can also use libraries like react-ga4 or a Tag Manager setup to handle SPA page views, but the underlying requirement remains the same: send a page_view on every virtual navigation. If you use GTM make sure Tag Manager is configured to listen for history events or manual push events and avoid duplicate automatic tracking.

Summary and best practices

In short: keep your measurement ID in an environment variable, disable automatic send_page_view in the initial gtag config, and fire manual page_view events from a React listener that runs on initial mount and every route change. Validate with GA4 DebugView and browser network tools. Following this pattern will give you accurate SPA page view data in GA4 and prevent common pitfalls like missing views or double counting.

If you want, I can provide a copyable React hook or a minimal index.html snippet formatted for Vite that implements these steps. Tell me whether you use React Router v6 or another router and whether you prefer a hook or a component solution.

Share:

LinkedIn

Share
Copy link
URL has been copied successfully!


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

Close filters
Products Search