OnyxMetric
Integration GuidesReact

React Router

Add OnyxMetric analytics to your React Router app

In React Router framework mode (v7 and later, the successor to Remix), app/root.tsx renders the <html> document for every route. Add the tag to the <head> of its Layout export. This replaces the Remix guide for v7 projects.

Copy your tracking snippet

In OnyxMetric, open your site's Site Settings and copy the tracking script. It looks like this:

<script src="https://app.onyxmetric.com/api/script.js?siteId=YOUR_SITE_ID" async></script>

YOUR_SITE_ID is the numeric ID of your site.

Add the snippet to React Router

Open app/root.tsx and add the tag inside <head> in Layout, after <Meta /> and <Links />. Layout wraps the app, HydrateFallback, and ErrorBoundary, so the tag is present in all of those states.

app/root.tsx
export function Layout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <Meta />
        <Links />
        <script src="https://app.onyxmetric.com/api/script.js?siteId=YOUR_SITE_ID" async></script>
      </head>
      <body>
        {children}
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}

If root.tsx has no Layout export, put the tag in the <head> of the default App component. Restart the dev server afterwards. Navigation via <Link> and useNavigate() is tracked automatically.

Check that it works

Load your live site and click through a few pages. Pageviews show up in the OnyxMetric dashboard within seconds.

If nothing arrives:

  • View the page source and search for script.js?siteId= to confirm the tag is present.
  • In the browser's Network tab, check that script.js returns 200 and that requests go to /api/track.
  • Turn off ad blockers, or proxy the script through your own domain.
  • Read script troubleshooting for other causes.

Track custom events

Route modules also render on the server, so check for window.

app/routes/pricing.tsx
export default function Pricing() {
  const handleClick = () => {
    if (typeof window !== "undefined") {
      (window as any).onyx?.event("plan_selected", { plan: "pro" });
    }
  };

  return <button onClick={handleClick}>Choose Pro</button>;
}

Troubleshooting

  • Nonce-based Content Security Policy: if you pass a nonce to <Scripts /> and <ScrollRestoration />, give the OnyxMetric tag the same nonce, or the browser will block it.

Next steps

On this page