RevealTheme logo
Back to Blog

Cumulative Layout Shift: How to Eliminate CLS

Cumulative Layout Shift: How to Eliminate CLS
The RevealTheme Team

By

·

Cumulative Layout Shift is the Core Web Vital that makes people feel your site is broken even when nothing technically is. You go to tap a button, an image finishes loading above it, and suddenly your thumb lands on the wrong link. Google measures exactly that frustration and folds it into ranking. The good news: CLS is the most fixable of the three vitals, because layout shift always has a concrete, traceable cause. This guide walks through the real causes on WordPress sites and how to eliminate each one.

What CLS actually measures (and the number you're aiming for)

CLS scores the total amount of unexpected layout movement during a page's life, weighted by how much of the viewport moved and how far. It is one of the three Core Web Vitals alongside LCP (loading) and INP (interactivity, which replaced FID in March 2024). The thresholds are simple:

  • Good: 0.1 or lower — this is your target.
  • Needs improvement: 0.1 to 0.25.
  • Poor: above 0.25.

One critical distinction trips up almost everyone: lab CLS and field CLS are not the same number. Lighthouse and PageSpeed Insights run a synthetic load and measure shift only during that load. Real-world (field) CLS, the data Google actually ranks on, comes from the Chrome User Experience Report and accumulates shift across the entire page lifecycle using session windows — including shifts caused by a user scrolling, a sticky bar appearing, or an ad refreshing ten seconds in. This is why a page can score a green 0.02 in PageSpeed Insights but show "poor" in Search Console. If those two disagree, trust Search Console and start looking for shifts that happen after initial load.

Cause 1: Images and embeds without reserved space

This is the classic CLS culprit, but the WordPress story has changed. Since WordPress 5.5, core automatically adds width and height attributes to images inserted through the block editor, and modern browsers use them to reserve the correct aspect-ratio box before the file downloads. So the lazy claim "WordPress doesn't set image dimensions" is no longer true for normal content images.

The shifts that remain come from the gaps core doesn't cover:

  • CSS-resized images where a theme sets width: 100%; height: auto without the browser knowing the intrinsic ratio.
  • Background images loaded via CSS, which never reserve space at all.
  • Images injected by plugins or page builders (sliders, galleries, related-post widgets) that build markup at runtime.
  • Hand-written HTML in Custom HTML blocks where you forgot the dimensions.

Fixes: ensure every <img> carries explicit width and height (or a CSS aspect-ratio) so the browser can paint a placeholder box. WP Rocket's "Add missing image dimensions" toggle and LiteSpeed Cache both patch this automatically. For embeds — YouTube, Vimeo, Maps, AdSense — wrap the iframe in a container with a fixed aspect-ratio (16/9 for video) or a min-height, so the slot exists before the third-party content arrives.

Cause 2: Web fonts and the late reflow

Fonts are the CLS source people miss because the shift is subtle. When a custom font loads after the page renders, the browser re-lays-out the text, and because your custom font has different letter widths and line heights than the system fallback, paragraphs grow or shrink and everything below them jumps.

The common advice — set font-display: swap — solves the wrong problem. Swap eliminates invisible text (FOIT) but it actively causes the swap-in reflow that hurts CLS. The real fixes:

  • Preload your primary font files so they arrive before first paint. Perfmatters and WP Rocket both expose a font-preload setting.
  • Match fallback metrics using size-adjust, ascent-override, and descent-override on an @font-face fallback declaration, so the system font occupies almost exactly the same space the web font will. This shrinks the reflow to near zero.
  • Self-host instead of calling Google Fonts. A plugin like OMGF downloads the fonts locally, removing a third-party round trip and giving you control over preloading and font-display. (Self-hosting is also the cleaner GDPR posture in the EU.)

Cause 3: Content injected after load

These are the shifts that ruin your field CLS while your lab score stays green, because they fire on a timer or on interaction:

  • Cookie and GDPR banners that slide down from the top and push the whole page. Render them as a fixed overlay that floats over content, not in the document flow that displaces it.
  • Sticky headers and notification bars that the theme adds with JavaScript after the page paints.
  • Page-builder sliders — Swiper, Slick, the Elementor and Divi carousel modules — that initialize on DOMContentLoaded and collapse from a tall stack of slides into a single row. Reserve the final slider height in CSS so the collapse is invisible.
  • Ad units (AdSense, Ezoic) that fill an empty zero-height container. Always give ad slots an explicit min-height matching the most common ad size.

Cause 4: Lazy-loading the wrong image

Lazy-loading offscreen images is good. Lazy-loading your hero image is a CLS-and-LCP own-goal. Since WordPress 5.9, core deliberately skips native lazy-loading on the first content image on a page, because that image is usually the LCP element and lazy-loading it delays both its paint and its dimension reservation.

The problem is that aggressive optimization plugins re-break this. If you've configured Perfmatters, WP Rocket, or a3 Lazy Load to lazy-load everything, you've often reintroduced shift on the most visible part of the page. Exclude above-the-fold and hero images from lazy-loading. Most of these plugins have an exclusion field — add your hero image class or the logo to it.

How to find your specific shifts

Don't guess — instrument it:

  1. Search Console > Core Web Vitals tells you which URL groups fail CLS in the field. Start here, since it's the data Google ranks on.
  2. PageSpeed Insights gives you both field (CrUX) and lab numbers side by side, plus a "Avoid large layout shifts" diagnostic listing the offending elements.
  3. Chrome DevTools > Performance panel records the page and visually highlights every shift region with a red rectangle and timestamp — this is the most precise tool for catching a specific element.
  4. The Web Vitals Chrome extension shows live CLS as you interact with the page, which is the only easy way to reproduce post-load shifts from banners, sliders, and ads.

A practical order of operations

If you want the fastest path to a green score: first fix image and iframe dimensions, because that knocks out the bulk of load-time shift in one pass. Then preload and metric-match your fonts. Then chase the post-load offenders — banner, sliders, ads — using the Web Vitals extension while you scroll. Re-measure in the field after deploying; CrUX data is a 28-day rolling average, so Search Console can take a few weeks to reflect the improvement even when your lab score flips to green immediately.

CLS rewards patience over plugins. There's no single switch that eliminates it, but because every shift traces back to one of these handful of causes, a methodical site will reliably land under 0.1 and stay there.