Wire GlitchReplay into your app in ten minutes
The canonical recipe for instrumenting a Next.js app: install @sentry/react + @glitchreplay/a11y + @glitchreplay/network-probe, drop in instrumentation-client.ts, mount the error boundary, ship.
You created a project. The dashboard is showing a DSN. The next ten minutes are the difference between "I'll integrate this later" and "errors are flowing." This is the canonical recipe — the same one we run on inventivehq.com and the dozen other sites we instrument ourselves — written so you can copy-paste it into a Next.js 15 App Router project and have a working pipeline before your coffee finishes brewing.
If you're on Next.js 14, a Vite SPA, or plain JS, the same pieces apply with small adjustments — those variants are at the bottom.
The five things you'll wire up
GlitchReplay's ingest is wire-compatible with the Sentry SDK envelope protocol. That means the "SDK" is just @sentry/react pointed at a different host. We ship two helper packages on npm — @glitchreplay/a11y for axe-core accessibility scanning and @glitchreplay/network-probe for ad-blocker-aware resource error capture — and a recommended root-layout error boundary. Five files touched, no Webpack plugin, no build wrapper.
The DSN format you'll see in the dashboard
On the Setup tab of your project you'll see something like:
https://68d35775144d81bbfd18a2295510bbf1@glitchreplay.com/0The path is /0, not your project ID. That trips up everyone the first time. Sentry SDK 10's validateDsn requires the path component to be all digits — anything else is rejected at the SDK boundary, silently disabling envelope POSTs (you'll see a "Transport disabled" warning in the console if you guess wrong). The envelope endpoint authenticates by the public key in the URL, so the path is informational; /0 is the conventional self-hosted placeholder.
The public key (the long hex string before the @) is safe to ship in your client bundle. Same trust model as Sentry: it authenticates which project an event belongs to, not who you are. It is not a secret.
Step 1 — Install the packages
npm install --save \
@sentry/react \
@glitchreplay/a11y \
@glitchreplay/network-probe \
axe-coreThree things to know about this list:
Use @sentry/react, not @sentry/nextjs or @sentry/browser. The Next.js wrapper ships a Webpack plugin and a Vercel build adapter that fight with @cloudflare/next-on-pages and @opennextjs/cloudflare. Most teams running Next on Cloudflare end up disabling half the wrapper anyway. @sentry/react is a strict superset of @sentry/browser — same envelope protocol, plus Sentry.ErrorBoundary for React component-stack capture on render errors.
@glitchreplay/a11y is optional but cheap. It loads axe-core into the browser and reports WCAG violations through the same SDK pipeline. Default config runs in development and staging only; opt into production scanning with productionSampleRate: 0.05 if you want RUM-style accessibility coverage of real user paths.
@glitchreplay/network-probe handles a problem you'll hit immediately if you ship images or scripts from a CDN like imagedelivery.net: ad-blockers and privacy extensions block them at the network layer, and the SDK reports those as resource errors. The probe verifies whether the resource is actually reachable from the user's context before letting the error through, so genuine CDN outages still surface but the ad-blocker noise gets dropped.
Step 2 — Drop in instrumentation-client.ts
Next 15.3+ runs instrumentation-client.ts at the project root once on the client at startup, before any user code. This is where the SDK init lives. Create the file with:
/**
* Next.js client-side instrumentation. Runs once at startup before any
* user code. Wires Sentry SDK -> GlitchReplay ingest, plus axe-core
* accessibility scanning and ad-blocker-aware resource error capture.
*/
import * as Sentry from "@sentry/react";
import { axeIntegration } from "@glitchreplay/a11y";
import { networkProbeIntegration } from "@glitchreplay/network-probe";
const PROJECT_ID = "prj_..."; // visible in your dashboard URL
const PUBLIC_KEY = "..."; // the hex string from the DSN
const DSN = `https://${PUBLIC_KEY}@glitchreplay.com/0`;
Sentry.init({
dsn: DSN,
release:
process.env.NEXT_PUBLIC_RELEASE ||
process.env.NEXT_PUBLIC_COMMIT_SHA ||
"dev",
// Always-on session replay. The recorder starts synchronously at
// SDK init so first-frame errors (React hydration #418, etc.) get a
// viewable replay. Buffer mode (replaysSessionSampleRate: 0) silently
// drops these because rrweb's full snapshot is queued asynchronously.
tracesSampleRate: 0,
replaysSessionSampleRate: 1.0,
replaysOnErrorSampleRate: 1.0,
integrations: [
Sentry.replayIntegration({
maskAllText: true,
maskAllInputs: true,
blockAllMedia: true,
}),
axeIntegration({
runOn: ["development", "staging", "production"],
productionSampleRate: 0.05,
wcagLevel: "AA",
minImpact: "moderate",
}),
networkProbeIntegration({
verifyResourceErrors: [/imagedelivery\.net/i],
}),
],
});That's the minimum viable init. The full canonical version (which you'll want eventually) adds a denyUrls list for tracker noise (Zaraz, Cloudflare Insights, fbevents, googletag, hubspot, etc.) and a beforeSend filter that drops stack-less rejections from embedded chat widgets and browser quirks. We publish it at /docs/getting-started if you want to skip the iteration.
About the masking defaults
The replay integration ships with maskAllText: true, maskAllInputs: true, blockAllMedia: true. Stricter than Sentry's defaults, deliberately. We'd rather you start safe and unmask specific elements as you need them than have a customer email show up in a session you forwarded to a contractor. Add data-glitch-replay-unmask on elements you want preserved in the replay.
Step 3 — Wrap the layout in an error boundary
The SDK's window.onerror path catches uncaught exceptions, but it loses the React component stack. Sentry.ErrorBoundary fills that gap for errors thrown during render. Drop a small wrapper into components/SentryErrorBoundary.tsx:
"use client";
import * as Sentry from "@sentry/react";
import type { ReactNode } from "react";
function Fallback() {
return (
<div style={{ padding: "2rem", textAlign: "center" }}>
<h1>Something went wrong</h1>
<p>The error has been reported. Please reload.</p>
<button onClick={() => window.location.reload()}>Reload</button>
</div>
);
}
export function SentryErrorBoundary({ children }: { children: ReactNode }) {
return <Sentry.ErrorBoundary fallback={Fallback}>{children}</Sentry.ErrorBoundary>;
}Then mount it in app/layout.tsx wrapping your tree:
import { SentryErrorBoundary } from "@/components/SentryErrorBoundary";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<SentryErrorBoundary>{children}</SentryErrorBoundary>
</body>
</html>
);
}Step 4 — Trigger a test event
Run your dev server. Open DevTools, paste this into the console:
throw new Error("glitchreplay wiring test");Within a few seconds the event should appear on your project's Issues page in the dashboard, grouped by fingerprint. If you don't see it:
- No event at all — check the browser console for "Transport disabled" (your DSN path isn't
/0) or for the network tab showing a 4xx fromglitchreplay.com/api/.../envelope/. - Event lands but breadcrumbs are empty — Next.js isn't including
instrumentation-client.tsin the client bundle. This file is only recognised on Next 15.3+. On older versions, see the Next 14 variant below. - Event lands but the stack is minified — you need source maps. That's the next section.
Once you've confirmed the wiring, delete the test issue from its detail page so it doesn't pollute your real-issue counts.
Step 5 — Source maps (optional but recommended)
Without source maps, every stack frame shows the minified bundle (e.g. lt at index-DLrL68bZ.js:1:10383). To deminify, your build needs to upload .map files to GlitchReplay tagged with the same release string you set in Sentry.init. The full recipe is at /docs/source-maps — typically a small scripts/upload-sourcemaps.mjs hooked into your postbuild script. We deliberately don't recommend Sentry's Webpack plugin auto-uploader because it conflicts with @cloudflare/next-on-pages.
Variations for non-canonical stacks
Next.js 14 (no instrumentation-client.ts)
Next 14 doesn't support instrumentation-client.ts — that hook landed in 15.3. Move the same Sentry.init body into a small client component called from useEffect:
// app/components/SentryInit.tsx
"use client";
import { useEffect } from "react";
import * as Sentry from "@sentry/react";
import { axeIntegration } from "@glitchreplay/a11y";
import { networkProbeIntegration } from "@glitchreplay/network-probe";
export default function SentryInit() {
useEffect(() => {
Sentry.init({ /* same body as instrumentation-client.ts */ });
}, []);
return null;
}Mount <SentryInit /> in the root layout. The trade-off: the effect runs after the initial React render, so errors thrown during that render aren't captured. Acceptable for marketing-heavy apps; upgrade to Next 15.3+ for the cleaner pattern.
Vite SPA
Call Sentry.init at the top of src/main.tsx before ReactDOM.createRoot().render(). Same body as the Next 15 recipe, no instrumentation-client.ts needed — Vite handles the import order naturally.
Plain JavaScript (no TypeScript)
The integration packages are TS-typed but the runtime works in JS. Use .js file extensions, drop the Array<string | RegExp> annotations, and skip the import type lines. Functionally identical.
Astro / Cloudflare Pages static export / legacy static HTML
For Astro, drop a <script> tag in the root layout pointing at a small bundle that calls Sentry.init. For static HTML sites without a build pipeline, load @sentry/browser from a CDN and inline the init in a <script>. Both paths give up the dashboard-config side-channel and the React error boundary, but the core ingest still works.
What you get once events are flowing
The minute a real error fires in your app:
- Issue detail page with a breadcrumb timeline (clicks, navigation, fetch, console) — the same fields Sentry exposes, rendered as a scrubbable timeline rather than a raw list.
- Session replay rendered with rrweb-player, scoped to the window around the error. Always-on means even hydration errors fired before the first React paint produce a viewable replay.
- Web Vitals (LCP / CLS / INP) attached to each issue, so you can tell whether a user error correlates with a slow page.
- Accessibility violations (if you opted into
productionSampleRate) under the same dashboard, fingerprinted by rule × selector so you don't see ten thousand "color-contrast" entries.
If you're migrating from a Sentry account, see our migration guide for the DSN swap, alert-rule mapping, and 14-day overlap pattern. If you're wiring up source maps, the deminification post walks through the upload pipeline. If you want to know what happens once errors arrive — fingerprint dedup, replay linkage, the prune model — start with always-on replay with 3-day prune.
Welcome to the dashboard.
GlitchReplay is Sentry-SDK compatible, includes session replay and security signals, and never charges per event. Free to start, five minutes to first event.