Skip to content

fix(web): preload the root error page so hydration skips a round trip - #347

Merged
botandbang merged 1 commit into
mainfrom
bnb/lighthouse-lcp
Nov 1, 2026
Merged

botandbang merged 1 commit into
mainfrom
bnb/lighthouse-lcp

Conversation

@botandbang

Copy link
Copy Markdown
Contributor

What and why

SvelteKit's client awaits the root error page (nodes/1) in start() before it hydrates, but the server only modulepreloads the route's own nodes (sveltejs/kit#17415). Every page fetched nodes/1 only after start.js had run, a round trip behind the largest chunk, before it could hydrate.

That request is also why Lighthouse's LCP check on /projects/halflight failed on a busy machine (2,404 and 2,408 ms against 2,300). On localhost the first paint lands after the modules run, so the simulated LCP counts every request finished by then. When nodes/1 finished first, which a loaded host makes the usual case, the LCP went from 2,101 to 2,252 ms, and one more simulated round trip put it over the budget.

Added: patches/@sveltejs__kit@3.1.0.patch, which adds the root error page's imports to the modulepreloads (the root-error part of sveltejs/kit#17416), registered in pnpm-workspace.yaml with a note to drop it once a release has the fix.

Changed: tests/build.test.ts counts the root error page in the JavaScript every page loads (now about 179 KiB of the 180 KiB budget), since every page downloads it.

Removed: nothing. The Lighthouse config and its 2,300 ms LCP limit are unchanged.

How it was tested

Full lhci collect on a fresh network, 4 times x 3 runs, with 16 busy processes on the machine: /projects/halflight LCP over the 12 runs went from 2,103 to 2,256 ms (8 of 12 at about 2,252) to 2,100 to 2,118 ms, and / and /tools/locallibrary dropped about 150 ms too. Direct Lighthouse runs with 48 busy processes went from a median of 2,254 ms (max 2,261) to 2,102 ms (max 2,160). The simulated FCP rose about 75 ms because lantern counts the extra modulepreload as blocking; FCP isn't asserted and the performance score stayed at 0.98.

Ran pnpm verify, pnpm test:lighthouse and pnpm test:e2e --project chromium in apps/web, plus tools/policy.sh and ast-grep scan.

Checklist

  • Tests cover the change (the build budget test and Lighthouse CI on these pages)
  • The READMEs, docs/ and --help still say what the code does

SvelteKit's client awaits the root error page (nodes/1) in start() before
it hydrates, but the server only modulepreloads the route's own nodes
(sveltejs/kit#17415). So every page fetched nodes/1 only after start.js
had run, a round trip behind the largest chunk.

Lighthouse's simulated LCP counts that request whenever it finishes before
the first paint on localhost, which a busy machine makes likely. That took
/projects/halflight from 2.10 s to 2.25 s, 48 ms under the 2.3 s budget,
and one more simulated round trip put it over (2,404 and 2,408 ms). With
nodes/1 preloaded alongside the other modules it's off the critical path:
under 16 busy processes, halflight's 12 runs measured 2,100 to 2,118 ms
(before: 2,103 to 2,256 ms, 8 of 12 at about 2,252), and / and
/tools/locallibrary dropped about 150 ms too.

The patch adds the root error page's imports to the modulepreloads. Drop
it once a SvelteKit release has the fix (sveltejs/kit#17416). The build
test now counts the root error page in what every page loads.
@botandbang
botandbang requested a review from bnb as a code owner October 31, 2026 15:11
@github-actions github-actions Bot added dependencies Pull requests that update a dependency file web The Local Network website (apps/web) tests Tests, fixtures and test harnesses tooling Repo config, lint and build tooling, and GitHub settings labels Oct 31, 2026
@github-actions

github-actions Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Coverage Report for apps/web

Status Category Percentage Covered / Total
🟢 Lines 95.43% (🎯 93%)
🟰 ±0 pp
7916 / 8295
🟢 Statements 94.09% (🎯 91%)
🟰 ±0 pp
12053 / 12810
🟢 Functions 94.21% (🎯 92%)
🟰 ±0 pp
3126 / 3318
🟢 Branches 84.71% (🎯 80%)
🟰 ±0 pp
5621 / 6635
File CoverageNo changed files found.
Generated in workflow #34 for commit 1df9ec4 by the Vitest Coverage Report Action

Size

Compared with main. KiB are 1,024 bytes.

This pull request main Change
Client JavaScript 680.7 KiB 680.7 KiB 0 B (0%)
Client JavaScript, brotli 226.8 KiB 226.8 KiB 0 B (0%)
Client CSS 89.6 KiB 89.6 KiB 0 B (0%)
Client CSS, brotli 12.9 KiB 12.9 KiB 0 B (0%)

@github-actions

github-actions Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Coverage Report for apps/localtoolbox

Status Category Percentage Covered / Total
🟢 Lines 100% (🎯 100%)
🟰 ±0 pp
4303 / 4303
🟢 Statements 99.64% (🎯 95%)
🟰 ±0 pp
5862 / 5883
🟢 Functions 99.65% (🎯 95%)
🟰 ±0 pp
1715 / 1721
🟢 Branches 97.72% (🎯 50%)
🟰 ±0 pp
2791 / 2856
File CoverageNo changed files found.
Generated in workflow #34 for commit 1df9ec4 by the Vitest Coverage Report Action

Size

Compared with main. KiB are 1,024 bytes.

This pull request main Change
Packaged (localtoolbox.zip) 366.1 KiB 366.1 KiB -3 B (-0%)
Unpacked (dist/) 842.8 KiB 842.8 KiB 0 B (0%)

@botandbang

Copy link
Copy Markdown
Contributor Author

Closing and reopening so CI runs on the new single-workflow CI against current main. Branch unchanged.

@botandbang

Copy link
Copy Markdown
Contributor Author

Closing and reopening so CI runs against current main (with #353's CodeQL fix). Branch unchanged.

@botandbang botandbang closed this Nov 1, 2026
@botandbang botandbang reopened this Nov 1, 2026
@botandbang botandbang closed this Nov 1, 2026
@botandbang botandbang reopened this Nov 1, 2026
@botandbang
botandbang enabled auto-merge November 1, 2026 16:50
@botandbang
botandbang added this pull request to the merge queue Nov 1, 2026
Merged via the queue into main with commit 16ba8ec Nov 1, 2026
101 of 129 checks passed
@botandbang
botandbang deleted the bnb/lighthouse-lcp branch November 1, 2026 17:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file tests Tests, fixtures and test harnesses tooling Repo config, lint and build tooling, and GitHub settings web The Local Network website (apps/web)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants