fix(app-router): resolve explicit parallel slot with no page (#1535)#1852
Conversation
When a parallel slot defines an explicit sub-page (e.g. `@slot/baz/page.tsx`) but no corresponding children page (`baz/page.tsx`) and no `default.tsx` for the children slot, vinext minted a synthetic `/baz` route with a null children page. That route shadowed the sibling catch-all and rendered an empty children prop, so the request hung (the upstream "explicit slot but no page" case timed out). Next.js serves such a slot-only sub-route's children from the nearest sibling catch-all. Add `findCatchAllPage` and use it as the children fallback in `discoverSlotSubRoutes` when no `default.tsx` exists, so `/baz` renders `[...catchAll]/page.tsx` for children while `@slot/baz/page.tsx` fills the slot. A children `default.tsx` still wins over the catch-all when both exist. Finishes the explicit-slot-but-no-page case of #1535 (the children-slot priority case landed in #1659). Fixes #1535.
|
/bigbonk review for issues |
commit: |
|
|
/bigbonk review for issues |
Review: fix(app-router): resolve explicit parallel slot with no page (#1535)Reviewed the change to Verified
Latent gap: catch-all children page receives empty params (not a blocker)When children fall through to
I confirmed this by inspecting the built route in a local debug run: At render time, the page component receives params verbatim from the matched route ( This is not exercised by the upstream fixture (its But a real-world catch-all children page that reads
Minor
Overall: the fix is correct for the reported hang and safe to merge for its stated scope. The params gap should be tracked as a documented follow-up rather than left silent. |
|
Posted the review on PR #1852. SummaryI reviewed the change to Verified good:
One latent gap flagged (not a blocker):
The fix is safe to merge for its scope; the params divergence should be tracked as a documented follow-up rather than left silent. |
Address ask-bonk review: the synthetic slot-only sub-route (e.g. /baz) has a static URL pattern, so a catch-all children page reading params.catchAll gets empty params (Next.js passes ["baz"]). Document the divergence inline in discoverSlotSubRoutes and add a graph-level test locking in the current static shape so a future param-population fix must update it.
|
Thanks for the review. Addressed the catch-all-children params gap in-PR:
The full param-population fix needs the slot-override-style request-time pattern matching threaded to the children prop (graph → manifest → wiring → element builder), which is broader than this hang fix; I've spun it off as a dedicated follow-up so this PR stays scoped to resolving the timeout. |
Finishes the explicit-slot-but-no-page case of #1535 (children-slot priority landed in #1659).
Problem
When a parallel slot defines an explicit sub-page (e.g.
@slot/baz/page.tsx) but there is no corresponding children page (baz/page.tsx) and nodefault.tsxfor the children slot, vinext minted a synthetic/bazroute with anullchildren page. That static route shadowed the sibling catch-all ([...catchAll]) and rendered an empty children prop, so the request hung — the upstreamshould match correctly when defining an explicit slot but no pagetest timed out.Fix
Next.js serves such a slot-only sub-route's children from the nearest sibling catch-all.
discoverSlotSubRoutesnow falls the children prop through to a sibling catch-all page (findCatchAllPage) when no childrendefault.tsxexists. For the upstream fixture,/bazrenders[...catchAll]/page.tsx("main catchall") for children while@slot/baz/page.tsx("baz slot") fills the slot. A childrendefault.tsxstill wins over the catch-all when both are present.The change is localized to
discoverSlotSubRoutes; the children-slot priority case (/and/nested) from #1659 is untouched and still passes.Tests
Added a regression
describe("explicit slot but no page (issue #1535)")intests/app-route-graph.test.ts, ported from Next.js'test/e2e/app-dir/parallel-routes-catchall:@slot/baz/page.tsxdefault.tsxstill wins over the catch-all when both existCloses #1535