Mobile LCP unstable on product page (OS 2.0 – product-media.liquid)

Topic summary

A Shopify store owner reports unstable mobile Largest Contentful Paint (LCP) metrics on product pages, fluctuating between ~4.7s and ~11s in PageSpeed Insights, despite implementing standard optimization techniques.

Optimizations already applied:

  • Preloaded featured image with high fetchpriority in theme.liquid
  • Optimized srcset widths and responsive sizes attribute
  • Set first product image to eager loading with high fetchpriority, remaining images to lazy
  • Converted images to WebP format with compression

Seeking guidance on:

  • Additional factors in Online Store 2.0’s product-media.liquid or layout that could cause LCP delays exceeding 5 seconds on mobile
  • Recommended patterns for reserving space or implementing skeleton loaders for hero images in product templates
  • Theme-level scripts that may be blocking LCP and require different deferral strategies

The issue appears specific to mobile devices. Code snippets for both theme.liquid preload implementation and product-media.liquid image rendering logic are provided. Status: Awaiting community response.

Summarized with AI on November 2. AI used: claude-sonnet-4-5-20250929.

Hello Shopify team,

Store: https://khalegishop.shop
Tested page: جهاز ذكي يوفر كهرباء بيتك من أول - يوم عرض خاص اليوم فقط – khalij
Theme: Online Store 2.0 (customized). Issue only on mobile – LCP fluctuates from ~4.7s to ~11s across runs on PageSpeed Insights.

What I did:

  1. Preloaded the featured image with high fetchpriority in theme.liquid.
  2. Reduced srcset widths and set sizes to (min-width: 750px) 50vw, 100vw.
  3. Made the first product image eager + fetchpriority=high, others lazy+auto.
  4. Images are WebP and compressed.

Code snippets:

– theme.liquid (inside ) –

– product-media.liquid –
{% if media.media_type == ‘image’ -%}
{% if forloop.first %}
{% assign loading_type = ‘eager’ %}
{% assign fetch_priority = ‘high’ %}
{% else %}
{% assign loading_type = ‘lazy’ %}
{% assign fetch_priority = ‘auto’ %}
{% endif %}

<img
class=“global-media-settings global-media-settings–no-shadow{% if variant_image %} product__media-item–variant{% endif %}”
loading=“{{ loading_type }}”
fetchpriority=“{{ fetch_priority }}”
decoding=“async”
srcset="
{{ media.preview_image | image_url: width: 550 }} 550w,
{{ media.preview_image | image_url: width: 800 }} 800w,
{{ media.preview_image | image_url: width: 1100 }} 1100w
"
sizes=“(min-width: 750px) 50vw, 100vw”
src=“{{ media.preview_image | image_url: width: 700 }}”
alt=“{{ media.alt | escape }}”
width=“700”
height=“{{ 700 | divided_by: media.preview_image.aspect_ratio | ceil }}”
data-media-id=“{{ media.id }}”

{% endif %}

Question:

  • What else in OS 2.0 product-media.liquid or layout could keep LCP > 5s on mobile?
  • Is there a recommended pattern for reserving space or skeleton for the hero image in product templates to stabilize LCP?
  • Any theme-level scripts known to delay LCP we should defer differently?

Thanks!

Use “preload” parameter of image_url on your LCP image, not on your logo. Liquid filters: image_tag

Shopify image engine serves modern formats by default if browser supports them.
For example, I’ve received your .webp image in jpeg format when I select “view as iPhone” in dev tools.

Be careful when you generate the preload element yourself – your preload element loads one image, but actual <img> tag loads a different one and starts later:

And finally – your image is landscape but is cropped to portrait.
About half of the image is never shown, so pre-crop it properly.