feat: added partial shell generation using root params#73816
Conversation
Tests Passed |
dc04d6b to
af60007
Compare
af60007 to
b0881dd
Compare
Stats from current PRDefault Build (Increase detected
|
| vercel/next.js canary | vercel/next.js feat/generate-partial-shells | Change | |
|---|---|---|---|
| buildDuration | 17.8s | 15.5s | N/A |
| buildDurationCached | 14.7s | 12.4s | N/A |
| nodeModulesSize | 410 MB | 410 MB | |
| nextStartRea..uration (ms) | 476ms | 471ms | N/A |
Client Bundles (main, webpack)
| vercel/next.js canary | vercel/next.js feat/generate-partial-shells | Change | |
|---|---|---|---|
| 1187-HASH.js gzip | 51.1 kB | 51.1 kB | N/A |
| 8276.HASH.js gzip | 169 B | 168 B | N/A |
| 8377-HASH.js gzip | 5.36 kB | 5.36 kB | N/A |
| bccd1874-HASH.js gzip | 53 kB | 53 kB | N/A |
| framework-HASH.js gzip | 57.5 kB | 57.5 kB | N/A |
| main-app-HASH.js gzip | 232 B | 235 B | N/A |
| main-HASH.js gzip | 34.1 kB | 34 kB | N/A |
| webpack-HASH.js gzip | 1.71 kB | 1.71 kB | N/A |
| Overall change | 0 B | 0 B | ✓ |
Legacy Client Bundles (polyfills)
| vercel/next.js canary | vercel/next.js feat/generate-partial-shells | Change | |
|---|---|---|---|
| polyfills-HASH.js gzip | 39.4 kB | 39.4 kB | ✓ |
| Overall change | 39.4 kB | 39.4 kB | ✓ |
Client Pages
| vercel/next.js canary | vercel/next.js feat/generate-partial-shells | Change | |
|---|---|---|---|
| _app-HASH.js gzip | 193 B | 193 B | ✓ |
| _error-HASH.js gzip | 193 B | 193 B | ✓ |
| amp-HASH.js gzip | 512 B | 510 B | N/A |
| css-HASH.js gzip | 343 B | 342 B | N/A |
| dynamic-HASH.js gzip | 1.84 kB | 1.84 kB | ✓ |
| edge-ssr-HASH.js gzip | 265 B | 265 B | ✓ |
| head-HASH.js gzip | 363 B | 362 B | N/A |
| hooks-HASH.js gzip | 393 B | 392 B | N/A |
| image-HASH.js gzip | 4.49 kB | 4.49 kB | N/A |
| index-HASH.js gzip | 268 B | 268 B | ✓ |
| link-HASH.js gzip | 2.35 kB | 2.34 kB | N/A |
| routerDirect..HASH.js gzip | 328 B | 328 B | ✓ |
| script-HASH.js gzip | 397 B | 397 B | ✓ |
| withRouter-HASH.js gzip | 323 B | 326 B | N/A |
| 1afbb74e6ecf..834.css gzip | 106 B | 106 B | ✓ |
| Overall change | 3.59 kB | 3.59 kB | ✓ |
Client Build Manifests
| vercel/next.js canary | vercel/next.js feat/generate-partial-shells | Change | |
|---|---|---|---|
| _buildManifest.js gzip | 749 B | 746 B | N/A |
| Overall change | 0 B | 0 B | ✓ |
Rendered Page Sizes
| vercel/next.js canary | vercel/next.js feat/generate-partial-shells | Change | |
|---|---|---|---|
| index.html gzip | 524 B | 523 B | N/A |
| link.html gzip | 539 B | 537 B | N/A |
| withRouter.html gzip | 520 B | 519 B | N/A |
| Overall change | 0 B | 0 B | ✓ |
Edge SSR bundle Size
| vercel/next.js canary | vercel/next.js feat/generate-partial-shells | Change | |
|---|---|---|---|
| edge-ssr.js gzip | 128 kB | 128 kB | N/A |
| page.js gzip | 204 kB | 204 kB | N/A |
| Overall change | 0 B | 0 B | ✓ |
Middleware size
| vercel/next.js canary | vercel/next.js feat/generate-partial-shells | Change | |
|---|---|---|---|
| middleware-b..fest.js gzip | 672 B | 668 B | N/A |
| middleware-r..fest.js gzip | 155 B | 156 B | N/A |
| middleware.js gzip | 31.2 kB | 31.2 kB | N/A |
| edge-runtime..pack.js gzip | 844 B | 844 B | ✓ |
| Overall change | 844 B | 844 B | ✓ |
Next Runtimes
| vercel/next.js canary | vercel/next.js feat/generate-partial-shells | Change | |
|---|---|---|---|
| 523-experime...dev.js gzip | 322 B | 322 B | ✓ |
| 523.runtime.dev.js gzip | 314 B | 314 B | ✓ |
| app-page-exp...dev.js gzip | 323 kB | 323 kB | N/A |
| app-page-exp..prod.js gzip | 127 kB | 127 kB | ✓ |
| app-page-tur..prod.js gzip | 140 kB | 140 kB | ✓ |
| app-page-tur..prod.js gzip | 135 kB | 135 kB | ✓ |
| app-page.run...dev.js gzip | 314 kB | 314 kB | N/A |
| app-page.run..prod.js gzip | 123 kB | 123 kB | ✓ |
| app-route-ex...dev.js gzip | 37.4 kB | 37.4 kB | ✓ |
| app-route-ex..prod.js gzip | 25.5 kB | 25.5 kB | ✓ |
| app-route-tu..prod.js gzip | 25.5 kB | 25.5 kB | ✓ |
| app-route-tu..prod.js gzip | 25.3 kB | 25.3 kB | ✓ |
| app-route.ru...dev.js gzip | 39 kB | 39 kB | ✓ |
| app-route.ru..prod.js gzip | 25.3 kB | 25.3 kB | ✓ |
| pages-api-tu..prod.js gzip | 9.69 kB | 9.69 kB | ✓ |
| pages-api.ru...dev.js gzip | 11.6 kB | 11.6 kB | ✓ |
| pages-api.ru..prod.js gzip | 9.68 kB | 9.68 kB | ✓ |
| pages-turbo...prod.js gzip | 21.7 kB | 21.7 kB | ✓ |
| pages.runtim...dev.js gzip | 27.4 kB | 27.4 kB | ✓ |
| pages.runtim..prod.js gzip | 21.7 kB | 21.7 kB | ✓ |
| server.runti..prod.js gzip | 916 kB | 916 kB | ✓ |
| Overall change | 1.72 MB | 1.72 MB | ✓ |
build cache Overall increase ⚠️
| vercel/next.js canary | vercel/next.js feat/generate-partial-shells | Change | |
|---|---|---|---|
| 0.pack gzip | 2.06 MB | 2.06 MB | |
| index.pack gzip | 72.7 kB | 73 kB | |
| Overall change | 2.13 MB | 2.13 MB |
Diff details
Diff for middleware.js
Diff too large to display
Diff for edge-ssr.js
Diff too large to display
Diff for main-HASH.js
Diff too large to display
d04aae1 to
c4663e6
Compare
df2bca0 to
32957e5
Compare
efa6905 to
2c87c16
Compare
acb7423 to
c957272
Compare
2c87c16 to
0520878
Compare
0520878 to
0f4adf8
Compare
c957272 to
d410536
Compare
0f4adf8 to
9cb16ca
Compare
d410536 to
f72a407
Compare
60a36f3 to
b51ca16
Compare
f72a407 to
affef26
Compare
b51ca16 to
386d2ec
Compare
affef26 to
ad7b0a5
Compare
386d2ec to
18cb80b
Compare
There was a problem hiding this comment.
looks like the comment here needs to be updated to reflect that this function is collecting root params
There was a problem hiding this comment.
(non-blocking nit)
this function is a pretty clever usage of loops but I worry that what it makes up for in performance it sacrifices in readability. I'm also not 100% sure that this is more performant than the more expressive version, eg:
for (const params of routeParams) {
const isDuplicate = unique.some((item) =>
paramKeys.every((key) => areParamValuesEqual(item[key], params[key]))
);
if (!isDuplicate) {
unique.push(params);
}
}In the worst case, this is O(N * M * K), which I think matches the implementation here as well:
for each params in routeParams (N times):
for each item in unique (M times):
for each key in paramKeys (K times):
// perform comparison
(I actually am curious if the built-in loops that power some/every might be slightly more efficient?)
There was a problem hiding this comment.
The only downside to the .some.every version is that each loop of N, we create 2 functions, which is avoided with the looping. I'll defer to @gnoff if this seems alright 😅
There was a problem hiding this comment.
I'm partial to the loop code b/c it should be faster both in internal implementation and b/c it creates fewer functions. Typlically for loops can be heavily optimized by JIT and the builtins are only optimized maybe in the newest runtimes and I suspect not likely nearly as much as raw loops. That said I think you can achieve better complexity by tracking the param 'key' becuase the routeParams are in order and spaces aren't allowed.
for /app/[foo]/[bar]/page.tsx
we'd encode
{} -> ''
{ foo: 'f0' } -> 'f0'
{ bar: 'b1', foo: 'f1' } -> 'f1 b1'
{ foo: 'f2', bar: 'b2' } -> 'f2 b2'
And so if you later encountered { bar: 'b2', foo: 'f2' } (same values, different object key order) you'd just recompute 'f2 b2' and see you already produced this result
It doesn't even have to be a string you could also have Maps of Maps but this has fewer objects created
The interesting question is what happens if you had just { bar: 'b3' }. This would really be treated like unknown params from the foo downwards so it should encode as '' since the bar can't be "reached" except if there was a known foo value.
f5d10b6 to
20b4910
Compare
18cb80b to
e689a21
Compare
22d9c2c to
21f813f
Compare
e689a21 to
acb0ab9
Compare
acb0ab9 to
28c6ea2
Compare
21f813f to
ff0ef9c
Compare
28c6ea2 to
d54fc94
Compare
Merge activity
|

This enables the generation of partial shells when using with partial prerendering, aided by the new
rootParams()API. Essentially, when your application only returns partial routes (where not all the route parameters are known), Next.js will now build route shells for these pages. We call these shells, fallback shells. They represent the partial state of the page that once served to the user, will provide a more complete loading experience as fast as possible.We'll also take every permutation of the provided root params and generate a shell just for them to ensure that any future calls to those routes will be able to use the more specific fallback shell that's generated rather than having to rely on a blank shell by default.