Skip to main content
@interfere/vite wires Interfere into your Vite build and your running app together. You set it up once: drop in the plugin and start the SDK before render. After that, Interfere records everything the app produces (errors, sessions, traces, and logs) with no sampling and no knobs to turn. It then collapses related symptoms into one problem, weighs how much it matters, and works out the cause for you. It works for a plain single-page app and for server-rendered setups like TanStack Start. This page covers the install plus the handful of settings most teams actually touch.
You’ll need a surface public key (interfere_pub_<region>_…) from Surfaces. For source-map upload and release tracking you’ll also want an Interfere API key (interfere_secret_<region>_…). See Environment variables.

Prerequisites

  • A Vite app using React >= 19
  • Node.js >= 20

Quick start

These four steps cover a single-page app. If you server-render (TanStack Start, for example), do these first, then Server-side rendering.
1

Install the packages

2

Add the plugin to your Vite config

The plugin stamps your public key into the build and, with build.sourcemap on, uploads source maps so a production stack trace points back to your original code instead of minified output.
vite.config.ts
3

Start the SDK before render

Call init() before you mount React, so the SDK is capturing the moment your app loads.
src/main.tsx
4

Add the provider

Wrap your app once so components can reach the SDK through hooks.
src/app.tsx
That’s the whole install for a single-page app. Run it, trigger an error, and it shows up in your dashboard within seconds, already grouped and triaged. Open your workspace to confirm it. (To control whether the SDK runs in development, see the FAQ.)

Environment variables

Only VITE_-prefixed variables reach the browser. Keep INTERFERE_API_KEY out of any VITE_ name so your secret never ships in the client bundle.

Configuration

The defaults are meant to be left alone, so plenty of apps never open this section. When you do need it, serviceName lives on the init() call from step 3, and what’s captured is set on the provider.

Name your app

Running more than one app on Interfere, like a storefront and an admin panel? Give each one a serviceName so its problems, sessions, and metrics stay attributed to it.
src/main.tsx
string
default:"interfere-sdk"
A stable name for this app. Interfere uses it to keep each surface’s data separate, and to correlate the same issue across surfaces into one problem.

Environments

Interfere tags everything it captures with the environment it ran in, and labels each release the same way. In the dashboard, releases carry an environment badge and you can filter by environment, so production data and preview data stay apart. Set it with INTERFERE_ENVIRONMENT:
The label is free-form: production, staging, preview, canary, or whatever you name it. If you don’t set it, Interfere falls back to VERCEL_ENV, then NODE_ENV, then development. The plugin stamps the value into your build, so it needs no VITE_ prefix.
Interfere drops local-like environments (development, local, test) before ingestion, so everyday local traffic never reaches your dashboard. A build with no environment set defaults to development and is filtered out. Set a real environment, or turn off environment filtering for the surface, to capture from one.

Custom ingest domain

Point the browser SDK at a subdomain of your own site to survive ad-blocker deny-lists (no server component involved), or at a regional endpoint. Set apiHost on the plugin:
vite.config.ts
Add a CNAME pointing your subdomain at the target shown in your dashboard; Interfere provisions TLS automatically. To target a regional endpoint instead, set apiHost (or VITE_INTERFERE_API_URL) to that URL. Ingest requests never carry cookies.

Choose what’s captured

Interfere captures every signal below unless you say otherwise. Switch any of them off with the provider’s plugins prop, for instance to drop session replay when recordings aren’t welcome:
src/app.tsx
object
Each signal can be toggled on or off. All default to on.
  • errors: uncaught exceptions
  • logs: console output
  • device: device and browser info
  • pageEvents: pageviews and clicks
  • rageClick: rage-click detection
  • replay: session replay

Identity

Sessions start out anonymous. Attach the signed-in user and a problem will show you exactly who ran into it in Users, by name and email instead of a random id. Call identity.set() from the useInterfere hook once you have a user:
string
required
Your internal, stable user ID. Use this rather than the email.
object
required
Where the identity came from.
  • type: one of clerk, auth0, or custom
  • name: the provider’s display name, for example “Clerk”
string
Display name.
string
Email address.
string
Avatar URL.
object
Any extra metadata you want attached to the user (Record<string, unknown>).
identity.set() is deduplicated per session, so calling it on every render is fine. Identity clears automatically when the session rotates.
Everything captures out of the box. To tie that to a cookie banner or a privacy choice, hand the provider a consent object. From then on only essential capture (error tracking and logs) runs, plus whichever categories you switch on:
Update consent at runtime through the same hook:
Interfere works with any consent platform (c15t, CookieYes, OneTrust). Map its booleans to the categories above.
Set the consent prop from the provider’s first render so a non-consented feature never gets a chance to load. Update it through the hook as the user changes their mind.

Mask replay data

Session replay records the DOM, so anything on screen can land in a recording. Password inputs are masked for you, and three privacy classes are always active — they’re hard-wired, so no config can turn them off. Add a class to any element to redact more; nothing else to set up.
To mask input values by type, pass a replay config to the provider’s plugins. Only passwords are masked by default; set maskAllInputs: true for a stricter baseline that redacts every input.
src/app.tsx
For finer control, plugins.replay also forwards raw rrweb options — maskTextSelector / blockSelector / ignoreSelector to target elements by CSS selector, maskInputFn / maskTextFn to transform masked values, and recordCanvas. These are additive; the interfere-* classes still apply.

Report a handled error

Uncaught errors are already covered. For one you catch and handle yourself but still want on the record, call capture:

Server-side rendering

If you server-render with TanStack Start (or another Nitro-based setup), do the Quick start steps, then boot server instrumentation and split the browser init() behind a runtime guard. Telemetry posts directly from the browser — set apiHost on the plugin for an ad-blocker-resistant subdomain.
1

Boot server instrumentation and browser init from the router

Call register() on the server (OTel bootstrap) and init() in the browser. Guard on typeof window so each half only runs where it should.
src/router.tsx
You can drop step 3 (init() in main.tsx) from the Quick start — the router handles it now.
2

Wrap the root route with the provider

src/routes/__root.tsx
Report handled errors from the server (route handlers, server functions) with captureError:

Other frameworks

Next.js

@interfere/next: a build plugin, server instrumentation, and a provider for the App Router.

NestJS

@interfere/nest: a backend module plus instrument.ts for server error capture.

FAQ

The Vite SDK runs whenever it’s loaded, including locally. To capture only in production builds, gate it: init({ enabled: import.meta.env.PROD }).
Set apiHost on the plugin to a subdomain of your own site — a CNAME to the target shown in your dashboard. Telemetry then posts to your own origin, no server route needed.
Yes. Give each app its own serviceName. When the same issue hits more than one surface, Interfere correlates it into a single problem instead of a separate alert per app.
Yes. Disable the signals you don’t want with the provider’s plugins prop, for example <InterfereProvider plugins={{ replay: false, pageEvents: false }}>. To drop browser tracing from the bundle entirely, pass init({ tracing: false }).