perf: lower manifest script preload priority - #8212
Draft
maxktz wants to merge 1 commit into
Draft
Conversation
Contributor
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueThanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
PR description mostly AI generated
Changes
This draft proposes rendering TanStack Start's generated JavaScript preloads with
fetchPriority="low"for both module and IIFE client output.The change is intentionally small:
getScriptPreloadAttrsadds low fetch priority to generated manifest preloadsThe purpose of this draft is to share a reproducible performance investigation and get maintainer feedback on whether low priority is the correct framework default. The evidence is promising, but it comes from one application and there are open questions described below.
Context
I migrated the same SSR portfolio from Next.js 16.3.0 to TanStack Start 1.168.49 with React 19.2.8. The design, content, fonts, data, hosting provider, cache policy, and user-facing behavior were kept equivalent.
The migration reduced HTML size and request count, and LCP remained comparable or improved, but mobile FCP initially regressed from roughly 1.15 seconds to 2.33 seconds. The page's SSR content was already present, so the investigation focused on resources competing before the first paint rather than server rendering or client execution.
Public reproductions:
What the investigation found
The unchanged TanStack document emitted four startup resources as ordinary module preloads:
Chrome assigned all four requests
Highpriority. They started alongside render-blocking CSS and fonts even though the SSR page did not need JavaScript to produce its first paint.The patched document emitted the same resources as:
The network records then reported those four requests as
Lowpriority. The resource set, script tags, CORS behavior, and module graph were otherwise equivalent. Content hashes differed because these were separate builds.A later dynamically imported Motion chunk remained
Highin the patched application. This patch only changes generated manifest preloads, not arbitrary scripts or runtime imports.Comparison with Next.js and React
This is not an application-specific pattern invented for the reproduction. The exact framework versions used by the site already apply the same priority policy through different plumbing:
getRequiredScriptsselects the bootstrap script, and the app renderer passes it to React asbootstrapScripts.fetchPriority: 'low'for both classic bootstrap script preloads andbootstrapModulesmodule preloads.ReactDOM.preloadfor JavaScript withfetchPriority: 'low'.The implementations are not identical. Next's App Router uses React bootstrap and preinit APIs, while TanStack Start builds explicit
modulepreloadlinks from the Vite or Rsbuild manifest. The shared policy is that early JavaScript fetching should not compete at high priority with render-blocking resources.The HTML standard explicitly passes a
modulepreloadelement'sfetchpriorityvalue into its module graph fetch options. Chrome's Fetch Priority guidance also recommends lowering non-critical preloaded scripts when they would otherwise compete with critical resources. Browsers without support ignore the hint without changing module semantics.Benchmark results
Controlled remote Lighthouse runs
Three runs per variant were interleaved against the three public Vercel deployments. These medians use Lighthouse simulated throttling.
Under this methodology, the patch improved median FCP by 580 ms on mobile and 144 ms on desktop compared with unchanged TanStack Start.
An earlier local three-way Lighthouse simulation showed the same direction: Next.js 1.063 seconds, unchanged TanStack Start 2.416 seconds, and patched TanStack Start 1.066 seconds for FCP.
Applied throttling caveat
The same deployments were also tested using DevTools applied network and CPU throttling instead of Lighthouse simulation.
That difference is only 13 ms. This is important: the large improvement is repeatable under Lighthouse simulation, but it did not reproduce at the same magnitude under applied throttling. The draft should not be interpreted as proof of an equivalent field-metric improvement.
Initial production comparison that prompted the investigation
This was an initial comparison, not the controlled three-run experiment above.
PageSpeed Insights results and variability
One complete comparable PageSpeed set produced these results:
These individual PageSpeed results were noisy. The same patched deployment produced mobile FCP values of 2.119 seconds and 1.051 seconds in two successful runs. Several other runs failed in PageSpeed's backend with throttling or overloaded-render-server errors. The controlled medians above are more useful than selecting the best PageSpeed result.
Reports:
Risks and open questions
ssr: falseroute can require JavaScript before useful content appears. That case should be benchmarked under constrained networking before making this a universal default.getScriptPreloadAttrs, but currently copy onlyhref,rel,as, andcrossOrigininto the HTTP hint. They do not carry the newfetchPriority. Where a browser acts on that hint before parsing the HTML, the HTML attribute may arrive too late to provide the intended scheduling behavior.The main question for maintainers is whether matching React's low-priority bootstrap behavior is the right default for all generated TanStack manifest preloads, or whether the framework should distinguish entry, route, selective-SSR, and user-configured assets.
Validation
The following affected checks pass locally:
Scripts.test.tsxcoverageScripts.test.tsxcoveragegit diff --checkThe upstream example-app browser check and a constrained
ssr: falseperformance comparison are still outstanding while this remains a draft.Checklist
Release Impact