Skip to content

perf(images): 1.5.0 — thumbnails stop downloading the original - #17

Merged
DelanoJoey merged 2 commits into
mainfrom
perf/next-image-thumbnails
Sep 20, 2026
Merged

DelanoJoey merged 2 commits into
mainfrom
perf/next-image-thumbnails

Conversation

@DelanoJoey

Copy link
Copy Markdown
Collaborator

What

RelatedArticles and BentoGrid each painted a card with a raw <img src={sourceFile}>, so the browser downloaded the original at full resolution to fill it. Both now use next/image with fill and a sizes hint matching the component's own grid, plus an optional imageSizes prop for a consumer rendering into a narrower container.

next is already a peer dependency (^16.2.5) and every consumer sets transpilePackages: ['@isimplifyme/ui'], so nothing new ships.

Measured

Controlled A/B on a local endsights production build — one harness, identical scroll, only these two files swapped in node_modules:

baseline (raw <img>) this PR
related-image bytes 3.905 MB 0.014 MB
images loaded 3/3 3/3
intrinsic width painted into a 379px box 1536 px 640 px

The optimizer returns a 640×427 WebP at 4,146 bytes for a 1,984,124-byte source. Verified in both headless-shell and a headed browser; screenshot confirms the cards render.

Adellion had already hit the same defect on its homepage — 113 MB of full-resolution JPGs — and worked around it by leaving backgroundImage unset and rendering its own next/image in children. That workaround can come out once this lands.

Tests

The first tests either component has ever had. Their absence is why a green 45/45 suite stayed green right through the original defect.

They pin the property that produced the saving — the browser is offered narrow candidates and a sizes hint, never the source file at its own URL — rather than the identity of the component providing it. 8 of the 11 fail when the two sources are reverted to main; the 3 that pass are the unchanged negative cases (no image, empty list, no background).

Full suite 56/56, tsc --noEmit clean.

Behavior changes to be aware of

  • alt on the related-card image is now "". The card's <h3> carries the title directly below it, so a matching alt read the same string to a screen reader twice. The image is decorative; the link's accessible name is unchanged.
  • next/image throws on a remote URL whose hostname isn't in images.remotePatterns. Checked all three consumers that use these components (endsights, simplified-media, adellion): 171 string featured_media values, zero remote — every image resolves to a local /images/... path, so nothing breaks today. If a future post ever carries a remote featured_media, that consumer will need remotePatterns.
  • homewealthmap depends on the package but uses neither component.

Not in this PR

  • Theme-agnostic tokens. The earlier claim that dark sites inherit a white nav was wrong — endsights, simplified.media and homewealthmap all have light body backgrounds, and Adellion (the only dark one) already handles it with its own shim. Low priority, no live breakage.
  • bento-grid.tsx accentStyles.hazard still uses amber rgba(255,164,26) from before Adellion's cyan rebrand.

Publishing is tag-triggered (v*), so merging this does not publish 1.5.0.

🤖 Generated with Claude Code

`RelatedArticles` and `BentoGrid` both painted a card with a raw
`<img src={sourceFile}>`, so the browser fetched the original at full
resolution to fill it. On endsights.com/roblox-tycoon-games that was
3.905 MB for three 379x237 thumbnails — two of the sources are 1.9 MB
1536px JPGs. Adellion had already hit the same thing on its homepage
(113 MB of full-resolution JPGs) and worked around it by leaving
`backgroundImage` unset and rendering its own `next/image` in children.

Both now render `next/image` with `fill` and a `sizes` hint matching the
component's own grid, plus an `imageSizes` prop for consumers in a
narrower container. `next` is already a peer dependency, and every
consumer transpiles the package, so nothing new ships.

Measured A/B on a local endsights production build, one harness, only
these two files swapped:

  related-image bytes   3.905 MB -> 0.014 MB
  images loaded             3/3  -> 3/3
  intrinsic width painted
    into a 379px box     1536 px -> 640 px

`alt` on the related-card image is now `""`. The card's own `<h3>`
carries the title directly below it, so a matching alt read the same
string to a screen reader twice.

Adds the first tests either component has ever had — the absence of
them is why the suite stayed green through the original defect. They
pin the property that produced the saving (narrow candidates plus a
`sizes` hint, never the source file at its own URL) rather than the
identity of the component providing it; 8 of the 11 fail when the
sources are reverted to `main`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@DelanoJoey
DelanoJoey merged commit 0cba98c into main Sep 20, 2026
1 check passed
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