Repository navigation
@sentry/nextjs bundles the whole Pages Router runtime (next/router, ~87 KB raw / 36 KB gzip) into every App Router app #24032
Description
Is there an existing issue for this?
- I have checked for existing issues https://github.com/getsentry/sentry-javascript/issues
- I have reviewed the documentation https://docs.sentry.io/
- I am using the latest SDK release https://github.com/getsentry/sentry-javascript/releases
How do you use Sentry?
Sentry Saas (sentry.io)
Which SDK are you using?
@sentry/nextjs
SDK Version
10.72.0 (measured); the import chain is unchanged in 10.73.0 and on develop
Framework Version
Next 16.3.3, App Router only (no pages/ directory), Turbopack
Link to Sentry event
No response
Reproduction Example/SDK Setup
// instrumentation-client.ts
import * as Sentry from '@sentry/nextjs';
Sentry.init({ dsn: process.env.NEXT_PUBLIC_SENTRY_DSN });
export const onRouterTransitionStart = Sentry.captureRouterTransitionStart;Nothing else is needed: any App Router app that calls init() reproduces it. The app has no pages/ directory and no import of next/router anywhere.
Steps to Reproduce
npx create-next-app@latest(App Router), add@sentry/nextjsand theinstrumentation-client.tsabove.next build --experimental-analyze(or any other bundle analysis).- Open the chunk that contains the SDK client (the one with the
[@sentry/nextjs]init warnings).
Expected Result
An App Router app downloads no Pages Router code. The SDK already decides at runtime which router it is on (routing/nextRoutingInstrumentation.ts: !WINDOW.document.getElementById('__NEXT_DATA__')), so the Pages Router branch is never taken on App Router.
Actual Result
The SDK client chunk carries the whole Pages Router client runtime: next/dist/shared/lib/router/router.js, next/dist/compiled/path-to-regexp, next/dist/client/route-loader.js, next/dist/client/script.js, sorted-routes.js, route-regex.js, prepare-destination.js, ... 52 modules from next, 87.2 KB raw / 35.6 KB gzip in our app (Turbopack analyzer, Next 16.3.3).
Sentry client chunk (App Router, __SENTRY_TRACING__ compiled to false) |
raw | of which next (Pages Router runtime) |
|---|---|---|
@sentry/nextjs 10.72.0 as published |
168.4 KB | 87.2 KB (52 modules) |
with next/router imported on demand (see below) |
82 KB | 0 - the runtime lands in an async chunk that an App Router app never requests |
It is there with tracing enabled and after compiling tracing out: the import is static, so no bundler can drop it.
Root cause - packages/nextjs/src/client/routing/pagesRouterRoutingInstrumentation.ts:
import RouterImport from 'next/router';
const Router: typeof RouterImport = RouterImport.events
? RouterImport
: (RouterImport as unknown as { default: typeof RouterImport }).default;reached statically through client/index.ts -> browserTracingIntegration.ts -> routing/nextRoutingInstrumentation.ts -> routing/pagesRouterRoutingInstrumentation.ts. Router is used in exactly one place, pagesRouterInstrumentNavigation (Router.events.on('routeChangeStart', ...)); the pageload half only reads __NEXT_DATA__ and __BUILD_MANIFEST. next declares no sideEffects, and the app/pages decision is made at runtime, so the module and everything next/router pulls in stay in every client bundle.
Why the SDK's own size gate cannot see it: .size-limit.js measures @sentry/nextjs (client) with ignore: ['next/router', 'next/constants'].
Additional Context
What I measured before proposing a fix (same app, same analyzer, patching the installed build):
- Moving the module-scope interop (
RouterImport.events ? ...) into a function changes nothing: the cost is the static import at module-graph level, not the getter access. - Replacing the static import with
import('next/router')insidepagesRouterInstrumentNavigationmoves the Pages Router runtime into an async chunk: Sentry chunk 168.4 -> 82 KB raw on our App Router app. On a Pages Router app therouteChangeStartlistener is registered once that import settles; the router module is already part of the framework runtime there. A minimal Pages Router app built with both Turbopack and webpack, pageload and mount-timerouter.replace()navigation spans included, is in the PR that follows. window.next.router.events(nonext/routerat all) was considered and rejected: Next marksinstance.eventsas "never documented ... remove the following major version".
Priority
React with 👍 to help prioritize this issue.
Metadata
Metadata
Assignees
Projects
- StatusShow more project fieldsNo status
Activity
linear-code commentedon Sep 3, 2026
JS-3570
chargome commentedon Sep 4, 2026
@AlbertoMihai98 thanks for flagging I'll take a look!
fix(nextjs): Keep the Pages Router runtime out of App Router client b…
fix(nextjs): Keep the Pages Router runtime out of App Router client b…