> ## Documentation Index
> Fetch the complete documentation index at: https://docs.northbeam.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Page View Events

> Track page changes on single-page apps. Most sites don't need this.

<Note>
  **Most sites don't need anything on this page.** The [Northbeam Pixel](/docs/add-pixel) tracks page views automatically every time a page loads.

  You only need this if your site is a **single-page application (SPA)**: a site that changes pages without a full reload.
</Note>

## Which setup you need

| Your store | What to do |
| :- | :- |
| **Shopify** with a standard theme | Nothing. The pixel handles page views. |
| **Shopify** headless storefront with normal page loads | Nothing. The pixel handles page views. |
| **Shopify** headless storefront that's a single-page app (for example, Hydrogen) | Add `trackPageView`. **Follow this page.** |
| **Custom** site with normal page loads | Nothing. The pixel handles page views. |
| **Custom** single-page app (for example, Next.js, Nuxt, or React Router) | Add `trackPageView`. **Follow this page.** |

Not sure which you have? Ask your developer whether the site is a single-page app.

## How it works

On a normal website, every page change is a full page load. The pixel records a page view each time.

On a single-page app, the URL changes but the page doesn't reload. The pixel doesn't run again, so those page views go **untracked**. To fix this, call `trackPageView()` each time the page changes:

```javascript theme={null}
window.Northbeam.trackPageView();
```

It takes no arguments. It records the page's current URL.

<Warning>
  **Only call `trackPageView()` when the page changes without a reload.**

  The pixel already records a page view on every full page load. Calling `trackPageView()` on full page loads too will create **duplicate page views**.
</Warning>

Before you start, make sure the [Northbeam Pixel](/docs/add-pixel) is installed on every page. `trackPageView()` won't work until the pixel has loaded.

***

## Option A: Google Tag Manager

Use this if you manage scripts through Google Tag Manager.

### Step 1: Confirm the pixel tag is in Google Tag Manager

The Northbeam Pixel must be its own tag, firing on **All Pages**. It has to load before the `trackPageView()` tag runs.

### Step 2: Create a Custom HTML tag

1. Go to **Tags → New**.
2. Set **Tag Type** to **Custom HTML**.
3. Paste this script:

```html theme={null}
<script>
  (function checkNorthbeam() {
    if (window.Northbeam && typeof window.Northbeam.trackPageView === 'function') {
      window.Northbeam.trackPageView();
    }
  })();
</script>
```

The `if` check makes sure the call only runs once the pixel is ready.

### Step 3: Set the trigger

<Warning>
  **Don't use an "All Pages" trigger.** That fires on full page loads too and creates duplicate page views.
</Warning>

Pick the trigger that matches how your site changes pages:

| How your site changes pages | Trigger to use |
| :- | :- |
| History API / `pushState` (most SPAs) | **History Change** |
| A custom data layer event | **Custom Event**, matching your event name (for example, `page_view`, `route_change`, or `Loaded a Page`) |
| Hash-based URLs (`#/page`) | **History Change** (it also catches hash changes) |

To set up a History Change trigger:

1. Go to **Triggers → New**.
2. Set **Trigger Type** to **History Change**.
3. Give it a name, for example `History Change - Virtual Page View`.
4. Attach it to your `trackPageView()` tag.

<Info>
  On some SPAs, History Change fires more than once per page change. If you see duplicate page views, open Preview mode and count the History events for one page change. If there's more than one, limit the trigger to one `gtm.historyChangeSource` value (for example, `pushState` only), or use a custom data layer event instead.
</Info>

### Step 4: Test and publish

In Preview mode, confirm that:

* The Northbeam Pixel tag fires on **all pages**
* The `trackPageView()` tag fires **only** when the page changes without a reload
* No extra page views appear on full page loads

Then **Submit** and publish your container. Unpublished changes don't track anything.

***

## Option B: In your site's code

Call `trackPageView()` from your app's router each time the page changes. Make sure that:

* It runs on every page change that doesn't reload
* It does **not** run on full page loads
* The pixel has loaded before it runs

### React Router example

Call `trackPageView()` whenever the location changes, using [`useLocation`](https://reactrouter.com/en/main/hooks/use-location):

```javascript theme={null}
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

function NorthbeamPageTracker() {
  const location = useLocation();

  useEffect(() => {
    if (window.Northbeam && typeof window.Northbeam.trackPageView === 'function') {
      window.Northbeam.trackPageView();
    }
  }, [location]);

  return null;
}
```

Place this component inside your router. Other frameworks work the same way: call `window.Northbeam.trackPageView()` from the router's navigation hook.

### Shopify Hydrogen

Hydrogen has its own routing and analytics, and the right setup depends on your Hydrogen version. Work with your developer to call `window.Northbeam.trackPageView()` from your router's navigation hook. The same rules apply: only on page changes without a reload, and only after the pixel has loaded.

***

## Troubleshooting

| Problem | Likely cause | Fix |
| :- | :- | :- |
| Page views don't increase after clicking around | `trackPageView()` isn't firing on page changes | Check that your History Change trigger or router hook fires when the URL changes |
| Duplicate page views on first load | `trackPageView()` also fires on full page loads | Use a History Change or custom event trigger, not All Pages |
| Duplicate page views when changing pages (Google Tag Manager) | History Change fires more than once per page change | In Preview mode, count the History events. Limit the trigger to `pushState`, or use a custom data layer event |
| `trackPageView is not a function` in the console | The pixel hadn't loaded when `trackPageView()` ran | Add the `if (window.Northbeam && ...)` check, and make sure the pixel loads first |
| Tag fires but no data shows in Northbeam | The pixel tag isn't published, or it's blocked | Confirm the pixel tag is live and not blocked by your Content Security Policy. Look for `nb-collector` requests in the Network tab |

## Related

* [Northbeam Pixel](/docs/add-pixel): what the pixel does, how to install it, and how to test it
* [Troubleshooting Pixel Tracking](/docs/guide-to-troubleshooting-pixel-tracking)
