Skip to content

perf: lazy-load MDX images and serve them as WebP - #1975

Draft
marcleblanc2 wants to merge 2 commits into
mainfrom
marc/perf/lazy-load-images
Draft

marcleblanc2 wants to merge 2 commits into
mainfrom
marc/perf/lazy-load-images

Conversation

@marcleblanc2

@marcleblanc2 marcleblanc2 commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Problem

A HAR of /docs/batch-changes/delete-a-batch-change shows 1.2 MB of PNG screenshots. They are uncompressed PNGs on GCS (sourcegraphstatic.com, storage.googleapis.com/sourcegraph-assets) served with max-age=3600 and no WebP negotiation, and ZoomableImage renders them as a bare <img> that loads eagerly.

Lazy loading alone does not help this page: it is 3748 px tall with images at 666 / 870 / 1515 / 2293 px, and Chrome starts fetching loading="lazy" images 1250 px ahead of the viewport (2500 px on slow connections), so all four load anyway. Measured on the preview vs prod: identical image bytes (1215 KB), FCP/LCP within noise. On a long page (/code-navigation/auto-indexing, 16 images) lazy loading cuts 2.8 MB → 28 KB before load.

Fix

  1. loading="lazy" decoding="async" on the inline image. Saves bandwidth on long pages.
  2. Route allow-listed remote PNG/JPEGs through /_next/image: WebP, capped at 1920 px wide, cached at Vercel's edge for a week (minimumCacheTTL). The allow-list lives in docs.config.js so next.config.js (images.remotePatterns) and ZoomableImage share it. SVG, GIF and other hosts (imgur, githubusercontent) pass through unchanged. The zoom modal keeps the original full-resolution file.

Measured with next start on this branch, the four images on the HAR page:

image PNG WebP via /_next/image
batch_changes_icon_in_menu (1320 px) 24 KB 6 KB
closing_batch_change_close_icon (2250 → 1920 px) 439 KB 80 KB
closing_batch_change_close_changesets (2300 → 1920 px) 275 KB 49 KB
closing_batch_change_deleting (2452 → 1920 px) 476 KB 78 KB
total 1214 KB 214 KB

Trade-offs

  • Vercel Image Optimization is a billed dimension (source images transformed, cache reads/writes). ~360 source images across the docs, transformed once per week per region.
  • A screenshot re-uploaded under the same URL can take up to a week to refresh at the edge. Drop minimumCacheTTL to fall back to Next's 4 h default if that matters.
  • w=1920 is the largest of Next's default deviceSizes that still covers the content column on a 2x display; wider screenshots are downscaled.

Verification

tsc, pnpm lint, pnpm build clean. On next start: every sourcegraphstatic.com PNG on the page renders through /_next/image as image/webp with Cache-Control: public, max-age=604800; a PNG from a bucket outside the allow-list returns 400. Page renders unchanged (images at intrinsic size within the column).

@vercel

vercel Bot commented Sep 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
sourcegraph-docs Ready Ready Preview Sep 17, 2026 10:02am UTC

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

ZoomableImage renders every doc image eagerly, so a page with several
1 MB PNGs downloads all of them before first paint.
The docs embed ~360 PNG screenshots from GCS (sourcegraphstatic.com and
storage.googleapis.com/sourcegraph-assets), uncompressed and with a
one-hour cache. Route allow-listed PNG/JPEG sources through /_next/image,
which converts to WebP, caps width at 1920px and caches at Vercel's edge
for a week. The zoom modal keeps the original full-resolution file.

Amp-Thread-ID: https://ampcode.com/threads/T-01a0ac9d-704f-73fe-8211-1b3e5840969f
Co-authored-by: Amp <amp@ampcode.com>
@marcleblanc2 marcleblanc2 changed the title perf: lazy-load MDX images perf: lazy-load MDX images and serve them as WebP Sep 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Spell check found 2 issue(s) in this PR

Only findings on lines added by this PR, and in its title and description, are shown.

Pull request description

  • line 2, column 123
    • sourcegraphstatic
  • line 26, column 65
    • sourcegraphstatic

Please correct the spelling, or add words which are correct to cspell-allow-list.txt.

Run npx cspell@10 --no-progress --dot '**/*' locally to check the full repository.

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.

1 participant