Snowplow Inspector: Event Grouping in Single-Page Applications (SPAs)

Snowplow  
Edited

Overview

When you use the Snowplow Chrome Inspector with a Single-Page Application (SPA), events appear grouped under a single page section even when your page_url is correct and your page view IDs are changing. This is a visualization limitation in the Inspector, not a Snowplow tracking bug. The underlying event data is accurate.

Why this happens

When a page loads in the browser, Chrome DevTools creates a single HAR (HTTP Archive) "page object" with a unique pageref ID. The Inspector's Timeline component groups events using pageref as the primary key, with page URL pathname as a fallback.

In traditional multi-page apps: Each full page load creates a new HAR page object. Network requests, including Snowplow events, get associated with the relevant pageref, which allows clean event grouping by page.

In SPAs: The browser only creates one HAR page object on the initial load (e.g., loading index.html). Every subsequent route change (/products, /checkout, etc.) happens via DOM manipulation, not a real page load. DevTools never creates new page objects, so every Snowplow event in the session gets stamped with that original pageref value. Since all events share the same pageref, they collapse into a single section in the Inspector.

Your event data is accurate

The url/dl field on each event contains the actual page URL at the time it fired. In your warehouse, the web_page entity UUID separates events by page when you call trackPageView() on route changes. The grouping issue exists only in the Inspector's timeline view.

Solutions

Option 1: Call trackPageView() on each route change (recommended)

Call trackPageView() whenever your application navigates to a new route. Each call generates a new web_page context entity with a fresh UUID, which separates events by virtual page in both the Inspector and your data warehouse.

React Router example:

import { trackPageView } from '@snowplow/browser-tracker';

// Inside your route change listener or useEffect
trackPageView({ title: 'Products Page' });

This is also Snowplow best practice for SPAs. If you're not calling trackPageView() on route changes, you're missing proper SPA page view data in your pipeline, so this fix is worth making regardless of the Inspector issue.

Option 2: Use preservePageViewIdForUrl

Set preservePageViewIdForUrl at tracker initialization to automatically generate a new page view ID when the URL changes. This is useful if your routing setup makes it difficult to call trackPageView() on every navigation event.

newTracker('sp', 'collector.example.com', {
  appId: 'my-app',
  preservePageViewIdForUrl: 'pathname' // new ID generated when URL pathname changes
  // options: 'pathname', 'pathnameAndSearch', 'full'
});

Option 3: Filter by URL path in the Inspector (temporary)

While you're setting up one of the above, use the Inspector's regex filter in the top toolbar to narrow events by URL path (e.g., filter for /checkout). This lets you isolate the events you need during debugging without fixing the underlying grouping behavior.

Options 1 and 2 fix both the Inspector display and your pipeline data. Option 3 is only useful while you're putting one of those in place.