Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added partial shell generation using root params #73816

Merged
merged 1 commit into from
Dec 17, 2024

Conversation

wyattjoh
Copy link
Member

@wyattjoh wyattjoh commented Dec 11, 2024

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.

@ijjk ijjk added created-by: Next.js team PRs by the Next.js team. type: next labels Dec 11, 2024
@ijjk
Copy link
Member

ijjk commented Dec 11, 2024

Tests Passed

@wyattjoh wyattjoh force-pushed the feat/generate-partial-shells branch 2 times, most recently from dc04d6b to af60007 Compare December 11, 2024 21:03
@wyattjoh wyattjoh changed the base branch from 11-12-feat_rootparams to canary December 11, 2024 21:03
@wyattjoh wyattjoh force-pushed the feat/generate-partial-shells branch from af60007 to b0881dd Compare December 11, 2024 21:13
@ijjk
Copy link
Member

ijjk commented Dec 11, 2024

Stats from current PR

Default Build (Increase detected ⚠️)
General Overall increase ⚠️
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 ⚠️ +123 kB
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 ⚠️ +557 B
index.pack gzip 72.7 kB 73 kB ⚠️ +368 B
Overall change 2.13 MB 2.13 MB ⚠️ +925 B
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

Commit: acb0ab9

@wyattjoh wyattjoh force-pushed the feat/generate-partial-shells branch 6 times, most recently from d04aae1 to c4663e6 Compare December 12, 2024 05:00
@ijjk ijjk added the tests label Dec 12, 2024
@wyattjoh wyattjoh changed the base branch from canary to 11-12-feat_rootparams December 12, 2024 05:00
@wyattjoh wyattjoh force-pushed the 11-12-feat_rootparams branch from df2bca0 to 32957e5 Compare December 12, 2024 05:03
@wyattjoh wyattjoh force-pushed the feat/generate-partial-shells branch 5 times, most recently from efa6905 to 2c87c16 Compare December 13, 2024 22:41
@wyattjoh wyattjoh changed the base branch from 11-12-feat_rootparams to chore/separate-static-paths-utils December 13, 2024 22:41
packages/next/src/server/request/params.ts Outdated Show resolved Hide resolved
packages/next/src/build/static-paths/types.ts Outdated Show resolved Hide resolved
packages/next/src/build/static-paths/pages.ts Outdated Show resolved Hide resolved
packages/next/src/build/static-paths/app.ts Outdated Show resolved Hide resolved
packages/next/src/build/index.ts Outdated Show resolved Hide resolved
@wyattjoh wyattjoh force-pushed the feat/generate-partial-shells branch from 0520878 to 0f4adf8 Compare December 14, 2024 04:46
@wyattjoh wyattjoh force-pushed the chore/separate-static-paths-utils branch from c957272 to d410536 Compare December 16, 2024 20:06
@wyattjoh wyattjoh force-pushed the feat/generate-partial-shells branch from 0f4adf8 to 9cb16ca Compare December 16, 2024 20:06
@wyattjoh wyattjoh force-pushed the chore/separate-static-paths-utils branch from d410536 to f72a407 Compare December 16, 2024 20:11
@wyattjoh wyattjoh force-pushed the feat/generate-partial-shells branch 2 times, most recently from 60a36f3 to b51ca16 Compare December 16, 2024 20:13
@wyattjoh wyattjoh marked this pull request as ready for review December 16, 2024 21:47
@wyattjoh wyattjoh force-pushed the chore/separate-static-paths-utils branch from f72a407 to affef26 Compare December 16, 2024 22:16
@wyattjoh wyattjoh force-pushed the feat/generate-partial-shells branch from b51ca16 to 386d2ec Compare December 16, 2024 22:17
@wyattjoh wyattjoh requested a review from ztanner December 16, 2024 22:18
@wyattjoh wyattjoh force-pushed the chore/separate-static-paths-utils branch from affef26 to ad7b0a5 Compare December 16, 2024 22:29
@wyattjoh wyattjoh force-pushed the feat/generate-partial-shells branch from 386d2ec to 18cb80b Compare December 16, 2024 22:29
}

/**
* Collects the segments for a given route module.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like the comment here needs to be updated to reflect that this function is collecting root params

Comment on lines +64 to +76
for (const params of routeParams) {
let i = 0
for (; i < unique.length; i++) {
const item = unique[i]
let j = 0
for (; j < paramKeys.length; j++) {
const key = paramKeys[j]

// If the param is not the same, then we need to break out of the loop.
if (!areParamValuesEqual(item[key], params[key])) {
break
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(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?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 😅

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@wyattjoh wyattjoh force-pushed the chore/separate-static-paths-utils branch 2 times, most recently from f5d10b6 to 20b4910 Compare December 16, 2024 23:44
@wyattjoh wyattjoh force-pushed the feat/generate-partial-shells branch from 18cb80b to e689a21 Compare December 16, 2024 23:44
@wyattjoh wyattjoh force-pushed the chore/separate-static-paths-utils branch from 22d9c2c to 21f813f Compare December 17, 2024 16:28
@wyattjoh wyattjoh force-pushed the feat/generate-partial-shells branch from e689a21 to acb0ab9 Compare December 17, 2024 16:28
@wyattjoh wyattjoh changed the base branch from chore/separate-static-paths-utils to graphite-base/73816 December 17, 2024 17:16
@wyattjoh wyattjoh force-pushed the feat/generate-partial-shells branch from acb0ab9 to 28c6ea2 Compare December 17, 2024 17:17
@wyattjoh wyattjoh force-pushed the graphite-base/73816 branch from 21f813f to ff0ef9c Compare December 17, 2024 17:17
@wyattjoh wyattjoh changed the base branch from graphite-base/73816 to canary December 17, 2024 17:17
@wyattjoh wyattjoh force-pushed the feat/generate-partial-shells branch from 28c6ea2 to d54fc94 Compare December 17, 2024 17:17
@wyattjoh wyattjoh merged commit c422755 into canary Dec 17, 2024
125 of 130 checks passed
Copy link
Member Author

Merge activity

  • Dec 17, 12:56 PM EST: A user merged this pull request with Graphite.

@wyattjoh wyattjoh deleted the feat/generate-partial-shells branch December 17, 2024 17:56
@github-actions github-actions bot added the locked label Jan 1, 2025
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jan 1, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants