What CLS actually measures
Cumulative Layout Shift is a score, not a time. Each time a visible element moves without the visitor having done anything to cause it, the browser records a shift. The size of each shift depends on how much of the viewport was affected and how far the content travelled. Shifts that happen close together are grouped into a window, and the page's CLS is the largest window of shifts during the visit.
Google treats a CLS of 0.1 or less as good, and anything over 0.25 as poor, judged at the 75th percentile of real visits to a page. Two consequences follow from that. First, a shift the visitor caused by tapping something, such as opening a menu, does not count. Second, a page can look fine on your office desktop and still fail, because the score that matters comes from real visitors, most of whom are on phones with slower connections where images arrive late and content shifts more.
Cause one: images without declared dimensions
This is the most common cause on Shopify stores and the easiest to fix. When an <img> tag has no width and height attributes, the browser has no idea how tall the image will be until the file arrives. It lays out the page as if the image were zero height, then pushes everything below it down when the image loads. On a product page with a gallery, a description and a set of recommended products, that can be several shifts in a row.
With width and height set, modern browsers reserve a box of the right aspect ratio before the image loads, even when CSS scales the image to fit a column. Nothing moves when the file arrives. Shopify's image_tag Liquid filter writes these attributes from the image's stored dimensions, so sections that use it are usually fine. The culprits tend to be older theme code that builds <img> tags by hand, custom sections added over the years, and markup injected by apps, which the theme has no control over.
The same problem applies to anything that occupies space but arrives late: product videos, embedded maps, review widgets and third-party badges. If the space is not reserved in advance, the content below will shift when it appears.
Cause two: content injected after the page has rendered
Announcement bars, cookie banners, countdown timers, free-shipping bars, live-chat launchers and app-generated reviews are often added by JavaScript after the initial HTML has been drawn. If they are inserted at the top of the page or in the normal flow, everything beneath them moves. A banner that appears a second after the page loads can on its own push a store over the CLS threshold.
There are two honest fixes. Either render the element in the initial HTML so it occupies its space from the start, or position it so that it overlays the page rather than pushing content, as a well-behaved chat button does. What does not work is hoping an app vendor has thought about this; many have not, and the only way to know is to measure with and without the app enabled.
Theme app extensions and app blocks can be as guilty as scripts injected through the theme's layout file. The question is not how the app is installed but whether it reserves space before it renders.
Cause three: web fonts swapping in
Most themes load one or two custom fonts. While a font is downloading, the browser either hides the text or shows it in a fallback system font and swaps once the custom font arrives. Shopify themes generally use the swap behaviour, which is right for readability, but the fallback font and the custom font rarely have the same letter widths and line heights. When the swap happens, paragraphs reflow, headings wrap differently and the content below moves.
The shift is usually small per line but it happens across the whole page at once, so it adds up. The fix is to make the fallback font match the custom one as closely as possible using the CSS size-adjust, ascent-override and descent-override descriptors on an @font-face rule for the fallback, and to preload the font files the first screen needs. Reducing the number of font weights the theme loads helps too; many themes load six and use three.
Cause four: late-arriving CSS and sliders that resize
Two smaller causes are worth checking. Stylesheets that load late, often because an app adds its own CSS file, can restyle elements after they have been drawn, changing their size. And carousels or sliders that initialise with JavaScript often render all their slides stacked vertically for a moment, then collapse into a single row once the script runs. That collapse is a large upward shift for everything below the slider, and it happens on the homepage, which is typically the most visited page.
The slider case is a good example of why CLS needs to be measured rather than reasoned about. The page looks correct a second later, so nobody on the team ever sees the problem. Visitors do.
How to measure it on your own store
Start with field data, which is what Google actually uses. Search Console's Core Web Vitals report groups your URLs by status and tells you which templates are failing on mobile and desktop. PageSpeed Insights shows the same real-visitor data for a single URL at the top of its report, above the lab result. If a URL has enough traffic to show field data, that figure is the one to trust.
Then reproduce the shifts in the lab so you can see what is moving. Three tools do this well:
- Chrome DevTools, Performance panel: record a page load with mobile throttling on, and look for the layout shift markers on the timeline. Clicking one highlights the element that moved and shows what it moved from and to.
- The Web Vitals extension for Chrome, with console logging enabled: it prints each shift as it happens, with the elements involved, while you browse the store normally.
- PageSpeed Insights or Lighthouse, lab section: the "Avoid large layout shifts" audit lists the elements that contributed most to the lab score, in order.
Test the templates that matter, not just the homepage: a product page, a collection page, the cart and, if you have one, a blog article. Test with the mobile device emulation on and the network throttled, because that is when late-loading content shifts the most. And test once with each suspicious app disabled in a duplicate theme, so you can attribute shifts to a specific cause rather than to the theme in general.
Working through the fixes in a sensible order
- Fix image dimensions in the theme's product and collection templates first. It is low risk, it usually accounts for the largest shifts, and it is the kind of change we documented in an anonymised performance case study.
- Reserve space for late content: give video and embed containers an aspect ratio, and render announcement bars in the initial HTML.
- Deal with fonts: fewer weights, preload the ones above the fold, match the fallback metrics.
- Review apps one at a time, measuring before and after. Remove the ones whose shift you cannot fix and whose value you cannot name.
- Re-measure in the field after a few weeks. Field data lags, because it is collected over a 28-day window, so do not expect Search Console to change the day after you deploy.
Common pitfalls
- Chasing a lab score. A 100 in Lighthouse on a fast laptop tells you little about a visitor on a mid-range phone on a train. Field data is the target.
- Installing a "speed" app to fix CLS. Layout shift is caused by markup and timing in the theme and apps; another script cannot reserve space that the theme did not. Some of these apps add shifts of their own.
- Fixing the homepage and stopping. Product pages carry most of the revenue and most of the media.
- Setting
font-display: optionalto avoid swap shifts without testing. It can leave the custom font never showing on slower connections, which the brand team will notice before you do. - Treating the number as the goal. The goal is a page that does not move under the shopper's thumb; CLS is just how you check.







