Skip to content

perf(sites): optimize article and feed images; record Phase 1 measurements (#71) - #142

Merged
clbotdev merged 1 commit into
mainfrom
perf/sites-phase1-followups
Oct 25, 2026
Merged

clbotdev merged 1 commit into
mainfrom
perf/sites-phase1-followups

Conversation

@clbotdev

Copy link
Copy Markdown
Contributor

Part of #75, closes the remaining work in #71 (the code from #78 was merged without measurements).

Change

  • The page-builder Image primitive now goes through the host's image resolver, like block images. Blog post, docs and blog-feed images now go through next/image (resized, modern formats). Article hero images load eagerly with fetchpriority="high"; feed images are lazy.
  • The resolver is stored on globalThis. /server and /primitives/server each bundle their own copy of the module, so registering it through one entry never reached the other. That's why these images were still full-size originals after Improve public site rendering performance #78.

Measurements (Lighthouse 13, mobile, simulated throttling, median of 5)

Sample tenant: homepage with 9 blocks and 8 images, 12 docs, 7 posts. Baseline = f8787cb (main before #78).

Page Metric before #78 main (after #78)
Home Perf score 55 91
TBT 1081 ms 144 ms
LCP 5.9 s 3.4 s
JS 643 KB 192 KB
Total transfer 996 KB 399 KB
Docs article Perf score 73 93
TBT 1321 ms 227 ms
LCP 2.27 s 2.64 s
Blog post Perf score 69 90
TBT 1260 ms 155 ms
LCP 3.13 s 3.30 s

This PR, on the blog post: image bytes 172 KB → 44 KB. LCP is a text paragraph on docs and blog. The small LCP change there is within run-to-run variance (first-run values 3.18 s vs 3.30 s) and comes from render-blocking CSS and the larger server-rendered HTML document on simulated slow 4G, not from images.

CLS is 0 everywhere.

Testing

  • page-builder and sites unit tests pass; sites typecheck passes.
  • Browser smoke on the production build: blog post hero and all 6 blog-feed images load from /_next/image, feed images are loading="lazy", and none are broken.

Not covered: images embedded inside TipTap content (docs/blog bodies) are rendered by @frontlit/text-editor, which has no resolver yet. That's a follow-up.

…er (#71)

The Image primitive rendered the original URL, so blog post, docs and
feed images skipped next/image. It now uses the same resolver as block
images, and article hero images load eagerly at high priority.

The resolver lives on globalThis: /server and /primitives/server each
bundle their own copy of the module, so registering it through one entry
didn't reach the other.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants