Skip to content

Commit f5a2219

Browse files
authored
fix(nextjs): Normalize trailing slashes in App Router route parameterization (#19365)
When `trailingSlash: true` is set in `next.config.js`, Next.js appends a trailing / to all URLs (e.g., `/about` becomes `/about/`). This caused `pageload` transactions to be incorrectly named `/:slug*` (the catch-all route) for nearly all routes. **Something to consider:** This does not preserve the actual route visited, it normalizes by trimming the slashes for all routes, so if the user visits `/about/` it will be reported as `/about` in traces. I noticed we have precedent for that with react-router so I followed that. Closes #19241
1 parent f824cce commit f5a2219

20 files changed

Lines changed: 509 additions & 12 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@sentry:registry=http://127.0.0.1:4873
2+
@sentry-internal:registry=http://127.0.0.1:4873
3+
public-hoist-pattern[]=*import-in-the-middle*
4+
public-hoist-pattern[]=*require-in-the-middle*
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function CatchAllPage() {
2+
return <div>Catch-all page</div>;
3+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function Layout({ children }: { children: React.ReactNode }) {
2+
return (
3+
<html lang="en">
4+
<body>{children}</body>
5+
</html>
6+
);
7+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import Link from 'next/link';
2+
3+
export default function Page() {
4+
return (
5+
<div>
6+
<h1>Next 16 trailing slash test app</h1>
7+
<ul>
8+
<li>
9+
<Link href="/static-page">Static Page</Link>
10+
</li>
11+
<li>
12+
<Link href="/parameterized/foo">Parameterized</Link>
13+
</li>
14+
<li>
15+
<Link href="/parameterized/static">Parameterized Static</Link>
16+
</li>
17+
</ul>
18+
</div>
19+
);
20+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function ParameterizedPage() {
2+
return <div>Dynamic parameterized page</div>;
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function ParameterizedStaticPage() {
2+
return <div>Parameterized static page</div>;
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function StaticPage() {
2+
return <div>Static page</div>;
3+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import * as Sentry from '@sentry/nextjs';
2+
3+
Sentry.init({
4+
environment: 'qa', // dynamic sampling bias to keep transactions
5+
dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN,
6+
tunnel: `http://localhost:3031/`, // proxy server
7+
tracesSampleRate: 1.0,
8+
sendDefaultPii: true,
9+
});
10+
11+
export const onRouterTransitionStart = Sentry.captureRouterTransitionStart;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as Sentry from '@sentry/nextjs';
2+
3+
export async function register() {
4+
if (process.env.NEXT_RUNTIME === 'nodejs') {
5+
await import('./sentry.server.config');
6+
}
7+
8+
if (process.env.NEXT_RUNTIME === 'edge') {
9+
await import('./sentry.edge.config');
10+
}
11+
}
12+
13+
export const onRequestError = Sentry.captureRequestError;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { withSentryConfig } from '@sentry/nextjs';
2+
import type { NextConfig } from 'next';
3+
4+
const nextConfig: NextConfig = {
5+
trailingSlash: true,
6+
};
7+
8+
export default withSentryConfig(nextConfig, {
9+
silent: true,
10+
});

0 commit comments

Comments
 (0)