# React (/docs/developer/react)





# React [#react]

Initialize the SDK from a component that mounts once near the root of your application.

<Callout type="info">
  Using Next.js? Use the [Next.js guide](/docs/developer/nextjs) instead — it
  handles App Router and Pages Router differently from a plain React SPA.
</Callout>

```tsx
import { useEffect } from "react";
import { initUcoderInsight } from "ucoder-insight";

export default function Analytics() {
  useEffect(() => {
    void initUcoderInsight("YOUR_PUBLIC_TRACKING_ID");
  }, []);

  return null;
}
```

## Where to render it [#where-to-render-it]

Render `<Analytics />` once, near the root of your component tree — for example in `App.tsx`:

```tsx
function App() {
  return (
    <>
      <Analytics />
      <YourAppRoutes />
    </>
  );
}
```

Do not initialize the SDK separately in every route component.

<Callout type="warn">
  In development, React's `StrictMode` intentionally mounts effects twice. You
  may see `initUcoderInsight` run twice in the console — this is expected and
  does not cause duplicate tracking in production.
</Callout>

## Tracking route changes in a single-page app [#tracking-route-changes-in-a-single-page-app]

React Router (and most other client-side routers) navigate by calling the browser's `history.pushState` / `history.replaceState` APIs, or by triggering `popstate` (back/forward button), instead of a full page reload. A normal analytics script that only listens for the `load` event would miss every one of these navigations.

Ucoder Insight handles this automatically once initialized:

* It &#x2A;*patches `history.pushState` and `history.replaceState`** so that every programmatic navigation (e.g. `navigate("/pricing")`, `<Link to="/about" />`) is detected the moment it happens.
* It **listens for the native `popstate` event** to catch browser back/forward navigation.
* On each detected change, it records a new page view for the updated URL — automatically, with no extra code in your routes or components.

<Callout type="info">
  Because tracking is based on `pushState`/`replaceState`/`popstate`, this works
  with any router built on the History API (React Router, TanStack Router,
  Wouter, etc.) without router-specific integration code.
</Callout>

You do not need to call any `trackPageView()`-style function manually — as long as `<Analytics />` is mounted once at the root, every route change is picked up automatically for the lifetime of the page.

<Callout type="warn">
  If your app uses **hash-based routing** (URLs like `/#/pricing`) instead of
  the History API, route changes may not trigger `pushState`/`popstate` the same
  way. Verify tracking works correctly if you're on a hash router.
</Callout>

See [Configuration](/docs/getting-started/configuration) for supported options.
