Skip to content

Speed

Fixing LCP on Shopify themes

Published

What LCP is on a Shopify store

Largest Contentful Paint measures how fast the largest visible content element renders in the viewport. On roughly 80% of Shopify themes, that element is the homepage hero image — a full-width image in the first section above the fold. On PDPs it's the hero product image. On collections it's either the collection cover image or the first product card image. Google's threshold: good under 2.5 seconds, needs-improvement under 4.0 seconds. Below 2.5 seconds is the bar you ship to.

Shopify's automatic image pipeline does a lot of the work — AVIF and WebP auto-emit, lazy-loading below-fold images, CDN delivery. But the platform can't fix a 4MB JPEG hero image, can't tell the theme to mark the hero with fetchpriority="high", and can't preload an asset whose URL is dynamic per section. Those three knobs are where the LCP wins live on Shopify in 2026.

The hero-image rule

The single biggest LCP factor on a Shopify store is the homepage hero image size. The rule: ship hero images under 1600px wide. Above 1600px, file size grows faster than visible quality on any device. A 4000px JPEG hero is the most common LCP killer we see — the merchant uploaded a print-resolution image not realising Shopify serves the source unless the theme uses the image_url filter to request specific widths.

Resize at upload. Shopify's optimize-site doc2 confirms image file names cannot be modified after upload — and neither can the source asset dimensions. If you've uploaded a 4000px hero, the fix is to replace the image with a properly-sized version. Don't try to compress your way out of an oversized source.

The image_url Liquid filter

Shopify's image_url Liquid filter is the single most important asset directive in modern themes. It tells the platform to serve a specific width of an asset, and it auto-negotiates AVIF/WebP/JPEG per browser. Themes that use raw <img src=...> tags miss the auto-format negotiation entirely and ship the original file format to every browser, including the source's full size.

liquid Hero image with image_url filter, srcset, fetchpriority
 <!-- Hero image with image_url filter, srcset, fetchpriority --> <img src="{{ section.settings.hero_image | image_url: width: 1600 }}" srcset="{{ section.settings.hero_image | image_url: width: 600 }} 600w,
          {{ section.settings.hero_image | image_url: width: 1000 }} 1000w,
          {{ section.settings.hero_image | image_url: width: 1600 }} 1600w" sizes="100vw" width="1600" height="900" alt="{{ section.settings.hero_alt | escape }}" fetchpriority="high" loading="eager" /> 

Three things this pattern does. (1) The image_url filter requests specific widths — Shopify serves AVIF/WebP/JPEG at the requested width per browser. (2) The srcset gives the browser multiple widths to choose from based on viewport. (3) fetchpriority="high" tells the browser to prioritise loading this image before anything else — critical for LCP. loading="eager" overrides the default lazy-load for above-fold elements.

fetchpriority and preload hints

The preload hint in the document head tells the browser to start fetching the hero image before the HTML body is parsed. This shaves 200-400ms off LCP on most Shopify themes. The catch: the preload directive needs to use the same image_url widths as the actual <img> tag, otherwise the browser fetches twice. The preload should fire only on the template where the hero lives — typically the homepage (template == 'index').

liquid Preload hint in theme.liquid head, homepage only
 <!-- In theme.liquid <head> for homepage only --> {%- if template == 'index' -%} <link rel="preload" as="image" href="{{ section.settings.hero_image | image_url: width: 1600 }}" imagesrcset="{{ section.settings.hero_image | image_url: width: 600 }} 600w,
                 {{ section.settings.hero_image | image_url: width: 1000 }} 1000w,
                 {{ section.settings.hero_image | image_url: width: 1600 }} 1600w" imagesizes="100vw" /> {%- endif -%} 

Place the preload in theme.liquid inside the <head>, wrapped in the template conditional so it only fires on the homepage. The imagesrcset and imagesizes attributes mirror the srcset and sizes on the hero image — the browser only preloads the responsive variant it actually needs.

Fonts and critical CSS

Two secondary LCP factors on Shopify themes. (1) Custom fonts that load via @font-face block the first text paint if they're not declared with font-display: swap. (2) Theme CSS files that aren't critical-inlined push the LCP back because the browser waits for CSS before rendering the hero image's container. Most modern Shopify themes handle critical CSS inlining automatically; older custom themes don't.

Font fix: open the theme's main.css (or theme.css.liquid). Search for @font-face declarations. Confirm each has font-display: swap; declared. Without it, the browser holds first paint until the font downloads — a 600ms+ LCP hit on slow connections.

Critical CSS: if your theme audit shows the hero image renders 500ms+ after the HTML parse completes, the theme's CSS strategy is the likely cause. The theme.liquid debugging path3 per Shopify's SEO FAQ covers the inspection. Custom themes from agencies sometimes ship without critical-CSS inlining — fix involves moving above-fold styles inline.

The LCP audit workflow

Walk this on every Shopify store. (1) PageSpeed Insights on the homepage, mobile tab. Capture LCP. (2) View Source on the hero image — confirm it uses image_url filter with srcset, fetchpriority, and proper width/height attributes. (3) Check the head for preload hint. (4) Verify @font-face declarations include font-display: swap. (5) Re-run PageSpeed Insights after fixes. Target: under 2.5s LCP on mobile.

  1. PageSpeed Insights baseline (mobile tab) on homepage. Note LCP value and the LCP candidate element.
  2. View Source on the homepage. Search for the hero image's <img> tag.
  3. Confirm: fetchpriority="high", loading="eager", srcset with multiple widths, width and height attributes.
  4. Search the head for <link rel="preload" as="image">. Confirm it matches the hero's srcset.
  5. View the source asset's actual file size. If above 350KB or above 1600px wide, replace.
  6. Open the theme's CSS. Search for @font-face. Confirm font-display: swap.
  7. Re-run PageSpeed Insights after each fix in sequence — measure deltas.

Per Google's published thresholds4, LCP under 2.5s is good. Most Shopify stores can hit 1.6-2.2s on mobile with these fixes in place. The audit takes 30-45 minutes per store; the fixes take 1-2 hours of theme code edits on a non-Hydrogen theme.