Skip to content

Commit 808d2ba

Browse files
authored
error when output: export is used with intercepting routes (vercel#75058)
Intercepting routes are built on top of [rewrites](https://nextjs.org/docs/app/api-reference/config/next-config-js/rewrites) which is one of the listed [unsupported features](https://nextjs.org/docs/app/building-your-application/deploying/static-exports#unsupported-features) of `output: "export"`. However, because the Next.js server injects the interception routes, it's not caught by existing validation logic in the export flow. This properly documents that intercepting routes are not currently supported by `output: "export"` and hard errors in next dev/build if detected. Previously route interception would just not have worked, instead serving the non-intercepted page when built, leading to a confusing experience. We eventually want to support this with improved SPA/export features, however that's out of scope for this PR: the goal here is to ensure we're providing more immediate feedback about the fact that this is unsupported.
1 parent 6fcd42c commit 808d2ba

File tree

13 files changed

+101
-2
lines changed

13 files changed

+101
-2
lines changed

docs/01-app/03-building-your-application/10-deploying/02-static-exports.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ Features that require a Node.js server, or dynamic logic that cannot be computed
288288
- [Image Optimization](/docs/app/building-your-application/optimizing/images) with the default `loader`
289289
- [Draft Mode](/docs/app/building-your-application/configuring/draft-mode)
290290
- [Server Actions](/docs/app/building-your-application/data-fetching/server-actions-and-mutations)
291+
- [Intercepting Routes](/docs/app/building-your-application/routing/intercepting-routes)
291292

292293
Attempting to use any of these features with `next dev` will result in an error, similar to setting the [`dynamic`](/docs/app/api-reference/file-conventions/route-segment-config#dynamic) option to `error` in the root layout.
293294

packages/next/errors.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,5 +622,7 @@
622622
"621": "Required root params (%s) were not provided in generateStaticParams for %s, please provide at least one value for each.",
623623
"622": "A required root parameter (%s) was not provided in generateStaticParams for %s, please provide at least one value.",
624624
"623": "Invalid quality prop (%s) on \\`next/image\\` does not match \\`images.qualities\\` configured in your \\`next.config.js\\`\\nSee more info: https://nextjs.org/docs/messages/next-image-unconfigured-qualities",
625-
"624": "Internal Next.js Error: createMutableActionQueue was called more than once"
625+
"624": "Internal Next.js Error: createMutableActionQueue was called more than once",
626+
"625": "Server Actions are not supported with static export.\\nRead more: https://nextjs.org/docs/app/building-your-application/deploying/static-exports#unsupported-features",
627+
"626": "Intercepting routes are not supported with static export.\\nRead more: https://nextjs.org/docs/app/building-your-application/deploying/static-exports#unsupported-features"
626628
}

packages/next/src/export/index.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
SERVER_DIRECTORY,
3333
SERVER_REFERENCE_MANIFEST,
3434
APP_PATH_ROUTES_MANIFEST,
35+
ROUTES_MANIFEST,
3536
} from '../shared/lib/constants'
3637
import loadConfig from '../server/config'
3738
import type { ExportPathMap } from '../server/config-shared'
@@ -52,6 +53,7 @@ import { formatManifest } from '../build/manifests/formatter/format-manifest'
5253
import { TurborepoAccessTraceResult } from '../build/turborepo-access-trace'
5354
import { createProgress } from '../build/progress'
5455
import type { DeepReadonly } from '../shared/lib/deep-readonly'
56+
import { isInterceptionRouteRewrite } from '../lib/generate-interception-routes-rewrites'
5557

5658
export class ExportError extends Error {
5759
code = 'NEXT_EXPORT_ERROR'
@@ -302,13 +304,29 @@ async function exportAppImpl(
302304
serverActionsManifest = require(
303305
join(distDir, SERVER_DIRECTORY, SERVER_REFERENCE_MANIFEST + '.json')
304306
)
307+
305308
if (nextConfig.output === 'export') {
309+
const routesManifest = require(join(distDir, ROUTES_MANIFEST))
310+
311+
// We already prevent rewrites earlier in the process, however Next.js will insert rewrites
312+
// for interception routes so we need to check for that here.
313+
if (routesManifest?.rewrites?.beforeFiles?.length > 0) {
314+
const hasInterceptionRouteRewrite =
315+
routesManifest.rewrites.beforeFiles.some(isInterceptionRouteRewrite)
316+
317+
if (hasInterceptionRouteRewrite) {
318+
throw new ExportError(
319+
`Intercepting routes are not supported with static export.\nRead more: https://nextjs.org/docs/app/building-your-application/deploying/static-exports#unsupported-features`
320+
)
321+
}
322+
}
323+
306324
if (
307325
Object.keys(serverActionsManifest.node).length > 0 ||
308326
Object.keys(serverActionsManifest.edge).length > 0
309327
) {
310328
throw new ExportError(
311-
`Server Actions are not supported with static export.`
329+
`Server Actions are not supported with static export.\nRead more: https://nextjs.org/docs/app/building-your-application/deploying/static-exports#unsupported-features`
312330
)
313331
}
314332
}

packages/next/src/server/dev/next-dev-server.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,14 @@ export default class DevServer extends Server {
604604
this.nextConfig.basePath
605605
).map((route) => new RegExp(buildCustomRoute('rewrite', route).regex))
606606

607+
if (this.nextConfig.output === 'export' && rewrites.length > 0) {
608+
Log.error(
609+
'Intercepting routes are not supported with static export.\nRead more: https://nextjs.org/docs/app/building-your-application/deploying/static-exports#unsupported-features'
610+
)
611+
612+
process.exit(1)
613+
}
614+
607615
return rewrites ?? []
608616
}
609617

test/e2e/app-dir/actions/app-action-export.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ describe('app-dir action handling - next export', () => {
2727
}
2828
`
2929
)
30+
// interception routes are also not supported with export
31+
await next.remove('app/interception-routes')
3032
try {
3133
await next.start()
3234
} catch {}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Page() {
2+
return 'intercepted'
3+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default () => null
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Default() {
2+
return null
3+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export default function Layout(props: {
2+
children: React.ReactNode
3+
modal: React.ReactNode
4+
}) {
5+
return (
6+
<html>
7+
<body>
8+
<div id="children">{props.children}</div>
9+
<div id="modal">{props.modal}</div>
10+
</body>
11+
</html>
12+
)
13+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import Link from 'next/link'
2+
3+
export default function Page() {
4+
return (
5+
<div>
6+
<Link href="/page">To /page</Link>
7+
</div>
8+
)
9+
}

0 commit comments

Comments
 (0)