# Track page views (https://docs.salla.dev/twilight/react/recipes/track-page-views)



Use this when you need one analytics view per page the customer sees, not one per full page load.

**Goal:** every page the customer opens sends a `viewPage` event to the data layer, once.

After the first load, links navigate in the browser without reloading, so a script that runs once misses them. After each client-side navigation the engine dispatches the SDK event `route::changed` with the current page (`slug`, `id`, `title`, `url`, ...). You subscribe once from a component in your layout. The `useGtm` hook pushes to `dataLayer` only when the store has a Tag Manager id. Background: [Salla SDK and events](https://docs.salla.dev/twilight/react/guide/salla-sdk-and-events), [SEO and head](https://docs.salla.dev/twilight/react/guide/seo-and-head).

### Create a tracker component [#create-a-tracker-component]

It sends the first view on mount, then one per navigation, and ignores a repeat of the same URL.

```tsx title="app/components/PageViewTracker.tsx"
import { useEffect, useRef } from 'react';
import { useGtm } from '@salla.sa/twilight-theme-engine/hooks/useGtm';

type Page = { slug?: string };

export function PageViewTracker() {
  const gtm = useGtm();
  const gtmRef = useRef(gtm);
  gtmRef.current = gtm;
  const last = useRef('');

  useEffect(() => {
    const send = (page?: Page) => {
      const url = window.location.pathname + window.location.search;
      if (url === last.current) return; // dedupe
      last.current = url;
      gtmRef.current.viewPage(page?.slug ?? window.location.pathname);
    };
    const listener = (...args: unknown[]) => send(args[0] as Page | undefined);
    let bound = false;

    const bind = () => {
      const event = window.salla?.event;
      if (bound || !event?.on) return;
      event.on('route::changed', listener);
      bound = true;
    };

    send();
    bind();
    document.addEventListener('theme::ready', bind);
    return () => {
      document.removeEventListener('theme::ready', bind);
      if (bound) window.salla?.event?.off?.('route::changed', listener);
    };
  }, []);

  return null;
}
```

### Render it once in your layout [#render-it-once-in-your-layout]

Put it next to the other global pieces.

```tsx title="app/components/layout/ThemeLayout.tsx"
import { PageViewTracker } from '../PageViewTracker';

// ...inside the returned JSX of ThemeLayout:
<PageViewTracker />
```

## Check it worked [#check-it-worked]

In the browser console run `window.dataLayer` (the store needs a Tag Manager id). Click through three pages: you see one `viewPage` entry per page, with no duplicates.

<Callout type="warn" title="Traps">
  * **A one-time `<script>` or a mount effect alone sees the first page only.** Later navigations never remount the layout.
  * **`route::changed` is a client-navigation event.** The first view is not guaranteed to come from it, which is why the tracker sends once on mount and dedupes by URL.
  * **Product views are already reported to the store.** On a product page the engine dispatches the SDK event `Product Viewed` once per visit, which the store's analytics and pixels read. Do not dispatch it yourself; listen to it if your own tool needs product views. See [Salla SDK and events](https://docs.salla.dev/twilight/react/guide/salla-sdk-and-events#know-when-a-product-is-viewed).
  * **`useGtm().viewPage(type)` does nothing** unless the store has a Tag Manager id. For other tools, call `window.dataLayer.push(...)` or your tool's SDK directly.
  * **Render the tracker once, in the layout.** In a page it adds a listener per page.
  * **Remove the listener with the same function reference**, or it leaks.
  * **`window.salla` is `undefined` on the server and before the SDK is ready.** Keep the code in `useEffect` and bind on `theme::ready`.
  * **Consent:** gate sending on your cookie banner if the store requires it.
</Callout>

## Next [#next]

* [React to cart events](https://docs.salla.dev/twilight/react/recipes/react-to-cart-events): track add-to-cart the same way.
* [Salla SDK and events](https://docs.salla.dev/twilight/react/guide/salla-sdk-and-events): the event list.
