10 modules for Magento 2.4.8 and Hyvä, 3 of them free

Browse modules →
← Blog

Hyvä Loads Three Product Images Eagerly: On a Phone, Two of Them Slow Down Your LCP

How the catalog page of caruselbooks.ro went from 88 to the 90s in Lighthouse on mobile, with one preload tag and one number changed in a layout file.

Daniel Eberhat · 5 November 2026 · 4 min read

On a category page, the largest thing a phone shows above the fold is the first product image. That image is your Largest Contentful Paint, and on caruselbooks.ro it was arriving late. This is how we found out why, in two steps, and what each one was worth.

All the measurements below are from the catalog page of caruselbooks.ro, on an emulated Pixel 7 with a slow 4G connection (1.6 Mbps, 150 ms latency) and a CPU slowed down four times, taking the median of five runs. Lighthouse numbers are Lighthouse’s own mobile test.

Step one: let the browser find the image early

The browser can only start downloading an image once it sees it. On our catalog page, the first product’s <picture> sat about 52 KB into the compressed HTML, after the mega menu, the search autocomplete, the drawers and the filters. The browser’s preload scanner reached it at around 680 ms.

The fix is a <link rel="preload"> in the <head>, for exactly the file the <picture> will pick:

<link rel="preload" as="image" type="image/webp" fetchpriority="high"
imagesrcset="…-320.webp 320w, …-640.webp 640w"
imagesizes="…the same sizes as the picture's <source>…">

Three details decided whether this helped or hurt:

  • imagesrcset and imagesizes must match the markup exactly. With width descriptors and no imagesizes, the browser assumes the image is as wide as the screen, preloads a different file than the one the page uses, and then downloads both. We generate the preload and the <source> from the same function, so they can’t drift apart.
  • It can’t come from a block in head.additional. Magento renders the head before the page body, so when the product list runs, the head is already written. We add the tag to the finished HTML with a plugin on ResultInterface::renderResult, ordered before the built-in full-page cache stores the response.
  • Don’t send it as a Link: header. It worked on our development machine and vanished in production: Magento’s stock Varnish configuration removes the Link header in vcl_deliver. On a Magento store behind Varnish, the preload belongs in the HTML.
LCP image starts loading at started by
before 2088 ms 680 ms the <img>
after 1600 ms 239 ms the preload

A quarter less, from one tag. But Lighthouse on mobile still said 88, with all three of its “LCP image discovery” checks passing. Something else was in the way.

Step two: the two images nobody asked for

Lighthouse’s breakdown had one number that made no sense: the LCP image took 2173 ms to download, for a file of about 58 KB. At 1.6 Mbps that should take under 300 ms. The rest was competition: 541 KB of book covers downloading at the same time, on the same connection.

The reason is one line in Hyvä’s default theme. Magento_Catalog/layout/catalog_category_view.xml marks the first three product images as eager, so they skip lazy loading. On desktop that makes sense: the first row shows three or four products. On a phone the grid has a single column, so images two and three are below the fold, but eager still starts them right away, next to the one that matters.

Our first guess was wrong, and it’s worth telling. We removed fetchpriority="high" from images two and three. LCP went from 1600 to 1592 ms: nothing. They kept downloading straight away, just at a lower priority. What matters is loading, not fetchpriority: an eager image starts at once whatever its priority.

The second trap cost us more time. The product list template has ?? 3 as the default, so we changed it there. Nothing happened, because the value doesn’t come from the template: the layout file passes it as an argument, and the argument wins. The fix belongs in your theme’s layout:

<!-- app/design/frontend/<Vendor>/<theme>/Magento_Catalog/layout/catalog_category_view.xml -->
<referenceBlock name="category.products.list">
<arguments>
<argument name="eager_load_images_count" xsi:type="number">1</argument>
</arguments>
</referenceBlock>
catalog page, mobile before after
Lighthouse performance 88 91 to 96
LCP, Lighthouse 3.76 s 2.56 to 2.81 s
LCP, slow 4G 1796 ms 1624 ms

And on desktop, where images two to four really are above the fold? LCP went from 1472 to 1480 ms. They are in the viewport, so the browser fetches them immediately anyway, only at normal priority and after layout. The change costs desktop nothing.

What we took from it

  • On a phone, the LCP image needs the connection to itself. Count what else starts at the same moment, not just when your image starts.
  • loading="lazy" is the lever; fetchpriority is a hint. A low-priority eager image still competes for bandwidth.
  • In Magento, check the layout XML before the template. A default in a .phtml file loses to an argument from layout, without a warning.
  • Measure on a slow connection. Unthrottled, the catalog’s LCP was under 300 ms before and after both changes. None of this shows up on an office connection, and all of it shows up on a phone on 4G.

Today the same page scores 91 in Lighthouse’s mobile test, with a Layout Shift of 0, while running Google, Meta and TikTok tracking. The rest of the work on that store is in the case study.