# Single-page apps and route changes

> Client-side navigation is counted automatically, including the back button; hash routers need one attribute.

Section: Tracking script  
Canonical page: https://statsy.co/docs/tracking-script/spa  
Last updated: 2026-09-15

Single-page apps need no extra code. The script watches the URL itself and records a pageview whenever it changes.

## How it works

On load, the script wraps `history.pushState` and `history.replaceState`, and listens for `popstate`. After each of those, it compares the current path and query string with the last one it saw. If they differ, it records a pageview.

Every client-side router in common use navigates that way, so React Router, Vue Router, the Next.js and Nuxt routers, SvelteKit, Remix, Astro view transitions and Turbo are all counted without configuration. The back and forward buttons are counted too.

## What happens on a route change

- The scroll depth reached on the page you are leaving is sent, then the counter resets for the new page.
- A pageview is recorded for the new URL.
- Heatmap recording is re-evaluated, because it is configured per path.
- Widgets that are limited to certain pages are shown or hidden.

## Hash routers

A hash router keeps the path the same and changes only the fragment after `#`, so by default those navigations are not pageviews. Opt in on the tag:

```html
<script defer data-website-id="ws_XXXXXXXX" data-domain="yourstartup.com" data-hash-routing="true" src="https://statsy.co/js/script.js"></script>
```

The fragment then becomes part of the URL the script compares, and `#/pricing` is a page of its own.

## Doing it by hand

If you have a router that changes views without touching the History API, record the pageview yourself after the view changes:

```js
statsy.pageview();
```

> **Watch out.** Do not add a route-change listener that calls `statsy.pageview()` on top of the automatic tracking, and do not load the script again on navigation. Either one doubles every pageview. This is the most common mistake in [Google Tag Manager](https://statsy.co/docs/install/gtm) setups.

Previous: [Serve the script from your own domain](https://statsy.co/docs/tracking-script/first-party-domain)  
Next: [Exclude your own visits and bots](https://statsy.co/docs/tracking-script/exclude-visits)