React + Vite quickstart with @nais/apm ΒΆ

Instrument a React single-page app (SPA) built with Vite using @nais/apm, the Nais browser telemetry SDK, and see your errors as issues in Nais APM. This is the fast path for a client-rendered React app: init at the entry point, a React error boundary, and route tracking for React Router.

Prerequisites ΒΆ

  • A React SPA built with Vite, deployed on Nais.
  • The @nais package registry configured in your .npmrc. This is a one-time setup β€” follow step 1 of Track frontend errors.

Install ΒΆ

sh

The React helpers used below β€” ApmErrorBoundary, enableApmReactRouterV6, ApmRoutes β€” live in the @nais/apm/react entry point.

1. Initialize at the entry point ΒΆ

Call init() once, as early as possible, in your app's entry module β€” before you render. On Nais, everything resolves automatically except the team, which you pass as namespace:

tsx

init() is idempotent, so React Strict Mode's double render won't re-initialize. App name, version, environment, and the collector URL resolve automatically from Nais meta tags or NAIS_* build env. On localhost, where no collector resolves, it sends nothing and echoes every signal to the console.

2. Add an error boundary ΒΆ

Wrap your app in ApmErrorBoundary. It reports each caught render error exactly once through captureException β€” picking up the SDK's fingerprint and context β€” and renders a fallback:

tsx

The fallback can also be a render prop (error, resetError) => node, and withApmErrorBoundary(Component, props?) gives you the HOC form.

3. Track route changes (React Router) ΒΆ

For client-side navigation, report route changes so pageviews and errors carry the right URL. @nais/apm re-exports faro-react's route tracking as ApmRoutes. Call enableApmReactRouterV6 once after init(), passing your own react-router-dom exports (the SDK never imports react-router itself, which keeps its version out of its dependency tree), then render <ApmRoutes> where you'd render <Routes>:

tsx
tsx

4. Enable tracing (optional) ΒΆ

To connect browser spans to your backend traces in Tempo, turn on tracing. @grafana/faro-web-tracing is lazily loaded, so it stays out of your bundle unless you enable it:

ts

Trace headers are propagated to your own origin and *.nav.no backends by default (a non-overridable security floor). Add more origins with tracing: { propagateExtraOrigins: ['https://api.partner.example'] }. Your backend still has to allow the traceparent CORS header and export spans to Tempo β€” see frontend-to-backend trace propagation.

Ship readable stack traces ΒΆ

Stack traces resolve server-side from sourcemaps on the CDN β€” nothing to upload (how it works). You just have to emit them. For Vite:

js

A Vite SPA served from the CDN is the ideal case β€” the .map files sit next to the bundle. See Sourcemap deobfuscation β†’ Requirements.

Don't send personal data ΒΆ

Telemetry lands in a shared Loki instance every team can read, so identities must never reach it. setUser takes an opaque/hashed id only β€” emails, idents, and fΓΈdselsnummer are dropped in code β€” and a mandatory PII scrubber runs over every outgoing signal. See Privacy: PII scrubbing for the full rules.

See your errors as issues ΒΆ

Deploy, then trigger an error. Open your service in Nais APM and go to the Issues tab. Your exception shows up as an issue, grouped with other occurrences, with its (deobfuscated) stack trace and impact. Filter the source to Browser to isolate frontend issues.

From there you can triage it or create an alert.