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

npm install @interfere/vite @interfere/react
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
import { interfere } from "@interfere/vite/plugin";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";

export default defineConfig({
  build: { sourcemap: true },
  plugins: [react(), interfere()],
});
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
import { init } from "@interfere/vite/init";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";

import { App } from "./app";

init();

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <App />
  </StrictMode>
);
4

Add the provider

Wrap your app once so components can reach the SDK through hooks.
src/app.tsx
import { InterfereProvider } from "@interfere/vite/provider";

export function App() {
  return <InterfereProvider>{/* your routes */}</InterfereProvider>;
}
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

VariableRequiredDescription
VITE_INTERFERE_PUBLIC_KEYYesYour surface public key (interfere_pub_<region>_…, where <region> is us or eu). Safe to expose; the plugin stamps it into your build.
INTERFERE_API_KEYFor releasesInterfere API key (interfere_secret_<region>_…). A build-time credential for source-map upload and release metadata.
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, both settings live on the same init() call from step 3.

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
init({ serviceName: "@acme/storefront" });
serviceName
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.

Choose what’s captured

Interfere captures every signal below unless you say otherwise. Switch any of them off with plugins, for instance to drop session replay when recordings aren’t welcome:
src/main.tsx
init({ plugins: { replay: false } });
plugins
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:
import { useInterfere } from "@interfere/vite/provider";

function SyncIdentity() {
  const { identity } = useInterfere();
  const { user } = useAuthProvider(); // Clerk, Auth0, etc.

  useEffect(() => {
    if (!user) return;
    identity.set({
      identifier: user.id,
      name: user.name,
      email: user.email,
      source: { type: "clerk", name: "Clerk" },
    });
  }, [user]);

  return null;
}
identifier
string
required
Your internal, stable user ID. Use this rather than the email.
source
object
required
Where the identity came from.
  • type: one of clerk, auth0, or custom
  • name: the provider’s display name, for example “Clerk”
name
string
Display name.
email
string
Email address.
avatar
string
Avatar URL.
traits
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:
<InterfereProvider consent={{ analytics: true, replay: false }}>
  {/* your routes */}
</InterfereProvider>
CategoryWhat it coversGateable
necessaryError tracking, logsAlways on
analyticsPage events, rage clicks, device infoYes
replaySession replayYes
Update consent at runtime through the same hook:
const { consent } = useInterfere();

consent.set({ analytics: true, replay: true }); // selective
consent.set(); // grant everything
Interfere works with any consent platform (c15t, CookieYes, OneTrust). Map its booleans to the categories above.
To apply consent before the first render, and avoid any flash where a non-consented feature loads, pass it to init({consent}) instead. The provider prop then keeps it in sync as the user changes their mind.

Report a handled error

Uncaught errors are already covered. For one you catch and handle yourself but still want on the record, call capture:
import { capture } from "@interfere/vite/init";

try {
  await saveDraft();
} catch (error) {
  capture(error);
}

Server-side rendering

If you server-render with TanStack Start (or another Nitro-based setup), do the Quick start steps, then add the pieces below. Two things change: your server errors get captured too, and browser telemetry routes through your own domain so ad-blockers and CORS don’t drop it. The plugin detects an SSR framework and switches to this proxy mode for you.
1

Add the telemetry route

A catch-all route proxies browser telemetry through your origin and boots server instrumentation. For TanStack Start:
src/routes/api/interfere/$.tsx
import { handle } from "@interfere/vite/handler";
import { register } from "@interfere/vite/server";
import { createFileRoute } from "@tanstack/react-router";

if (typeof window === "undefined") {
  register();
}

export const Route = createFileRoute("/api/interfere/$")({
  server: {
    handlers: {
      GET: ({ request }) => handle(request),
      POST: ({ request }) => handle(request),
      OPTIONS: ({ request }) => handle(request),
    },
  },
});
2

Start the SDK in your client entry

Call init() before hydration, in your client entry rather than main.tsx.
src/client.tsx
import { init } from "@interfere/vite/init";

init();
// ...hydrateRoot(...)
3

Wrap the root route with the provider

src/routes/__root.tsx
import { InterfereProvider } from "@interfere/vite/provider";

function RootComponent() {
  return (
    <InterfereProvider>
      <Outlet />
    </InterfereProvider>
  );
}
Report handled errors from the server (route handlers, server functions) with captureError:
import { captureError } from "@interfere/vite/server";

try {
  await chargeCard();
} catch (error) {
  captureError(error);
  throw error;
}

Other frameworks

https://mintcdn.com/interfere/MgNg5VNkabABMNbR/icons/tech/nextjs/currentcolor.svg?fit=max&auto=format&n=MgNg5VNkabABMNbR&q=85&s=78138c30ab456b35f56f5dbb78504079

Next.js

@interfere/next: a build plugin, the ingest route, and a provider for the App Router.
https://mintcdn.com/interfere/-ZXFpoZpeu8oxyWI/icons/tech/nestjs/transparent.svg?fit=max&auto=format&n=-ZXFpoZpeu8oxyWI&q=85&s=24095f2ad2719279173814d170a854b0

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 }).
A pure single-page app sends telemetry straight to Interfere, which some ad-blockers filter. If that’s a concern, run it behind a small server and add the proxy route from Server-side rendering; requests then go through your own domain.
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 plugins, for example init({ plugins: { replay: false, pageEvents: false } }). To drop browser tracing from the bundle entirely, pass init({ tracing: false }).