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.
- Learn which elements are shifting
- Change font-display to swap or optional
- Exclude above the fold content from optimizations
- Host fonts locally and preload them
- Specify dimensions of images, videos, iframes, ads
- Use CSS transform property in animations
- Delay JavaScript
- Use separate mobile cache when it makes sense
- Change your cookie notice plugin
- Serve dynamic content properly
- Set global font to Mulish in Oxygen Builder
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.
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)
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.
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.
- 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.
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.
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.
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!
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.
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.
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).
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.
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.
<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).
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.
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.
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.
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.
9. Change Your Cookie Notice Plugin
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.
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