Single-Page Apps
How the Ours Privacy Web SDK tracks page views on client-side route changes in single-page apps, and the routing setups that need extra handling.
Use this page to understand how the Ours Privacy Web SDK handles page views when your site navigates without a full browser reload, and the few routing setups that need extra attention.
This applies to any site that changes the URL without reloading the document: React, Vue, Angular, and Svelte apps, and also page builders like Wix, Squarespace, Framer, and Webflow that animate between pages.
Route Changes Are Tracked Automatically
When the Web SDK is installed with track_web_events enabled, which is the default in the snippet on the Install Ours page, it tracks page views for you:
- On the initial page load.
- On every client-side route change, by watching
history.pushStateand the browser'spopstateevent.
You do not need to add a router listener, a useEffect, or any framework-specific code. Initialize the SDK once when your application starts, then let the router navigate.
Important: Because route changes are already tracked, adding your own manual page view call on route change will record every in-app navigation twice and inflate your page view counts. If you have existing manual tracking code, remove it rather than layering the two.
Telling Initial Loads From In-App Navigation
Automatically tracked page views carry properties that let you separate a real page load from an in-app navigation, so you can report on them differently without turning off automatic tracking. Subsequent page views also include engagement time for the view the visitor just left. The full property list is in the Web SDK reference.
Routing Setups That Need Extra Handling
Hash-based routing
Some older single-page apps route by changing only the URL fragment, for example example.com/#/pricing. Assigning to the hash directly does not always go through history.pushState, so a route change may not be detected.
If you use a hash router and in-app navigations are missing from Recent Events, track them yourself on route change:
window.addEventListener('hashchange', () => {
ours('track', 'page_view');
});Only add this if page views are actually missing. On a router that uses pushState, this would double count.
View transition APIs
Frameworks that swap page content with a view transition API, such as Astro's client router, still update history through pushState, so tracking continues to work. Confirm this on your own site by navigating between two pages and watching Recent Events, since behavior depends on how your framework is configured.
Content inside an iframe
Embedded booking widgets, schedulers, and checkout flows often render in an iframe. A snippet on the parent page cannot see navigation inside that iframe, so steps completed there are not tracked as page views. Track the outcome you care about from the parent page instead, using the widget's own callback or message event, and send it as a custom event.
Verifying Your Setup
- Load your site and confirm a page view appears in Recent Events.
- Navigate to another page using in-app links, without reloading.
- Confirm a second page view appears, with the new path.
- Use the browser back button and confirm a third page view appears.
If step 2 or 4 produces nothing, your router is likely not using pushState. If they produce two page views each, you have manual tracking running alongside the automatic tracking, so remove the manual call.
Next Steps
- Web SDK reference: configuration options and the full auto-tracked event list.
- Installing on a headless CMS: where the snippet belongs in a decoupled setup.
- Cookie Consent installation: add the consent banner so it loads before other tracking scripts.
If you need further assistance, contact us at support@oursprivacy.com.
How is this guide?

