Repository navigation
Adopt partial prefetching - #1076
Merged
Merged
Conversation
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
oBusk
force-pushed
the
partial-prefetching
branch
from
October 22, 2026 20:50
3f61803 to
e043a63
Compare
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PLHpPQrKPUewGdqNgK98B6
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Catalog and diff pages shared the [...parts] route, so its App Shell painted a diff skeleton before every catalog page. Rewrite catalog-shaped paths to an internal /-/catalog/[...parts] route with the catalog skeleton as its shell, and redirect the internal path to the public one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PLHpPQrKPUewGdqNgK98B6
oBusk
force-pushed
the
partial-prefetching
branch
from
November 5, 2026 11:00
e043a63 to
dec0932
Compare
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The (?!.+%40) lookahead skipped a %40 directly after the first character, so /q%401.5.1 was rewritten to the catalog route and returned 404. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Catalog pages moved out of [...parts] and lost its maxDuration, falling back to the platform default. Cold catalog renders take under 2s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The rewrite regexes and isCatalogPage decide independently whether a path is a catalog page. The test asserts they agree on encoded and unencoded paths, and records the catalog paths left to [...parts]. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The route wrapped CatalogPage, which has its own boundary with the same fallback, in a second Suspense. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
generateMetadata and the page both parsed the same params. React cache() memoizes the parse, keyed on the serialized parts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paths like /react@* are catalog pages the rewrite doesn't match, so they rendered in [...parts] behind the diff skeleton. Redirect them to the bare package name, which the rewrite sends to the catalog route, and drop the catalog rendering from [...parts]. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DiffPageFallback copied DiffIntro's markup and the list of services. DiffIntro now renders through a slot-based layout that DiffIntroSkeleton also uses, and DiffServices owns the service list and its skeleton. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paths like /@types%2Fnode are catalog pages the rewrite doesn't match, and they returned 404 because the redirect target was the same path. Render the catalog in [...parts] in that case and only redirect the @* forms. getCatalogPackageName now runs once per render instead of after isCatalogPage. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The catalog name pattern only rejected a literal ... and %40, so /react%2E%2E%2E18.0.0 was rewritten to the catalog route and 404'd. npm package names never contain %, so reject it anywhere in the name and let [...parts] handle encoded paths. The test now matches with the same options Next uses for rewrites. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop the JSON round-trip and cast, and type the route props from Next's generated PageProps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This reverts commit fc8980a.
Catalog URLs that the rewrite misses, such as /@types%2Fnode or /re%61ct, were rendered by the diff route behind the diff page skeleton. Redirect them to /<name> instead, which the rewrite always sends to the catalog route, so catalog pages only render there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The diff route redirects every other catalog URL to its canonical form, so the rewrite no longer has to recognise an encoded %40 scope. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only the catalog route renders them now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Set ensureStatic on the root layout so new routes inherit it, instead of setting it on the diff and catalog pages only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replaces the hand-written Suspense boundary and inner component. The prerendered shell and prefetch segments are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The catalog route re-exported the diff route's error boundary. Move it to the app root instead, with ErrorBox in src/components, so both routes use it without importing from each other. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
loading.tsx owns the route's fallback, so CatalogPage goes back to a single default-exported component. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The catalog page is no longer rendered from the diff route, so the page can carry "use cache" instead of an inner component. It resolves the package name once, and the metadata takes that name directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Non-canonical catalog URLs (e.g. /react@*) don't match the catalog rewrite and land on the diff route, so both generateMetadata and the page must redirect them. One helper keeps the two from drifting. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
npm allows scopes like @.foo, but the scope pattern reused the name pattern, which rejects a leading period. Such URLs missed the rewrite, and [...parts] redirected them to themselves in a loop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adopts Partial Prefetching from Next.js 16.4. With it on, a default
<Link>prefetches the route's static App Shell, which is shared by every link to that route.A hard requirement for this PR is zero increase in server CPU, so it only uses prefetches that are served from build output. It does not use
<Link prefetch={true}>,router.prefetch(..., { kind: "full" })orprefetch()stages, because those render on the server once per link.Changes
partialPrefetching: Next 16.4 warns whencacheComponentsis on without it set, and it becomes the default in Next 17.[...parts]: the shell used to be an empty<Suspense>. It now has a skeleton shaped like the top of the diff page, so clicking a diff link paints immediately./-/catalog/[...parts]route with a catalog skeleton as their shell. Routing follows one rule:next.config.tssends canonical catalog URLs (/react,/@types/node) to the catalog route. The patterns live insrc/lib/catalogRoutes.ts, and tests run them through Next's own path matcher./react@*,/@types%2Fnode,/%40types/node) reaches[...parts], which redirects it to the canonical URL. A test checks that every such redirect target matches the rewrite, so the redirect can't loop./-/catalog/...path redirects to the public URL.loading.tsx.error.tsxmoves tosrc/app/error.tsxso the catalog route shares it./and the/-/aboutpages now get the same error UI instead of Next's default.prefetch={false}from the catalog's suggested diffs: a default prefetch now only fetches the shared static shell.ensureStatic = "shell"on the root layout: every route, including future ones, must keep its App Shell static.ensureStatic = "navigation"on/and the/-/aboutpages: the build fails if they become dynamic, even inside<Suspense>. Verified by adding aconnection()call inside<Suspense>on/-/about, which failed the build.Server cost
Measured on the preview deployment, with request logs in the Vercel dashboard:
_tree,_indexand page segments) is served from build output (x-vercel-cache: HIT/PRERENDER,x-matched-path: /[...parts].segments/…or/-/catalog/[...parts].segments/…). This holds for unknown packages too, so it never invokes a function. Production already serves its default prefetches the same way./and the/-/aboutpages come from the ISR cache with no function invocation.[...parts]redirect for non-canonical catalog URLs only runs for requests that already invoked a function before this PR.Testing
In DevTools, open the Network tab, filter on
_rsc, and throttle to Slow 4G./is-even)/reactor/@types/nodedirectly/react@*,/@types%2Fnodeor/%40types/node/reactor/@types/node/-/catalog/react/react🤖 Generated with Claude Code