Why CLS matters
When a page's content shifts unexpectedly, users can lose their reading position, click the wrong target, or miss an important action. The most common triggers are asynchronous resource loads, DOM elements added before existing content, images or videos without reserved dimensions, web fonts that render differently than their fallback, and third-party embeds that resize themselves. Development environments often mask these problems: content behaves differently in production, test images are already cached, and local API calls are too fast to reveal real-world latency.
How CLS is defined
CLS measures the largest burst of layout shift scores for every unexpected shift that occurs during a page's entire lifecycle. A layout shift happens whenever a visible element changes its position between two rendered frames. A burst, or session window, is one or more individual shifts occurring in rapid succession, with less than one second between each shift and a total window duration of no more than five seconds. The largest burst is the session window with the highest cumulative score.
What makes a good score
For a good user experience, sites should keep CLS at 0.1 or less. The recommended measurement threshold is the 75th percentile of page loads, segmented by mobile and desktop devices.
Layout Instability API mechanics
Layout shifts are tracked by the Layout Instability API, which reports layout-shift entries whenever a viewport-visible element changes its start position (top and left coordinates in the default writing mode) between frames. Such elements are considered unstable elements. A new DOM element or a size change on an existing element only counts as a shift if it causes other visible elements to move.
Calculating the shift score
The browser computes a layout shift score from two factors: the impact fraction, which is the union of unstable elements' visible areas across two frames as a fraction of the viewport, and the distance fraction, which is the greatest horizontal or vertical movement of any unstable element divided by the viewport's largest dimension.
layout shift score = impact fraction * distance fraction
Consider an element occupying half the viewport that shifts down by 25% of the viewport height. The union of its visible areas in both frames is 75% of the viewport (impact fraction 0.75), and the movement is 25% of the height (distance fraction 0.25). The shift score is 0.75 × 0.25 = 0.1875.
Edge cases in scoring
Element movement only counts for the part visible in the viewport. If a green box shifts down and part of it goes off-screen, the invisible portion is excluded when computing the impact fraction were. Also, new elements inserted into the DOM don't count as shifts on their own—only the displacement they cause to other, existing elements ' start' positions matters.
Expected versus unexpected shifts
Shifts that happen in direct response to user input are generally fine, especially when they occur within 500 milliseconds of the interaction. Those get flagged with hadRecentInput and can be excluded from metric calculations. For slower interactions, it's best to reserve space and show a loading indicator right away, so users know content is coming and don't try to click something that might move. Use CSS transform: scale() and transform: translate() instead of animating height, width, top, right, bottom, or left, and respect prefers-reduced-motion settings for visitors sensitive to animation.
Where CLS data comes from
Layout shift data is available in both lab and field tooling, so you can check for problems before release and monitor real user experiences after. Field measurement options include the Chrome User Experience Report, PageSpeed Insights, the Search Console Core Web Vitals report, and the web-vitals JavaScript library. In the lab, Chrome DevTools, Lighthouse, PageSpeed Insights, and WebPageTest can all surface CLS issues.
Reading layout shifts with the Layout Instability API
To watch for layout shifts directly in JavaScript, use the Layout Instability API. A PerformanceObserver can be registered to capture layout-shift entries:
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('Layout shift:', entry);
}
}).observe({type: 'layout-shift', buffered: true});
Calculating CLS in JavaScript
CLS itself is computed by grouping unexpected layout-shift entries into sessions and taking the maximum session score. A reference implementation is available in the web-vitals JavaScript library source code. For most pages, the CLS value at page unload is the final score, but several edge cases complicate this straightforward calculation.
The Layout Instability API and the CLS metric do not always agree. If a page loads in the background, or is backgrounded before the browser paints anything, it should not report a CLS value at all. When a page is restored from the back/forward cache, its CLS resets to zero because users treat that as a new visit. The API also does not emit layout-shift entries for shifts occurring inside iframes, even though those shifts are part of the user experience and count toward the metric — a difference that can show up between CrUX and RUM data. Sub-frames can use the API themselves and aggregate their entries into the parent frame.
Because CLS spans the entire lifetime of a page, further complications arise. A tab might stay open for days or longer, and mobile browsers often skip unload callbacks for background tabs, so the final value may never be sent. The recommended solution is to report CLS whenever the page is backgrounded or unloaded, using the visibilitychange event to cover both, and let analytics backends compute the final score. Most teams should simply use the web-vitals library, which handles backgrounding, bfcache, and visibility scenarios automatically:
import {onCLS} from 'web-vitals';
// Measure and log CLS in all situations
// where it needs to be reported.
onCLS(console.log);
Reducing layout shift
For field debugging and lab-based optimization strategies, Google’s guide on optimizing CLS walks through identifying the sources of shift and fixing them with available tooling.
Additional focused guidance is available from Google Publisher Tag on minimizing layout shift for ad slots. Engineers who want a deeper conceptual grounding can watch Understanding Cumulative Layout Shift, a talk by Annie Sullivan and Steve Kobes from #PerfMatters 2020.
Tracking metric changes
Definitions and APIs for web vitals are occasionally corrected, and those corrections can move your dashboards and internal reports up or down. All changes to CLS implementation or definition are recorded in the Chromium metrics changelog for the metric. Feedback on the metrics can be filed in the web-vitals-feedback Google group.



