@interfere/next connects your Next.js app to Interfere. It’s a one-time install: add a few lines, ship as usual, and Interfere captures everything your app emits (errors, sessions, traces, and logs) with no sampling and nothing to tune. From there, Interfere groups related symptoms into a single problem, decides how much it matters, and investigates the cause for you.
This page covers the install and the handful of settings most teams actually touch: naming your app, identifying your users, and respecting consent.
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
- Next.js
>= 16(App Router) - React
>= 19 - Node.js
>= 20
Quick start
Five steps, each wired once. After this you don’t touch it again.1
Install the package
2
Wrap your Next.js config
This lets Interfere upload source maps at build time, so a production stack trace points back to your original code instead of minified output. It also tags each build as a release.
next.config.ts
3
Wire server instrumentation
So server-side errors get captured and OTel bootstraps for Server Components, route handlers, and server actions. Next.js calls the exported
register() on server start.instrumentation.ts
4
Boot the client SDK
Next.js auto-loads
instrumentation-client.ts in the browser on every page load. init() starts the SDK there — the provider only connects React to this running instance, so without it nothing is captured client-side. When INTERFERE_PUBLIC_KEY is set, the build fails if this file is missing.instrumentation-client.ts
5
Add the provider
Connects React to the running SDK: the
useInterfere hook, consent controls, and an error boundary that reports render errors. Wrap your app once, at the root layout.app/layout.tsx
Environment variables
Build plugin
withInterfere() is more than a config wrapper. At production build time it hooks Next.js’s runAfterProductionCompile to upload source maps, publish release metadata, and then delete the maps from the build output so your original source never ships. It also adjusts a few Next settings so symbolication and trace propagation work:
- Forces
productionBrowserSourceMaps: truewhenever an API key is present. Set it tofalseand Interfere overrides it with a warning — without maps it can’t symbolicate. - Sets
experimental.clientTraceMetadata(traceparent,baggage) so a server trace continues into the browser, andexperimental.instrumentationHookon Next.js ≤ 14, which needs the flag to loadinstrumentation.ts. - Chains your hooks. An existing
runAfterProductionCompileruns first; Interfere’s pipeline runs after it. A release-pipeline failure never fails the build — it warns and ships, and that release’s data is ingested but skipped when generating insights. - Guards your setup. The build fails fast if
INTERFERE_PUBLIC_KEYis set but noinstrumentation-client.tsexists, and warns ifinstrumentation.tsis missing. Settinginterfere.buildIdis rejected — override the commit SHA withINTERFERE_SOURCE_IDinstead.
Coexisting with other build tools
Sentry (@sentry/nextjs) and PostHog (@posthog/nextjs-config) read the same .next output and also delete maps after upload by default. When more than one tool uploads maps, the first to delete starves every later one. The rule: only the last tool to run deletes; every earlier one keeps its maps.
Make Interfere the last tool to run, so it purges while Sentry keeps its maps:
next.config.ts
sourceMaps: { purge: false } on Interfere and leave that tool’s deletion flag on. Un-symbolicated traces after a deploy mean an earlier tool deleted the maps too soon — flip the flags so the purging tool is genuinely last.
boolean
default:"true"
Passed as the first argument to
withInterfere(settings, config). Deletes source maps from the build output after upload. Set to false when another tool shares the same .next build and runs its own upload afterward.Release identity
Interfere derives each release from your commit SHA, so the client bundle and server spans resolve to the same release. It reads the SHA from your CI’s git env vars (VERCEL_GIT_COMMIT_SHA, GITHUB_SHA, …) or the local git checkout. If none is available — a Docker build with no git context, say — set INTERFERE_SOURCE_ID to the SHA on both the build and runtime environments.
Configuration
Interfere is built to run without tuning, so most apps install it and stop here. Reach for these only when you have a specific reason.Name your app
If you run more than one app against Interfere (a storefront and an admin panel, say), give each aserviceName so problems, sessions, and metrics are attributed to the right one. Pass it to init() in your instrumentation-client.ts:
instrumentation-client.ts
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 withINTERFERE_ENVIRONMENT:
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. On Vercel you usually get the right value without doing anything.
Interfere reads this at build time and bakes it into your bundle, so it does not need a
NEXT_PUBLIC_ prefix.Custom ingest domain
Interfere’s public endpoint is the default. 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:next.config.ts
apiHost (or the INTERFERE_API_URL env var) to that URL. Ingest requests never carry cookies.
Choose what’s captured
By default Interfere captures all of the signals below. Turn any off with the provider’splugins prop. For example, disable session replay if you don’t want recordings:
app/layout.tsx
object
Each signal can be toggled on or off. All default to on.
errors: uncaught exceptionslogs: console outputdevice: device and browser infopageEvents: pageviews and clicksrageClick: rage-click detectionreplay: session replay
React error boundary
The provider wraps your app in an error boundary that reports React render errors automatically. Opt out — to keep your own boundary as the only one, say — witherrorBoundary={false}.
app/layout.tsx
Identity
By default a session is anonymous. Link it to your authenticated user so a problem shows you who hit it in Users, with a name and email instead of an opaque id. Callidentity.set() from the useInterfere hook once your user loads:
string
required
Your internal, stable user ID. Use this rather than the email.
object
required
Where the identity came from.
type: one ofclerk,auth0, orcustomname: 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.Need the current session id — to attach to a support ticket, say? Read it with the
useSession hook from @interfere/next/provider.Consent
By default all features are active. To respect a cookie banner or privacy preference, passconsent to the provider. Once you do, only essential capture (error tracking and logs) plus the categories you opt into will run:
app/layout.tsx
Update consent at runtime through the same hook:
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.replay config to the provider’s plugins. Only passwords are masked by default; set maskAllInputs: true for a stricter baseline that redacts every input.
app/layout.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
Interfere captures uncaught errors for you. When you catch an error yourself but still want it reported, callcapture on the client or captureError on the server.
Custom spans
To time and trace your own work, wrap it inspan. Interfere records the duration and outcome and links it into the surrounding trace.
Middleware and server actions
Next.js does not route proxy (middleware) or Server Action errors throughonRequestError, so wrap them explicitly to capture them with full context.
Proxy (middleware)
Wrap yourproxy.ts export (Next.js’s renamed middleware) with withInterfereProxy. It captures thrown errors — tagged with the request method and path — then re-throws them unchanged.
proxy.ts
Server actions
Wrap a Server Action withwithInterfereServerAction to attribute failures to the action. It records the action’s name, an argument shape (types only, never values), and outcome on a span, then annotates any thrown error so Next’s onRequestError reports it as one event that groups with the client report — instead of forking a second, digest-less issue.
Other frameworks
Vite + React
@interfere/vite: a Vite plugin plus init() before render. Works for SPA and SSR.TanStack Start
@interfere/vite: the same plugin, with server instrumentation for SSR.NestJS
@interfere/nest: a backend module plus instrument.ts for server error capture.FAQ
Why isn't anything showing up in development?
Why isn't anything showing up in development?
The SDK stays quiet when
NODE_ENV !== "production", so local noise doesn’t reach your dashboard. To capture while testing, call init({ enabled: true }) in instrumentation-client.ts. Interfere also drops the development, local, and test environments before ingestion, so set a real environment too if you want that data to land.Do you handle the same issue across multiple apps?
Do you handle the same issue across multiple apps?
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.What about ad-blockers?
What about ad-blockers?
Set
apiHost 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 or middleware needed.Can I ship errors only, without analytics or replay?
Can I ship errors only, without analytics or replay?
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 }).