On this page
The short version
Fix the LCP image first: one preload, one
fetchpriority="high", correctsrcsetwidths. That is usually a full second on mobile.Remove scripts from apps you no longer use, then
defereverything that is not needed for first paint. INP is where most Shopify stores fail in 2026, and third-party JavaScript is why.Give every image and every banner a reserved height. CLS is the easiest of the three to pass and the one merchants forget.
What we measure, and what we ignore
Every audit we run starts with the same three numbers from Google's field data: Largest Contentful Paint (LCP), Interaction to Next Paint (INP) and Cumulative Layout Shift (CLS). The targets are 2.5 seconds, 200 milliseconds and 0.1. Those three, on the product page, on mobile, decide whether Google considers your store fast. They are also the three that track most closely with what shoppers feel.
We ignore the speed score in the Shopify admin. It is a lab score on a Shopify-run device that does not see your apps as a customer does. We also treat a Lighthouse score from a desktop as a warning sign rather than a result. A store can hit 90 on a MacBook and fail every Core Web Vital on a mid-range Android with a weak signal, and the Android is the one buying.
The order below is the order we work in on a store we have never seen before. It is sorted by how much each fix moves the field numbers per hour of developer time, not by how easy it is to explain.
The diff is the only complete list of what was changed. Memory is the incomplete one.
The diff is the only complete list of what was changed. Memory is the incomplete one.
Images and the LCP element
On a product page the LCP element is almost always the first product image. On a home page it is the hero. Four things decide how fast it appears:
Preload it. Shopify's image_tag filter accepts preload: true, which writes a <link rel="preload"> into the head. Use it on exactly one image per page. Two preloads compete and both lose.
Give it fetch priority. Add fetchpriority="high" to the LCP image tag and make sure it is not lazy loaded. Dawn-derived themes sometimes ship the first slide of a product gallery with loading="lazy", which is a self-inflicted delay.
Size the srcset to the layout. A gallery that renders at 400 pixels wide on a phone should not ship a 2048 pixel candidate first. Pass the widths parameter to image_tag with real breakpoints and a sizes attribute that matches the CSS.
Stop the hero video on mobile. An autoplay MP4 behind a headline is a second LCP candidate that never wins. Serve a poster image on small screens and let the video load after interaction.
{{ product.featured_image | image_url: width: 1600 | image_tag: widths: '400, 600, 800, 1000, 1200, 1600', sizes: '(min-width: 990px) 50vw, 100vw', preload: true, fetchpriority: 'high', loading: 'eager' }}

Below the fold, the opposite applies: everything gets loading="lazy", and every image gets explicit width and height attributes so the browser can reserve the box before the bytes arrive. That single habit removes most of the CLS on a typical store.
Rather have this done than read about it? Send us the store URL. We read the code and send back the exact list, free, within 4 hours.
Get the free audit