Skip to content

React real user monitoring

Osuite Real User Monitoring runs in any browser application through the @osuite/rum package. In a plain React app — built with Vite or Create React App — you initialize the SDK once at your entry point and wrap your tree in the React error boundary from @osuite/rum/react. That gives you page-load and route-change spans, uncaught error capture, session replay, and end-to-end trace correlation with your backend, with strict privacy masking on by default.

  • A React 18 or newer app bundled with Vite or Create React App.
  • An Osuite ingest token from Settings → API Keys.
  • Your ingest region (ingest.<region>.osuite.io).
Terminal window
npm install @osuite/rum
# or
pnpm add @osuite/rum

React is declared as an optional peer dependency, so importing the error boundary from @osuite/rum/react needs no extra install. The baseline bundle does not pull React in.

  1. Call osuite.init at your entry point

    Initialize the SDK at the top of your app’s entry module, before React renders. A plain React entry runs only in the browser, so a module-scope call is safe here — no server-side guard is needed.

    src/main.tsx
    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import { osuite } from '@osuite/rum';
    import { OsuiteErrorBoundary } from '@osuite/rum/react';
    import App from './App';
    osuite.init({
    apiKey: import.meta.env.VITE_OSUITE_INGEST_TOKEN,
    endpoint: 'https://ingest.<region>.osuite.io',
    apiEndpoint: 'https://api.<region>.osuite.io',
    service: {
    name: 'frontend-web',
    version: import.meta.env.VITE_BUILD_SHA ?? 'dev',
    environment: import.meta.env.MODE,
    },
    });
    ReactDOM.createRoot(document.getElementById('root')!).render(
    <React.StrictMode>
    <OsuiteErrorBoundary fallback={<p>Something went wrong.</p>}>
    <App />
    </OsuiteErrorBoundary>
    </React.StrictMode>,
    );
  2. Set the environment variables

    endpoint is the OTLP host the SDK posts traces and logs to; apiEndpoint is the control-plane host used for session-replay uploads. They are different hosts — copy both from the values above and substitute your region. The ingest token is public (it ships in your JavaScript bundle and is rate-limited per key), so expose it through your bundler’s public env prefix:

    .env
    VITE_OSUITE_INGEST_TOKEN=<your-ingest-token>
    VITE_BUILD_SHA=dev
  3. Run the app

    Start your dev server or serve a production build. Load a page and click around to generate telemetry.

That single call is enough to get page-load spans, fetch and XHR spans, click spans, error logs, and full session replay flowing. Every option beyond apiKey / endpoint / apiEndpoint / service is documented in the SDK reference.

Client-side route changes are captured automatically. The SDK’s navigation tracker patches the History API (pushState, replaceState, popstate), so any router built on it — React Router’s BrowserRouter, TanStack Router, and similar — emits a route_change span per navigation with no extra wiring. Each span adopts the fetch and XHR calls fired during the transition and settles when the DOM is quiet, giving you the true settling time of every page transition.

OsuiteErrorBoundary — imported from @osuite/rum/react in the entry file above — catches React render crashes, reports them as browser.react_error logs with a react.component_stack attribute, and triggers a sampling upgrade so the buffered context around the crash exports. Place it high in your tree, and add an onError callback if you need a side effect:

<OsuiteErrorBoundary
fallback={<ErrorPage />}
onError={(err, info) => {/* optional side-effect */}}
>
<App />
</OsuiteErrorBoundary>

Runtime errors, unhandled Promise rejections, and failed resource loads are captured by default without the boundary. See Frontend errors for every error source and manual capture.

Once you know who is signed in, attach the user identity so sessions, errors, and replays are attributable. Clear it on sign-out. Either call rotates the session so a logged-in session is never conflated with the pre-login one:

import { osuite } from '@osuite/rum';
osuite.setUser({ id: 'u_123', plan: 'pro' });
osuite.clearUser();

Do not pass email, phone, or other PII in id or extra — the SDK does not inspect these values. The full public API, including captureException, addSpanAttributes, and flush, is in the SDK reference.

What you should see

Load a page and click around, then open RUM. Your session appears within a minute under the frontend-web service, with page-load and route-change spans and a session replay attached. Trigger a handled error with osuite.captureException and confirm it lands in the frontend error list, linked to the session that produced it.

No data in Osuite after a few minutes? Work through these checks.

  • Init ran — confirm osuite.init executes before render and only once. Set debug: true (see the debug option) to print init steps, sampling decisions, and transport failures to console.debug.
  • Network — open the browser Network tab and confirm POST requests to ${endpoint}/v1/traces and ${endpoint}/v1/logs are succeeding. A 404 means the ingest token is wrong or rotated — ingest matches the token at the edge, so an unrecognised token fails route matching instead of returning 401/403. A CORS or connection error means endpoint is misconfigured.
  • Endpoint — confirm endpoint and apiEndpoint point at the right region and are distinct hosts. Replay uploads use apiEndpoint, not endpoint.
  • No backend correlation — trace headers are same-origin only by default. If your API is on another origin, add it to the propagation allowlist.

Still stuck? Ask the Investigation Agent or contact support.

  • Session replay — masking, privacy classes, and upload behavior
  • Frontend errors — error sources, event names, and manual capture
  • Sampling — lower cost as traffic grows while keeping broken sessions
  • SDK reference — every configuration option and the full public API