# React Router

Add ProductLense to a React Router app by pasting the script into index.html; route changes are recorded as pageviews without extra code.

React Router changes the URL with the history API, and the ProductLense script listens to `pushState`, `replaceState` and `popstate`, so every route change is recorded as a pageview. The script goes once into the HTML page that loads your app.

## Add the script

Replace `SITE_ID` with the id from Settings → Sites in the ProductLense dashboard. In a Vite project the page is `index.html`:

```html title="index.html" {5-6}
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <script>window.productlense=window.productlense||function(){(window.productlense.q=window.productlense.q||[]).push(arguments)}</script>
    <script defer data-site="SITE_ID" src="https://api.productlense.app/a.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>
```

If you use React Router in framework mode, add the same two tags to the `<head>` in `app/root.tsx`, as shown in the [Remix guide](/docs/install/remix).

> [!IMPORTANT]
> Use a browser router (`createBrowserRouter` or `<BrowserRouter>`). With `createHashRouter` or `<HashRouter>` the route lives after the `#`, which ProductLense drops from the cleaned URL, so route changes inside the hash are not recorded as separate pages. See [pageviews and single-page apps](/docs/track/pageviews-and-spas).

## Identify users

Call `identify` once the signed-in user is known, for example in the component that wraps your routes:

```tsx title="src/AppShell.tsx" {8}
import { useEffect } from 'react';
import { Outlet } from 'react-router';
import { useUser } from './auth';

export function AppShell() {
  const user = useUser();
  useEffect(() => {
    if (user) window.productlense?.('identify', user.id);
  }, [user]);
  return <Outlet />;
}
```

Use the same id you store on the customer in your billing provider; see [how payments are attributed](/docs/revenue/attribution).

## Custom events

```tsx
<button onClick={() => window.productlense?.('event', 'Upgrade clicked', { plan: 'growth' })}>Upgrade</button>
```

See [custom events](/docs/track/custom-events) for naming rules.

## Verify your installation

1. Build and deploy, then open your site in a normal browser window.
2. Open the Live page in the ProductLense dashboard. Your visit appears within a few seconds.
3. In the browser's developer tools, the Network tab shows a `POST` to `https://api.productlense.app/api/e` with status `204`. Click a link to another route: a second pageview follows.

The development server runs on `localhost`, where nothing is sent unless you add `data-dev="true"` to the tag. Ad blockers in your own browser may block the request. If the visit does not appear, follow [Verify your installation](/docs/install/verify).
