Shopify LCP. Under 2.5s.
How to cut Shopify LCP from 4 seconds to under 2.5: hero image strategy, fetchpriority, font preloading, and the 12 Liquid changes that move the needle.
Twelve moves, sub-2.5 seconds.
Largest Contentful Paint on most Shopify stores lands at 3.5 to 4.5 seconds out of the box. Google's pass threshold is 2.5 seconds at the 75th percentile of real-user field data, measured via the Chrome User Experience Report. Twelve changes move a typical store from failing to passing in one focused week. The hero image accounts for 40 to 60 percent of the gap: ship it as WebP under 200 KB on mobile, with fetchpriority=high, no lazy-load, and a preload Link header. Self-host fonts with font-display=swap and preload the two weights above the fold. Defer or lazy-load the apps loading scripts above the fold (Klaviyo, Yotpo, Gorgias are the usual offenders). Inline critical CSS, defer everything else. Use the modern image_url Liquid filter, not the legacy img_url. Set explicit width and height on every img so the browser reserves space immediately. Eight to twelve hours of dev time cuts LCP by 1.0 to 1.5 seconds on a mid-size store, the difference between a failing report and a passing one.
The biggest visible element, timed to paint.
Largest Contentful Paint is the render time of the largest visible element in the viewport above the fold. On a Shopify homepage that's almost always the hero image or, on text-heavy collection pages, the H1 headline. Google's web.dev LCP guide defines the thresholds: 2.5 seconds or less is "good," 2.5 to 4 seconds is "needs improvement," over 4 seconds is "poor."
The number that affects Search rankings is the 75th-percentile LCP from real-user field data, collected via the Chrome User Experience Report. Lighthouse and PageSpeed Insights lab data are diagnostic tools, not the score Google uses to rank. A store with lab LCP of 1.8 seconds but a field LCP of 3.4 seconds at p75 fails Core Web Vitals because real users on real devices are seeing the slower experience.
Mobile-first is the rule: Google measures from a simulated Moto G4 on a 4G connection. That device is intentionally slower than what most US shoppers use, but it represents the long tail of mobile traffic on slower networks. The implication: a store can pass on a desktop test and still fail field data because the mobile experience is what 60 to 80 percent of Shopify traffic uses. About 75 percent of all visits to the page must hit the threshold for the page to "pass"; Google reports the page as passing only when the 75th-percentile user gets a sub-2.5s experience.
The single most useful tool: open PageSpeed Insights, paste your URL, and read the "Discover what your real users are experiencing" section at the top. That's CrUX field data. If it shows red, that's what Search Console will flag. Lab Lighthouse results below tell you why.
Twelve fixes, ordered by impact.
The list, in priority order. First, ship the hero image as WebP at the correct size: under 200 KB on mobile, under 350 KB on desktop. Second, add fetchpriority="high" on the hero img element. Third, never lazy-load the hero; loading="eager" or no loading attribute at all. Fourth, preload the hero via a Link header in theme.liquid (the head section). Fifth, preload critical fonts the same way. Sixth, self-host fonts via Shopify's asset_url filter or use font-display=swap if loading from Google Fonts.
Seventh, inline the critical above-the-fold CSS so the browser doesn't need a separate stylesheet request to render the hero. Eighth, defer non-critical JavaScript via the defer or async attribute, or via dynamic injection after the page is interactive. Ninth, remove or defer 3rd-party scripts loading above the fold; the audit usually finds 3 to 5 offenders. Tenth, avoid client-side rendering for the hero element; server-render it via Liquid.
Eleventh, use Shopify's modern image_url filter, not the legacy img_url; the new one supports WebP delivery and width parameters that match a responsive srcset. Twelfth, set explicit width and height attributes on every img element, including the hero. This last one prevents the layout-shift cascade that delays LCP painting by 100 to 200ms because the browser is recomputing layout when the image dimensions arrive.
The first six moves alone usually move LCP from 4.0s to 2.7s. The full twelve get most mid-size Shopify stores to 2.0 to 2.3 seconds at field-data p75. Diminishing returns kick in around 1.8 seconds; below that needs server-side performance work that exceeds the scope of a theme-level optimization sprint.
The before-and-after code.
The legacy pattern that fails LCP, often found in Dawn forks and older themes: {{ section.settings.hero_image | img_url: '1600x' | img_tag }}. That returns a JPEG (no WebP), no explicit dimensions, no fetchpriority, no preload. The browser discovers the image only after parsing the HTML body, which on a 4G connection is 1 to 2 seconds into the page load.
The 2026 pattern that passes: a Link header preload at the top of theme.liquid head, <link rel="preload" as="image" href="{{ section.settings.hero_image | image_url: width: 1200, format: 'webp' }}" fetchpriority="high">. Then in the section body, the img tag itself: <img src="..." width="1200" height="600" fetchpriority="high" loading="eager" alt="...">. Width and height attributes set in pixels, even when CSS resizes the image, because the browser uses them to reserve layout space.
This is specific to Shopify Online Store 2.0 sections. The section settings file declares the hero_image setting; the section's liquid template renders the img with the modern filter. For themes still on Online Store 1.0 (rare in 2026 but real for older Dawn forks), the same pattern works at the theme.liquid level without section JSON.
One detail the docs underplay: the format parameter in image_url accepts 'webp', 'jpg', 'pjpg', or omitted (Shopify's CDN negotiates via Accept header). Setting format: 'webp' explicitly is more reliable than relying on negotiation, because some intermediary caches don't vary on Accept and serve the wrong format. Explicit is safer.
The silent LCP killer.
Most Shopify stores fail LCP not because of the hero image but because of installed apps adding script tags above the fold. Each app inserts its own script tag, sometimes blocking the main thread for 100 to 400ms before the browser can render. Stack 5 to 8 apps and LCP gets pushed back 0.8 to 2.0 seconds. The audit lives in Shopify's own admin speed report or in PageSpeed Insights under "Reduce JavaScript execution time."
The five apps most often blocking LCP, in our audit experience. Klaviyo: the onsite tracking script runs synchronously on every page; load with the defer attribute or via Shopify's Customer Events pixel system to move it off the main thread. Yotpo, Loox, Judge.me: review widgets that load review data via XHR on page load; lazy-load them via IntersectionObserver so they only fire when the user scrolls near the review section.
Gorgias and Tidio chat widgets: load synchronously and create their own iframe, which adds 200 to 500ms of main-thread work. Defer the chat widget loading by 3 to 5 seconds via setTimeout after the page is interactive. Real users don't open chat in the first 3 seconds of a page visit, so the deferral is invisible to them. Third-party tracking pixels (Facebook, TikTok, Pinterest): load them via Google Tag Manager with the async attribute and trigger conditions that fire after the first user interaction.
The pattern for app deferral that works on Shopify themes: wrap the original synchronous script in a setTimeout or an idle callback. window.addEventListener('load', () => requestIdleCallback(() => { /* load app script */ }));. This guarantees the app doesn't run until the browser is genuinely idle, which is usually 2 to 4 seconds after first paint. LCP drops by 0.4 to 1.0 seconds depending on how many apps you defer.
One change, one measurement.
The discipline that separates teams who actually move LCP from teams who ship a batch of "best practices" and hope. Measure the baseline with PageSpeed Insights, both field (CrUX) and lab (Lighthouse). Apply one fix. Measure again. The delta tells you whether the fix landed. Then move to the next one. Bundling 5 changes into one deploy leaves you guessing which one moved the metric, and which one might have regressed it.
Field data lags lab data by 28 days because CrUX rolls a 28-day window of real-user samples. Lab data is instant. A typical loop: deploy the change in staging, run Lighthouse 5 times and average, deploy to production, watch field data for 7 to 14 days to confirm the improvement holds. Use the web-vitals JavaScript library for real-user monitoring in production; it logs LCP per session and gives you a faster feedback loop than waiting for CrUX.
Typical engagement on a mid-size Shopify store: 8 to 12 hours of focused dev time cuts LCP by 1.0 to 1.5 seconds. The hero image alone is 1 to 2 hours of work for 0.5 to 0.8 seconds of LCP win. App deferral is 2 to 4 hours for another 0.3 to 0.7 seconds. Font and CSS work is 2 to 4 hours for the last 0.2 to 0.4 seconds. Beyond that, server-side performance work (Shopify backend response time, Liquid render time on complex templates) starts mattering, and that's a separate engagement.
Related reading: our Shopify image optimization guide covers the WebP + srcset + lazy-load specifics that this article references at a high level. For the JavaScript-runtime side of Core Web Vitals see our Shopify INP fixes piece, and for layout stability see Shopify CLS fixes. Combined, the three articles plus the image-optimization deep-dive cover the full 2026 Core Web Vitals story for Shopify stores.
Six answers.
What is LCP and what's a good score for Shopify?
Largest Contentful Paint = render time of the largest above-fold element, usually the hero image or H1. Google's thresholds: under 2.5 seconds good, 2.5-4 needs improvement, over 4 poor. Field data via CrUX is what affects rankings, not lab Lighthouse scores.
Why is the Shopify LCP so often above 3 seconds?
Three reasons: oversized hero images (multi-megabyte raw uploads), too many installed apps loading scripts above the fold, and theme JavaScript blocking the main thread. Fixing the hero image alone usually saves 0.5-1.2 seconds. Most stores can hit sub-2.5s with focused work.
Should I lazy-load the hero image?
No. Lazy-loading the hero delays its download and pushes LCP back 200-800ms. For the hero, use loading=eager plus fetchpriority=high so the browser prioritizes it. Lazy-load everything below the fold.
What does fetchpriority do?
fetchpriority=high tells the browser to prioritize fetching that resource, jumping it ahead of others in the queue. For the hero image, this typically saves 100-400ms of LCP. Pair with preload Link headers for fonts and critical CSS.
Which Shopify apps hurt LCP most?
Klaviyo onsite scripts, review widgets (Yotpo, Loox, Judge.me), chat widgets (Gorgias, Tidio), and any third-party tracking pixel loaded synchronously. Defer the chat widget 3-5 seconds; lazy-load the review widgets; load Klaviyo defer. The audit tools surface the offenders.
How long does Shopify LCP optimization take?
Typical engagement: 8-12 hours of dev time cuts LCP by 1.0-1.5 seconds on a mid-size store. Hero image optimization alone is 1-2 hours. Theme JavaScript audit and deferral 4-6 hours. App audit and lazy-loading 2-4 hours. Most stores reach sub-2.5s in a one-week sprint.
From failing to passing.
Our Shopify speed-optimization engagements ship a 2-week Core Web Vitals overhaul: image audit, LCP tuning, JS deferral, font preloading, and a 30-day retention report. Scoped quote in 48 hours.
Published .