Cumulative Layout Shift In WordPress: Reducing It To “0” (Core Web Vitals Item)

Cumulative layout shift wordpress

In this guide, we’ll be improving cumulative layout shift in WordPress.

Cumulative layout shift (CLS) means elements are shifting while your website is loading. Google’s Layout Shift Debugger creates a GIF highlighting specific elements causing layout shifts on desktop/mobile since they usually happen too fast to be noticed by the naked eye.

Layout shifts can often be fixed by tweaking CSS, fonts, specifying dimensions, and excluding above the fold content from certain optimizations (lazy load, asynchronous CSS, and deferred JavaScript). As of writing this, cumulative layout shift is weighted at 15% of Lighthouse scores.

Before reading this, it will be helpful to understand the difference between FOIT, FOUT, and FOUC. The GIF on malthemilthers.com shows a nice visual demonstration of FOIT and FOUT.

Cumulative Layout Shift Score
Under .1 Good
.1-.25 Needs Improvement
.25+ Poor

 

1. Learn Which Elements Are Shifting

Cumulative Layout Shift Debugger – creates a GIF showing you all mobile/desktop layout shifts on a page. It was built by a Chrome engineer and is the main tool I recommend using.

Cumulative layout shift

PageSpeed Insights – several recommendations in your PSI report are directly related to your CLS score. If you see errors for any of these, they’re a good place to start.

  • Avoid non-composited animations (step #7)
  • Ensure text remains visible during webfont load (step #2)
  • Image elements do not have explicit width and height (step #5)
  • Avoid large layout shifts (another way to see what’s causing them, see image below)

Avoid large layout shifts

Layout Shift Regions – browse your website in real-time while viewing layout shifts (highlighted in blue). Here are instructions for using layout shift regions in Chrome Dev Tools:

  • Open Chrome Dev Tools.
  • Open the Command Menu.
  • Start typing “Rendering.”
  • Run the Show Rendering command.
  • Enable the Layout Shift Regions checkbox.
  • As you interact with a page, layout shifts are highlighted.

Layout shifts in chrome dev tools

Layout shift regions chrome dev tools

 

2. Change Font-Display To Swap Or Optional

If you need to ensure text remains visible during webfont load in PageSpeed Insights, try changing your font-display method to swap or optional.

Ensure text remains visible during webfont load

  • Swap – while your custom font is downloading, a fallback font is used. Once the custom font is finished downloading, the fallback font will be “swapped” with your custom font.
  • Optional – similar to swap method but lets the browser determine whether a custom font is used, then only displays custom fonts on fast connections (but not slower connections).

Both methods prevent FOIT (flash of invisible text) because instead of a blank area, a fallback font is loaded immediately. However, if the fallback font is swapped with the custom font and the 2 fonts have different sizes, it causes a layout shift in the fonts. This is called FOUT (flash of unstyled text. The solution is to use the same styling for fallback fonts as custom fonts (the same font family, size, etc). Google also has examples on scaling fonts using size-adjust in CSS.

Foit vs fout
Credit: malthemilthers.com

So how do you fix it?

Most cache plugins have a setting to set the font-display property (just change it to swap or optional). So does Perfmatters, Elementor, or you can use Swap Google Fonts Display. Other cache plugins like WP Rocket add “swap” automatically when minify or combine CSS are on.

Elementor google fonts swap

Otherwise, manually edit the font’s CSS.

View your “webfont load” report in PageSpeed Insights and use String Locator to find the problematic file. Find the CSS file where it’s loading, edit the stylesheet, and search for the font.

String locator

Once you find the font’s CSS, you can tweak it and try swap, optional, or try “size-adjust.”

Without font-display swap:

/assets/vendor/googleapis/css2?family=Lato:wght@100

With font-display swap:

/assets/vendor/googleapis/css2?family=Lato:wght@100&display=swap

Without font-display swap:

@font-face {
font-family: 'slick';
font-style: normal;
font-weight: 300;
src: local(''),
url('../fonts/slick.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
}

With font-display swap:

@font-face {
font-family: 'slick';
font-display: swap;
font-style: normal;
font-weight: 300;
src: local(''),
url('../fonts/slick.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
}

 

3. Exclude Above The Fold Content From Optimizations

Some cache plugin settings are meant to delay loading content until later. But above the fold content should not be delayed – people need to see it immediately.

Which means if you enabled settings for lazy load, load CSS asynchronously, or defer JavaScript in your cache plugin but didn’t exclude above the fold files, it can increase both CLS/LCP scores.

Exclude Above The Fold Images From Lazy Load

Most cache plugins can exclude images from lazy load which are usually your logo, sidebar image, and maybe some background images. The problem with manually excluding them is above the fold images can be different across your site. For example, I only use this image at the top of this post and nowhere else. Manually adding images from all pages/posts is a lot of work.

A better option is to exclude leading images which is supported by Perfmatters and FlyingPress. This lets exclude above the fold images from lazy load by setting the number of images typically shown above the fold. It’s easier and usually more effective!

Flyingpress image settings

Exclude Above The Fold JS Files From Being Deferred

If above the fold JavaScript is deferred, it’s also counterintuitive since the idea of defer is to execute files after the document is parsed. You’ll need to find JavaScript files loading above the fold and exclude them from defer. This is common with sliders and even third-party JavaScript. Most cache plugins have an option to do this in their settings or WP Rocket has a helper plugin.

Exclude Above The Fold CSS Files From Loading Asynchronously

Asynchronous CSS can cause FOUC (flash of unstyled content) where your content is loaded before the CSS. Not only is FOUC ugly and visually looks bad, but it can also create layout shifts.

Fouc flash of unstyled content
What FOUC looks like

In your cache plugin, try the following: exclude files from asynchronous CSS, disable asynchronous CSS completely, or remove unused CSS (the alternative to asynchronous CSS).

Removing unused CSS generally gives you better results but I don’t recommend WP Rocket because it loads used CSS inline instead of a separate file which is slower for visitors (but better for scores). FlyingPress, Perfmatters, LiteSpeed Cache, and almost all other cache plugins load used CSS in a separate file which is faster since it can be cached and doesn’t increase HTML size.

Remove unused css wp rocket vs perfmatters vs flyingpress
Vikas explains why WP Rocket’s remove used CSS is slower (update: Perfmatters uses separate file now)

If remove unused CSS doesn’t work or breaks your site, try enabling the loading CSS asynchronously setting. Make sure you add critical CSS which loads above the fold CSS immediately and prevents FOUC. And if that doesn’t work, then I would try another plugin.

Using Critical CSS

  • Run your site through a critical path CSS generator.
  • Copy the critical path CSS code.
  • Paste the code into your cache plugin’s critical CSS field.
  • Instructions can vary by cache plugin (some don’t support critical CSS at all).
Critical path css generator
Run your site through a critical CSS generator
Wp rocket critical css
Add the critical CSS to your cache plugin

 

4. Host Fonts Locally And Preload Them

Preloading fonts can also fix layout shifts.

When optional fonts are preloaded, layout shifts are completely eliminated in Chrome.

Step 1 is to host fonts locally. View your font files and make sure they include your domain instead of third-party domains like fonts.gstatic.com. Hosting fonts locally can be done in many tools: Elementor’s performance settings in the theme customizer, Perfmatters, FlyingPress, and Transfonter to name a few.

Local vs third party fonts

Once fonts are hosted locally, preload fonts mentioned above the fold and in CSS files.

PageSpeed Insights may recommend fonts to preload in preload key requests. Otherwise, find your font files in GTmetrix Waterfall (see screenshot above) Copy their URLs then preload them.

Some cache plugins support font preloading. You can use Perfmatters, Pre* Party Resource Hints, or do it manually with code. Preloaded fonts should also have the crossorigin attribute.

Preload font perfmatters

<link rel="preload" href="/fonts/roboto.woff2" as="font/woff2" crossorigin>

 

5. Specify Dimensions Of Images, Videos, Iframes, Ads

When elements don’t include correct dimensions, they cause layout shifts and can make other content shift around too.

Images

Specifying image dimensions means you need to add a width/height to the image’s HTML. Some cache plugins like FlyingPress and WP Rocket (and Permatters) have a setting to add missing image dimensions. Otherwise, you can manually add dimensions in HTML. It’s much easier to add dimensions to images in HTML than CSS. WP Rocket also doesn’t lazy load images when they’re found in CSS or JavaScript, in which case you would need to move them to inline HTML. Other cache plugins like FlyingPress include a lazy-bg helper class which makes it easier.

<img src="example.png" width="680" height="680" />

Responsive placeholders can also prevent layout shifts, like LiteSpeed and QUIC’s LQIP (low quality image placeholder).

Image elements do not have explicit width and height 1

Videos/IFrames

Similar to images, videos and iframes should also have a width and height specified in the embed code. It’s also important to make sure embedded videos and iframes are responsive.

<iframe src="https://www.youtube.com/embed/QlkzOoLRm6w" width="680"
height="340" ></iframe>

Advertisements

Google says “reserving space for the largest size configured to serve is the most effective way to eliminate layout shifts.”

They also recommend reserving space for the size most likely to be served (found in historical fill data from your Google Ad Manager reports).

Google has quite a few examples. By placing the ad code inside a div that specifies the ad’s dimensions, space is reserved so they don’t cause layout shifts for non-ad content on the page.

<div class="ad" style="min-width: 300px; min-height: 250px;"></div>

 

6. Use CSS Transform Property In Animations

If you have errors for non-composited animations in PageSpeed Insights, these also cause layout shifts.

Google recommends using the CSS transform property to use animations without causing layout shifts. Instead of changing width + height attributes, use transform: scale(). To move elements around, use transform: translate() instead of changing the top, right, bottom, or left property. In the image below, I used the Happy Addons for Elementor plugin for CSS transform.

Avoid non composited animations

Css transform translate elementor

I recommend testing a page with and without animations to see the difference in your cumulative layout shift score. If you really think animations are worth it, keep them. But it’s been reported many times in Facebook Groups that animations can slow down WordPress.

Cumulative layout shift wordpress animations

 

7. Delay JavaScript

Delaying JavaScript can also delay layout shifts caused by JS files.

Plugins that delay JavaScript include FlyingPress, WP Rocket, LiteSpeed Cache, Perfmatters, and Flying Scripts. WP Rocket and LiteSpeed Cache’s delay JavaScript feature are automatic while the other plugins require you to manually add specific JavaScript files you want delayed.

You can usually delay third-party code and specific plugins loading below the fold (such as comments or social sharing plugins). While I listed some common JS files you can delay below, it’s best to check your PageSpeed Insights report to see which files can be delayed. Look at your third-party code, layout shifts from JavaScript, and other JavaScript-related recommendations.

Delay javascript third party code

ga( '
ga('
google-analytics.com/analytics.js
analytics.js
gtagv4.js
analytics-minimal.js
/gtm.js
/gtag/js
gtag(
/gtm-
adsbygoogle.js
grecaptcha.execute
optimize.js
fbevents.js
fbq(
/busting/facebook-tracking/
disqus.com/embed.js
script.hotjar.com
wp-content/themes/script-name
wp-content/plugins/plugin-name

 

8. Use Separate Mobile Cache When It Makes Sense

If your mobile site has different styles, JavaScript, or mobile features, you should usually use a separate mobile cache.

As shown in a thread, this can prevent layout shifts. Cache plugins should have documentation on when you should use a separate mobile cache. This creates 2 cache files: one for desktop and one for mobile. It should be disabled in most cases since it increases CPU usage, but you need to go through the cache plugin’s documentation and learn whether it makes sense for your site.

Wp rocket separate mobile cache

 

It’s been reported in many support threads that some cookie notice plugins increase CLS. I recommend either Cookie Notice & Compliance for GDPR / CCPA or this 1 KB cookie notice.

Cookie notice compliance for gdpr ccpa plugin

Layout shift in cookie notice plugin

 

10. Serve Dynamic Content Properly

Dynamic content is clearly listed as a common offender for layout shifts.

If you must use dynamic content, reserve space for it using placeholders (specify a width and height). This ensures other elements on your page won’t shift when dynamic content is loaded.

Dynamic Content Examples:

  • Advertisements
  • Pops
  • Banners
  • Forms
  • GDPR notices

If you’re serving popups, Graham Ritchie on Stack Overflow says:

“You can fix this by adding position:fixed to the popup. This will take it out of the document flow and not cause a layout shift. (assuming it is more like a toast that covers the bottom of the screen rather than a dialog box style popup). You need to ensure that this style is applied inline within the HTML before the popup to stop it moving around once it receives styling.”

“Alternatively you can make the popup a bar at the top of the page (doesn’t have to be position: fixed as it can push the content down), at which point it will render correctly (assuming you inline your CSS).”

 

11. Set Global Font To Mulish In Oxygen Builder

If you’re using Oxygen Builder, there have been reports of global font settings causing CLS issues. The current solution seems to be to open stylesheets and set the global font to Mulish.

body { font-family: 'Mulish', sans-serif; }

 

Frequently Asked Questions

How do I reduce cumulative layout shift in WordPress?

Your cache plugin settings have a large impact on CLS in WordPress, specifically settings related to asynchronous CSS, fonts preloading, font-display property, delay JavaScript, add missing image dimensions, and separate mobile cache. Excluding above the fold content from certain optimizations can also improve CLS scores, such as excluding above the fold images from lazy load or excluding above the fold JavaScript from being deferred.

Which WordPress plugins fix layout shifts?

Cache plugins (and their settings), Perfmatters, Swap Google Fonts Display, Flying Scripts, and cookie plugins are a few WordPress plugins that can improve cumulative layout shift.

How do I reduce cumulative layout shift in WP Rocket?

To fix layout shifts using WP Rocket, try excluding above the fold files from lazy load, asynchronous CSS, and deferred JavaScript. You can also set a fallback critical CSS, add missing image dimensions, and enable delay JavaScript execution.

Drop me a comment if you have any questions! If you need help, please leave a link to your GTmetrix report and not your website or it will be marked as spam. I would appreciate it :)

Cheers,
Tom

You Might Also Like:

15 thoughts on “Cumulative Layout Shift In WordPress: Reducing It To “0” (Core Web Vitals Item)”

  1. I managed to get my CLS score down to 0.00. But there is an element I really like for my users that brings it back up to 0.01. I read what you said about 0.01 or less being fine. But if the shifting element I want is not really vital to my website, should I remove to have a 0.00?

    Reply
    • If it was my website I would only because I blog about speed. Since yours isn’t, I would just keep the element if you really like it. .01 is so small I doubt it’s going to make or break anything.

      Reply
  2. OMG!!! Thanks 1000 times Tom, I have a post with 10.000 words, 10 videos and countless images. It was already very optimised, but still got a 35 on mobile in google page insight… But with your post on divi and this post, I optimised again, and I can’t even believe it, can’t understand how it is possible for such a big post using divi. But I now get a 98 on mobile, crazy!! (Especially to disabled “optimise CSS delivery” in WP rocket reduced my CLS from 0.610 to 0.004! And in the last version of WP rocket, there is a new option to remove unused CSS, made a big difference too.)

    Reply
    • That’s awesome Antoine. Yeah, a lot of people are using the removed unused CSS option in WP Rocket and disabling optimize CSS delivery. Seems to be a popular setup at the moment. Glad the tutorials were so useful and you got such good results :)

      Reply
  3. Tom, terrific article. Just so you know, I unchecked the Optimize CSS Delivery in WP Rocket (+ added the fallback CSS from PurifyCSS) and my core web vitals turned green all at once! They were OKish before but the CLS dropped to 0.01, which is unreal! Thank so much for the tips!

    Reply
    • Just a follow up to my previous comment. By unchecking the Optimize CSS Delivery in WP Rocket you will NOT preload a bunch of JS / CSS files, which ultimately will result in a lower score on Page Speed Insights. The compromise I found was checking Optimize CSS Delivery in WP Rocket without providing any fallback CSS. Hope it helps

      Reply
  4. As ever, thanks for a great guide, Tom. Although I had a pretty quick loading time for my Home page I was getting warnings about CLS. It took a while to track down the cause but your guide pointed me in the right direction. For me, it was having the ‘Load CSS Asynchronously’ option selected in Litespeed Cache – once I removed that the CLS was down to 0.

    Reply
    • Ah, Vishal also just commented on this so I added the solution to the post and credited your names. Thanks for your feedback as always.

      Reply
  5. I got an avg CLS of 0.68 in my search console. I check for the moving components through chrome dev tool and its turn out my breadcrumbs and fonts. I think, you should try to disable optimize CSS delivery in WP rocket first. It may or may not work for you, but now my CLS score is 0.12 for mobile and 0.007 for desktop (according to the page speed insights).
    By the way, thanks for the help buddy!!! 

    Reply

Leave a Comment