Next.js Sentry Integration: Error Tracking, Source Maps, and Server Component Capture (2026)
Adding Sentry to a Next.js app takes one command. Here is how to capture Server Component errors, upload source maps with Turbopack, and bypass ad blockers in Next.js 16.
Adding Sentry to a Next.js app takes one command, npx @sentry/wizard@latest -i nextjs, which installs the SDK, generates client/server/edge config files, wires the instrumentation.ts hook, and uploads source maps on build. Once installed, Sentry automatically captures unhandled errors in Server Components, Server Actions, Route Handlers, and the browser, plus distributed traces across the edge and Node runtimes. The catch in 2026? Turbopack source maps and the onRequestError hook. Both have changed since the older pages-router setups you'll find on Stack Overflow.
The Sentry wizard scaffolds sentry.client.config.ts, sentry.server.config.ts, sentry.edge.config.ts, and an instrumentation.ts with the new onRequestError hook, which is required for Server Component error capture since Next.js 15.
Source-map upload now works with Turbopack as of @sentry/nextjs 8.x, but you must set SENTRY_AUTH_TOKEN at build time, not at runtime.
Server Actions errors don't bubble to error.tsx automatically. You wrap them with Sentry.withServerActionInstrumentation or rely on the global onRequestError hook.
The tunnelRoute option proxies Sentry traffic through your own domain to bypass ad blockers that drop direct requests to sentry.io.
Sentry's free tier covers 5,000 errors, 10,000 performance units, and 50 session replays per month. That's enough for a small SaaS but easy to blow through with traces enabled.
Edge runtime routes need the edge config explicitly; Node-runtime Sentry.init doesn't apply there.
Install Sentry in a Next.js 16 app
Run the wizard from your project root. It detects Next.js, asks for a Sentry account (or creates one), and writes the configuration files for you:
npx @sentry/wizard@latest -i nextjs
Honestly, I've run this on every Next.js project I've shipped since 2021 and the wizard has steadily gotten better. Back in the older days you had to hand-edit next.config.js with withSentryConfig yourself. Today it does that for you, plus it adds a /sentry-example-page route you can hit to verify the SDK is reporting. Don't skip that step. I've seen people swear Sentry "isn't working" only to discover their Vercel project didn't have the auth token set, so source maps were uploaded but the SDK was running unminified in dev only.
If you prefer manual installation, add the package and create the config files yourself:
npm install @sentry/nextjs
Then create three configs and an instrumentation file. The wizard does this in seconds, so unless you have an unusual monorepo layout, just let it run. After the wizard finishes, commit the changes, set SENTRY_AUTH_TOKEN as a Vercel environment variable (or in your CI), and trigger a build. The first deploy uploads source maps; from the second one onwards you'll see properly de-minified stack traces in the Sentry UI.
What each config file actually does
The wizard creates four files, and confusing them is the most common reason errors don't show up. Here's what each one is for:
Route handlers, Server Components, Server Actions, server-side rendering
sentry.edge.config.ts
Edge runtime
Middleware, edge route handlers, edge Server Components
instrumentation.ts
Process startup + every request
Loads the right config per runtime, exports onRequestError for Server Component capture
The pages-router habit I had to unlearn: sentry.server.config.ts alone is not enough in the App Router. Server Components run inside React's render pipeline, and their errors propagate differently. Without instrumentation.ts exporting onRequestError, you'll miss them entirely. The wizard wires this for you in new projects, but if you're migrating from @sentry/nextjs 7.x, double-check that instrumentation.ts exists and exports both register and onRequestError. See the Next.js instrumentation reference for the full hook contract.
Does Sentry work with Server Components?
Yes. With the onRequestError hook in instrumentation.ts, Sentry captures errors thrown from any Server Component, including async ones. Here's the exact pattern Next.js 15 and 16 expect:
// instrumentation.ts
import * as Sentry from '@sentry/nextjs';
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
await import('./sentry.server.config');
}
if (process.env.NEXT_RUNTIME === 'edge') {
await import('./sentry.edge.config');
}
}
export const onRequestError = Sentry.captureRequestError;
The onRequestError export is what closes the gap. Server Components render inside React's pipeline, so a thrown error gets caught by the nearest error.tsx boundary before any try/catch you might add upstream. Next.js calls onRequestError as the error bubbles, giving Sentry a chance to capture it with the request context (URL, headers, path) attached. If you're not seeing Server Component errors in Sentry, this missing export is the cause 95% of the time.
For more on how error boundaries themselves work in the App Router, see my guide to Next.js App Router error handling. Sentry sits one layer below those boundaries; it captures the error before error.tsx renders the fallback UI.
Capturing errors in Server Actions
Server Actions are sneaky. They run on the server but are invoked via a POST request the client kicks off, so an error in an action returns to the client as a generic Action failed message. Without instrumentation, you'll see nothing in Sentry. Wrap each action with withServerActionInstrumentation:
// app/actions.ts
'use server';
import * as Sentry from '@sentry/nextjs';
import { headers } from 'next/headers';
import { revalidatePath } from 'next/cache';
export async function deletePost(id: string) {
return Sentry.withServerActionInstrumentation(
'deletePost',
{
// forward headers so Sentry can correlate the action with the request trace
headers: await headers(),
},
async () => {
const post = await db.post.findUnique({ where: { id } });
if (!post) throw new Error(`Post ${id} not found`);
await db.post.delete({ where: { id } });
revalidatePath('/posts');
return { ok: true };
},
);
}
The wrapper does three things: it names the span (so the action shows up in performance traces with a useful label), it links the action's span to the parent request trace, and (critically) it captures any thrown error to Sentry before re-throwing. If you're already using onRequestError, errors will still be captured via that path, but the wrapper gives you a named span and matching breadcrumbs that make debugging a lot faster. Pair it with proper rate limiting (see my Next.js rate limiting guide) so a single buggy action doesn't fill your Sentry quota in an hour.
Upload source maps with Turbopack
Source maps de-minify the stack traces you see in Sentry. Without them, every frame reads like main-app-abc123.js:1:42891. The @sentry/nextjs webpack plugin used to handle this transparently. With Turbopack, which became the default bundler in Next.js 16, the integration moved into the SDK itself. The good news: as of @sentry/nextjs 8.43 (November 2025), Turbopack source-map upload works out of the box. The setup:
// next.config.ts
import { withSentryConfig } from '@sentry/nextjs';
const nextConfig = {
// your usual config
};
export default withSentryConfig(nextConfig, {
org: 'your-org',
project: 'your-project',
silent: !process.env.CI,
// hide source maps from the public bundle but upload to Sentry
sourcemaps: { deleteSourcemapsAfterUpload: true },
// ad-blocker bypass, see next section
tunnelRoute: '/monitoring',
});
Two gotchas I've hit shipping this:
SENTRY_AUTH_TOKEN must be set at build time, not runtime. On Vercel, add it as a "Production" environment variable scoped to the build step. The token never ships to the client.
deleteSourcemapsAfterUpload stops the public asset leak. Without it, your minified bundles ship with .map files anyone can download, and anyone can then read your source. Sentry's docs default to true now, but older configs may not have this set.
uBlock Origin and similar extensions block requests to *.sentry.io by default. So a meaningful slice of your visitors (developers, privacy-conscious users) will never report errors no matter how broken your app is. The tunnelRoute option proxies Sentry traffic through your own domain:
// in withSentryConfig options
tunnelRoute: '/monitoring',
With this set, the SDK ships errors to https://yourdomain.com/monitoring instead of https://o12345.ingest.sentry.io. Next.js generates a route handler at that path that forwards the body to Sentry. The performance cost is negligible (one extra hop, same region), and you reclaim accurate error counts. I turn this on for every production app. The bump in reported errors is real, usually 10-15% in my experience.
Performance monitoring and tracing
Sentry's tracing pairs each page navigation, route handler, and Server Action into a distributed trace, showing waterfall latency across the browser, edge, and Node runtimes. Enable it by setting tracesSampleRate in all three config files:
// sentry.server.config.ts
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
// Sample 10% of transactions in production; 100% in development
tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,
// Auto-instrument fetch, fs, postgres, redis, etc.
integrations: [Sentry.httpIntegration(), Sentry.prismaIntegration()],
});
Why not sample at 100% in production? Each transaction costs against your Sentry quota. The free tier includes 10,000 transactions per month, which sounds like a lot until you do the math: 10k transactions ÷ 30 days ÷ 1440 minutes ≈ 0.23 transactions per minute. Any real production traffic eats through that in a day. A 10% sample rate gives you statistically meaningful data without blowing the budget. Sentry's sampling guide covers dynamic sampling (e.g., 100% for slow requests, 1% for healthy ones), which is what you want at scale.
Session replay setup
Session replay is Sentry's screen-recording feature: when an error fires, you get a video-like reconstruction of what the user did in the last 30-60 seconds. Add it to sentry.client.config.ts:
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
integrations: [
Sentry.replayIntegration({
maskAllText: true,
blockAllMedia: true,
}),
],
replaysSessionSampleRate: 0.1, // record 10% of all sessions
replaysOnErrorSampleRate: 1.0, // record 100% of sessions where an error fires
});
maskAllText: true is essential for compliance. By default, replays record actual DOM text, which means form inputs, PII, and credentials end up on Sentry's servers. With masking on, every text node renders as asterisks in the replay, and you opt specific elements back in with the data-sentry-unmask attribute. I default to masking everything and unmasking only public content like marketing copy or status badges.
Edge runtime support and limits
Middleware and edge route handlers run in a stripped-down V8 isolate without Node APIs. Sentry's edge SDK works there, but with caveats:
No file system, no fs-based source-map resolution. Sentry uses inline source maps embedded in the bundle for edge runtime.
No long-running spans. Edge handlers must respond fast, so the SDK flushes events on response, not on a timer.
Some integrations don't load.prismaIntegration, httpIntegration's file-tracking features, and anything that touches require() at runtime all skip silently.
For a deeper dive on why edge handlers are different, see Next.js Edge Runtime vs Node Runtime. Practically: keep sentry.edge.config.ts minimal (DSN plus maybe one integration), and don't expect parity with the Node SDK.
Sentry pricing for Next.js apps
Sentry's free "Developer" plan in 2026 includes 5,000 errors, 10,000 performance units, 50 replays, and 1 GB of attachments per month. That's enough for a side project or a low-traffic SaaS but easy to outgrow. The next tier ("Team") starts at $26/month and adds higher quotas plus team features. The two tactics I use to stay within limits:
Filter noise aggressively. Set ignoreErrors in client config to drop ResizeObserver loop limit exceeded, browser extension errors, and any third-party script failures you can't fix.
Sample transactions dynamically. Use tracesSampler to send 100% of error-causing transactions but only 1% of healthy ones. This catches the slow requests without inflating volume.
If you blow past the quota, Sentry drops events server-side rather than charging you overage automatically. You just stop seeing data until the next billing cycle. Set up quota alerts in the Sentry UI so you find out before that happens. Combined with the Sentry JavaScript release notes for breaking-change tracking, that's the operating loop: install, sample, filter, alert.
Frequently Asked Questions
How do I add Sentry to an existing Next.js 16 project?
Run npx @sentry/wizard@latest -i nextjs from your project root. The wizard installs @sentry/nextjs, creates the four config files, wraps your next.config.ts with withSentryConfig, and stores your SENTRY_AUTH_TOKEN. Commit the changes, redeploy, and visit /sentry-example-page to verify it's reporting.
Why aren't my Server Component errors showing up in Sentry?
You're almost certainly missing the onRequestError export in instrumentation.ts. Server Components throw inside React's render pipeline, so the standard try/catch instrumentation can't see them. Add export const onRequestError = Sentry.captureRequestError; and redeploy.
Does Sentry work with Turbopack in Next.js 16?
Yes, as of @sentry/nextjs 8.43 (November 2025). Source-map upload happens automatically when you set SENTRY_AUTH_TOKEN at build time and wrap your config with withSentryConfig. Older SDK versions had Turbopack gaps, so upgrade to 8.43 or later.
Is Sentry free for Next.js apps?
Sentry's Developer plan is free and includes 5,000 errors, 10,000 performance units, and 50 session replays per month, which is enough for a hobby project or small SaaS. Paid plans start at $26/month for higher quotas. You can stay free longer by sampling transactions (e.g., 10% in production) and filtering known-noisy errors.
How do I stop ad blockers from blocking Sentry?
Set tunnelRoute: '/monitoring' in your withSentryConfig options. The SDK then sends error reports to your own domain, which the Next.js runtime proxies to Sentry. This bypasses the *.sentry.io domain block in uBlock Origin and similar extensions, typically recovering 10-15% of dropped events.
Enable Next.js Draft Mode in the App Router with draftMode(), signed __prerender_bypass cookies, and secure preview URLs for Sanity, Contentful, and DatoCMS.