Commit 5e1067a
Fix prerendering of interception routes with generateStaticParams (vercel#85835)
## What
Fixes a bug where interception routes in parallel slots could not be
prerendered using `generateStaticParams`, causing 404 responses when
these routes were accessed directly.
## Why
**The Problem:**
Interception routes like `app/@modal/(.)photo/[id]/page.tsx` could not
be prerendered even when they exported `generateStaticParams`. This was
because the static path generation code only examined "children"
segments in the loader tree, completely missing segments from parallel
routes (like `@modal`) that actually contribute to the pathname.
**Root Cause:**
The previous implementation used `childrenRouteParamSegments` which only
traversed the `children` branch of the loader tree:
```typescript
// OLD: Only looked at children
const childrenRouteParamSegments = [...segments from children only...]
// This missed parallel routes like @modal that have dynamic segments
```
For a route structure like:
```
app/
[username]/
page.tsx
@modal/
(.)[username]/
[id]/
page.tsx // ← This route's segments were MISSED
```
The build system couldn't discover the `[id]` parameter in the parallel
route because it never traversed that branch of the tree.
## How
**Solution:**
Introduces `extractPathnameSegments()` which properly traverses the
ENTIRE loader tree (not just children) to find ALL segments that
contribute to the pathname:
1. **BFS Traversal**: Explores both `children` AND all parallel route
slots (e.g., `@modal`, `@sidebar`)
2. **Depth Tracking**: Correctly tracks URL depth by:
- Skipping route groups `(marketing)` - not in URL
- Skipping parallel markers `@modal` - not in URL
- Including interception markers `(.)photo` - ARE in URL
3. **Prefix Validation**: Ensures static segments match the target
pathname before including dynamic segments
4. **Complete Parameter Discovery**: Returns all segments that
contribute to pathname construction, regardless of which tree branch
they're in
**Example:**
For `app/@modal/(.)photo/[id]/page.tsx`:
- Old: Missed the `[id]` parameter entirely
- New: Discovers `[id]` and enables prerendering with
`generateStaticParams`
## Changes
**New Module**: `extract-pathname-segments.ts` (192 lines)
- Core algorithm for traversing loader tree and extracting pathname
segments
- Handles complex cases: parallel routes, interception routes, route
groups
- Well-documented with examples and algorithm explanation
**Comprehensive Tests**: `extract-pathname-segments.test.ts` (897 lines)
- Tests for simple cases, nested structures, parallel routes
- Interception route handling in various configurations
- Route group behavior and edge cases
- Depth tracking validation
**Integration**: `static-paths/app.ts`
- Replaced `childrenRouteParamSegments` with `extractPathnameSegments()`
- Updated pathname construction to use segments from parallel routes
- Maintained backward compatibility
**E2E Test**: Added test validating prerendering works for intercepted
routes
## Test Plan
The new E2E test verifies:
```typescript
it('should prerender a dynamic intercepted route', async () => {
// Verifies build output contains the prerendered interception route
expect(next.cliOutput).toContain('/(.)john/1')
// Verifies it doesn't generate the non-intercepted path
expect(next.cliOutput).not.toContain('/john/1')
})
```
## Impact
**Before**: Interception routes with dynamic segments returned 404 when
accessed directly, even with `generateStaticParams`
**After**: These routes are properly prerendered at build time and
return correct responses1 parent 2ac197a commit 5e1067a
File tree
26 files changed
+3706
-1937
lines changed- packages/next
- src
- build
- segment-config/app
- static-paths
- app
- webpack/loaders
- server
- app-render
- dev
- request
- test/e2e/app-dir/interception-dynamic-segment
- app/@modal/(.)[username]/[id]
26 files changed
+3706
-1937
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
927 | 927 | | |
928 | 928 | | |
929 | 929 | | |
930 | | - | |
| 930 | + | |
| 931 | + | |
| 932 | + | |
| 933 | + | |
931 | 934 | | |
Lines changed: 26 additions & 112 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
20 | | - | |
21 | | - | |
22 | | - | |
23 | 20 | | |
24 | 21 | | |
25 | 22 | | |
| |||
63 | 60 | | |
64 | 61 | | |
65 | 62 | | |
66 | | - | |
67 | 63 | | |
68 | | - | |
69 | | - | |
70 | | - | |
71 | | - | |
72 | | - | |
73 | | - | |
74 | 64 | | |
75 | 65 | | |
76 | 66 | | |
| |||
82 | 72 | | |
83 | 73 | | |
84 | 74 | | |
85 | | - | |
| 75 | + | |
86 | 76 | | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
92 | | - | |
93 | | - | |
| 77 | + | |
| 78 | + | |
94 | 79 | | |
95 | 80 | | |
96 | | - | |
| 81 | + | |
97 | 82 | | |
98 | 83 | | |
99 | 84 | | |
100 | 85 | | |
101 | 86 | | |
102 | 87 | | |
103 | | - | |
| 88 | + | |
104 | 89 | | |
105 | 90 | | |
106 | 91 | | |
107 | | - | |
108 | | - | |
| 92 | + | |
| 93 | + | |
109 | 94 | | |
110 | 95 | | |
111 | | - | |
112 | 96 | | |
113 | | - | |
114 | 97 | | |
115 | 98 | | |
116 | 99 | | |
117 | 100 | | |
118 | 101 | | |
119 | 102 | | |
120 | 103 | | |
121 | | - | |
122 | | - | |
123 | | - | |
124 | | - | |
125 | | - | |
126 | | - | |
127 | | - | |
128 | | - | |
129 | | - | |
130 | | - | |
131 | | - | |
132 | | - | |
133 | | - | |
134 | | - | |
135 | | - | |
136 | | - | |
137 | | - | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
138 | 117 | | |
139 | 118 | | |
140 | 119 | | |
141 | | - | |
142 | | - | |
143 | | - | |
144 | | - | |
145 | | - | |
146 | | - | |
147 | | - | |
148 | | - | |
149 | | - | |
| 120 | + | |
| 121 | + | |
150 | 122 | | |
151 | 123 | | |
152 | 124 | | |
153 | | - | |
154 | | - | |
155 | | - | |
156 | | - | |
157 | | - | |
| 125 | + | |
158 | 126 | | |
159 | 127 | | |
160 | 128 | | |
| |||
174 | 142 | | |
175 | 143 | | |
176 | 144 | | |
177 | | - | |
| 145 | + | |
178 | 146 | | |
179 | 147 | | |
180 | 148 | | |
181 | | - | |
182 | | - | |
| 149 | + | |
| 150 | + | |
183 | 151 | | |
184 | | - | |
185 | 152 | | |
186 | 153 | | |
187 | | - | |
188 | 154 | | |
189 | 155 | | |
190 | 156 | | |
| |||
221 | 187 | | |
222 | 188 | | |
223 | 189 | | |
224 | | - | |
225 | | - | |
226 | | - | |
227 | | - | |
228 | | - | |
229 | | - | |
230 | | - | |
231 | | - | |
232 | | - | |
233 | | - | |
234 | | - | |
235 | | - | |
236 | | - | |
237 | | - | |
238 | | - | |
239 | | - | |
240 | | - | |
241 | | - | |
242 | | - | |
243 | | - | |
244 | | - | |
245 | | - | |
246 | | - | |
247 | | - | |
248 | | - | |
249 | | - | |
250 | | - | |
251 | | - | |
252 | | - | |
253 | | - | |
254 | | - | |
255 | | - | |
256 | | - | |
257 | | - | |
258 | | - | |
259 | | - | |
260 | | - | |
261 | | - | |
262 | | - | |
263 | | - | |
264 | | - | |
265 | | - | |
266 | | - | |
267 | | - | |
268 | | - | |
269 | | - | |
270 | | - | |
271 | | - | |
272 | | - | |
273 | | - | |
274 | | - | |
275 | | - | |
Lines changed: 3 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
20 | | - | |
21 | | - | |
22 | | - | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| |||
0 commit comments