Less code, faster builds, same telemetry: Turbopack support for the Next.js SDK
Less code, faster builds, same telemetry: Turbopack support for the Next.js SDK
TL;DR - Turbopack became the default in Next.js, so we reworked our SDK to stop depending on bundlers. The result is less code, faster builds, and the same telemetry. This blog explains how we got there.
You know the feeling when you spend years building tooling that supports something and all of a sudden that something becomes deprecated and you have to rethink your full approach?
And no, this isn’t a post about Ralph Wiggum, the recursive agent practice that we all as a community decided was okay to name that way and roll with it.
This is about Next.js rolling out Turbopack, deprecating Webpack (as of Next.js v16 Turbopack is the default) and us rethinking our telemetry approach in the SDK.
What we were doing before
When you ran next build, our Webpack loader intercepted every page, API route, middleware, and server component. It would:
Parse your file to determine its type
Bundle it with a Sentry wrapper template using Rollup
Replace your original code with the instrumented version
This worked. It also required maintaining six different wrapper templates, a 360-line webpack loader, and roughly 1,667 lines of instrumentation code. Every new Next.js feature — server components, route handlers, the App Router — meant writing another template and hoping Webpack's internals hadn't changed since last time we looked.
// The old serverComponentWrapperTemplate.ts (simplified)
import * as serverComponentModule from '__SENTRY_WRAPPING_TARGET_FILE__';
import * as Sentry from '@sentry/nextjs';
const serverComponent = serverComponentModule.default;
export default new Proxy(serverComponent, {
apply: (originalFunction, thisArg, args) => {
return Sentry.wrapServerComponentWithSentry(originalFunction, {
componentRoute: '__ROUTE__',
// ... extract headers, trace context, etc.
}).apply(thisArg, args);
},
});We were building a parallel universe where every file had a Sentry-wrapped doppelgänger. It's the kind of architecture that works until it doesn't, and when it doesn't, good luck figuring out which layer broke.
What we do now
Next.js has built-in OpenTelemetry instrumentation. It emits spans for every request, middleware execution, and render operation — complete with route information. Instead of wrapping your code, we listen.
// The new approach (simplified)
client?.on('spanStart', span => {
const spanAttributes = spanToJSON(span).data;
if (spanAttributes?.[ATTR_NEXT_ROUTE]) {
const route = spanAttributes[ATTR_NEXT_ROUTE];
rootSpan.updateName(route);
rootSpan.setAttribute(ATTR_HTTP_ROUTE, route);
}
});The snippet above is the clean version. In reality, we overwrite the standard Next.js OTel config with Sentry-specific parts (span processors, context manager, etc.) and customize our HTTP integration to disable incoming Next.js-generated spans so we can enrich them ourselves. Still simpler than six Rollup templates.
The Turbopack-specific code is about 164 lines. That's a 10x reduction from the Webpack approach, and most of those lines are config handling, not instrumentation logic.
What this actually means for your builds
Charly, an engineer on our Next.js SDK team, ran a test against the Sentry Changelog repo to see the difference:
The SDK no longer runs Rollup on every file in your application during compilation. If you've ever wondered why next build was taking a while, some of that was us. (Sorry.)
The SDK automatically detects whether you're using Turbopack or Webpack and adjusts. If you're on Next.js 15.4.1 or later, Turbopack just works. The tracing data you see in Sentry looks the same — route handlers, middleware, server components, data fetching. All still there.
What changes
Some Webpack configuration options no longer apply when using Turbopack:
// These are no-ops with Turbopack
module.exports = withSentryConfig(nextConfig, {
webpack: {
autoInstrumentServerFunctions: true, // Now handled via OTel
autoInstrumentMiddleware: true, // Now handled via OTel
excludeServerRoutes: ['/api/health'], // No build-time wrapping to exclude from
},
});If you were excluding specific routes from instrumentation, you'll need to filter them via Sentry's beforeSendTransaction hook instead. The SDK relies on Next.js's OpenTelemetry instrumentation, so there's no build-time wrapping to opt out of.
Server Actions still require manual instrumentation:
'use server';
import { withServerActionInstrumentation } from '@sentry/nextjs';
export async function submitForm(data: FormData) {
return withServerActionInstrumentation('submitForm', async () => {
// Your action code
});
}Server Actions don't emit OTel spans we can hook into. We're watching Next.js development here — if they expose the telemetry, we'll add automatic instrumentation.
Why this matters beyond our codebase
Instead of every APM vendor maintaining their own build plugins, loaders, and bundler integrations, frameworks are adopting OpenTelemetry as a standard telemetry interface. Next.js emits spans. We consume them. The framework handles the "how," we handle the "where it goes."
This is where framework instrumentation is heading. We just got there by deleting code instead of writing more of it.
Requirements
Next.js 15.4.1+ for Turbopack production builds
Next.js 15.6+ for native Debug IDs (improves source map resolution)
No changes to your
instrumentation.tsorinstrumentation-client.tsfiles
Next.js 16 makes Turbopack the default bundler and the SDK is ready for it.
The 15-month journey from "Turbopack: unsupported" to "Turbopack: default" involved 19 PRs, a complete architectural rethink, and the realization that the best way to support a new bundler was to stop depending on bundlers altogether. That approach now powers our Next.js SDK.



