3 Cumulative Layout Shift (CLS) Issues and Fixes for Your Blog

Last Updated on May 3, 2021 by Jeremy

Disclaimer: This Week in Blogging uses demographic data, email opt-ins, and affiliate links to operate this site. Please review our Terms and Conditions and Privacy Policy.

Google's Core Web Vitals metrics are said to be a new ranking factor starting in June 2021, and bloggers across the world are rushing to implement improvements on topics like Largest Content Paintful (LCP), First Input Delay (FID), and Cumulative Layout Shift (CLS).

While we could talk about each of these topics individually for days, one, Cumulative Layout Shift, is perhaps the most vexing- so much so that even those who run premium caching plugins like WP Rocket Pro may still not pass by default!

In this one, we wanted to highlight a bit more about what CLS is, what it looks like, and a few unique ways you can possibly improve your CLS score on your site beyond caching plugin settings!

  • Note: We feel that plugins like WP Rocket Pro are obligatory for those who want to run fast sites (both for CLS and otherwise). Unfortunately, the compatibility of some of the settings found in plugins like these will vary based on your theme and plugin array. In this article, we assume you are using a plugin like this one by default. The purpose of this post is to talk about other CLS issues that are common that you may need to work on.

What is Cumulative Layout Shift (CLS) and Why Should We Care?

CLS Example
An example CLS – Navigation Menu Loads Offset Before Adjusting to Final Position. This was fixed by narrowing the width of the navigation ever-so-slightly.

The premise of Cumulative Layout Shift (CLS) is that dynamic pages can reposition content on-screen which may affect the user experience negatively. The cumulative time associated with these shifts is measured across all users and, if high enough, is viewed poorly by Google (especially on mobile devices).

That “across all users” bit is quite important as we can't just measure CLS in a single snapshot but have to monitor average performance data across all users over time (thankfully this can be done in tools like PageSpeed Insights– we have PageSpeed Insight improvement tips here too).

So, what constitutes a shift? The easiest way to think of CLS is anything that causes the content you see to visibly move away from its initial placement. 

Some shifts can be quite innocuous- like an image taking a second to load and static content below it, like text, being effectively pushed down the page (many themes and plugins now call up a blank placeholder prior to the image loading to prevent this). Others can be rather jarring- like a menu becoming sticky when you scroll down past the first screen view and shifting everything down a proportional distance when it pops up (this looks awful).

From there, Google is concerned by how much time all of these events take as an average for all users be it those on a Gigabit desktop connection or the slowest smartphone connection. Blaming internet speeds for any given user is, unfortunately, now a thing of the past.

For Google, they want this score to be virtually zero. That is to say, they want all content to load and stay in the exact position you have coded it to appear in. To translate this into a technical value, a “good” score in the eyes of Google is a CLS of < 0.1 seconds. If you think in the realm of all users, on all connection types, and all screen styles, it is best to just assume they mean zero movements at all.

This means that almost all dynamic content should either be removed from your site or optimized to prevent a shift outright if you wish to pass this metric.

The main point of confusion on what constitutes a CLS event revolves around if a user should be expecting it or not. Expected shifts are generally thought to be okay- like when a user clicks to open a search bar or engaging with a drop-down navigation menu that moves the page. These typically do not constitute CLS events because they are triggered by user action and are to be expected. A navigation bar suddenly becoming sticky and shifting everything down is typically programmed by the site as an automatic design feature, not via user choice, and would likely be considered a CLS event.

The former is generally a non-issue but the latter is what Google is concerned about, and today we want to talk about three big things you should look for if you want to improve your CLS times.

  • In summary, a CLS event is any unexpected movement of static content on your page that is not initiated by a user.

Turn Off Special Effects for Your Theme

For most bloggers, the biggest contributing factor to CLS will revolve around the aesthetics of your theme. This is inherently unique to your blog- so what applies to you may not apply to others here.

As mentioned above, Cumulative Layout Shifts happen any time static content physically moves on your page outside of explicit user interaction (like opening a search bar). This means the sky is truly the limits on what is out there that could cause a shift, and you'll have to dig into the performance of your own site to truly see what moves your content. Tools like PageSpeed Insights and WebPageTest have options where you can see the visual loading of your site to help with this- although they leave a bit to be desired.

While we cannot outright tell you what shifts may be present on your site specifically, we can speak in a generic sense on some things we've seen contributing to CLS issues at large.

If we were to just start rambling off items, we'd go with things like sticky menus that fade in as you scroll and push content down, expanding or shrinking logos that do the same, and any kind of animation that moves content as a start. We'll keep repeating it, but anything that causes the static content on your page to move could be a contributor to CLS. Disabling these is worth testing. If you have 3rd party plugins that pop-in content, expand or decrease in size upon scroll, or produce other visual movements may also contribute to layout shift as well.

Note that we are using the words can and may as it may not always be the case. CLS issues are not created equally. Animations that do not affect any other content on your site, like floating sidebar share buttons on your desktop sidebar, may not contribute to CLS scores. They typically have to move otherwise static content when they're loaded to contribute to CLS- although your mileage may vary here. The literal phrase “layout shift” should be your guide here.

How you tackle these will be unique to your own theme or plugin array. Our theme, GeneratePress, has very clear settings for things like sticky navigation menus while others may not. The same goes for plugins and other scripts as well. As such, you may need to do some testing as well as searching Google with phrases like “How to fix CLS with [theme/plugin]” to see what others have done for your exact issue.

You may find a fix could be as simple as changing a setting on the back end of your theme. It could also be far more advanced and require you to change out a plugin or remove a certain aesthetic outright (including, but not limited to, changing themes). These are, unfortunately, up to you to figure out as there is an infinite number of possibilities in WordPress.

Fix CLS on Programmatic Ads

Mediavine CLS Fix
Example of Mediavine's CLS Fix (with Jeremy's logo in place of an ad). Note the light grey box is the space that is loaded for all ads independent of size (narrow image shown as an example of how blank space is displayed).

Beyond design-specific CLS issues, the next biggest contributor most bloggers find on their sites is programmatic advertising.

The reason for this is simple: programmatic ads take time to load. This happens because a user comes to your site, a bid request is sent out to all the ad exchanges, the network has to wait to see which one bids the highest, the ad has to be returned, and then after all that, the ad is finally loaded up on your site.

This happens concurrently for the three, five, or fifty ads on your page and, as stated above, takes time to complete.

The reason the shift occurs for most ad networks is that the ad spots are inherently collapsed until an ad is delivered. This was typically done because if no ad was returned, the slot would stay collapsed and no user would be the wiser. When an ad does become available, it would expand the space accordingly- causing a layout shift. This is no different than the lazy load example for images previously mentioned.

As this process can take a bit of time, a fast-reading user may be able to scroll down your page before an ad is fully loaded and inevitably will see the shift. This happens more than anyone would like to admit, which makes it a very real factor in most blogger's CLS scores. We simply dealt with it to enjoy the revenue potential of ad exchanges.

Thankfully, ad networks like Mediavine have been working to fix this CLS concern and have a novel solution (which must be turned on within your backend settings). This is achieved by having an uncollapsed box shown wherever ads are set to appear on your page. This box is large enough to accommodate all ad sizes such that no matter what ad size is loaded into it, no shift will occur.

Visually, it doesn't look the best (especially for slim horizontal banners which will have a fair bit of white space above and below the ad per the example image above). But if it is the choice between this or a poor CLS score, most bloggers will take the slight aesthetic hit of a pre-loaded box and a passing CLS score- I did.

It is important to note that you should also have some Mediavine PSA's enabled on your site as well to round this one out. These are generic messaging boxes that Mediavine has created for current events (like washing your hands during COVID) to fill unused ad inventory as a fallback. This ensures something will always display in the event of no ads being purchased for the slot. Otherwise, you may have just a blank space that would look a bit unusual to readers!

Mediavine has said they're working on possibly allowing custom PSA's to fill these voids (i.e. content we can create ourselves), but as of now you have to select between one of several Mediavine designed options. Again, these are turned off by default and need to be turned on within your dashboard.

  • Note: As of now, we are unsure if other ad networks have rolled out a fix like this. We hope all services will have a solution in place soon if they do not already. If yours hasn't, send their support team an email asking about the status of a fix as every ad network should be making this a priority.

Do Not Lazy Load Above-the-Fold Images

After implementing many of the above fixes, I struggled with the fact that I couldn't quite get my CLS scores < 0.15 seconds. As mentioned at the start of this one, Google wants < 0.1 seconds which seems like an impossibly difficult task to manage as it is almost impossible to spot.

It wasn't until I looked at how my site loaded visually over time in Page Speed Insights that I noticed my logo (on mobile specifically) was causing a layout shift that I couldn't replicate on my own device. Going into the depths of Google search results as to what could be causing that, I found a thread where someone pointed out that lazy loading above-the-fold images can cause a not-insignificant CLS.

When I read that, it really all started to make sense.

You see, we think of lazy loading images as being useful for images that are below-the-fold. They don't load until a user scrolls to within a certain distance of the image (100px, 200px, 1000px, etc). This, in effect, minimizes the amount of your site that has to be loaded upfront and gets your user to a functionally operational page that much faster. Then, as they scroll, lazy-loaded content can be called up only when it is needed but will generally be loaded before they arrive.

Lazy load programs are, unfortunately, not smart. The logic is often applied to all images- even those above the fold. Instead of just calling up an image directly, the lazy load function first has to initiate, it then sees that the image should be displayed, and then it loads the image. It may seem like splitting hairs here, but this causes a layout shift (about 0.05-0.08 seconds for me) and could be enough to get you to above that < 0.1 seconds target.

This is exactly what happened to me.

Getting around this really depends on your lazy load program. In WP Rocket Pro, for example, they have a feature where you can list images you want to exclude from lazy loading*- we added all images that appear sitewide above the fold. Others may offer a tag you can add into your images outright to prevent lazy load as well (easier for sidebar images where you can access the HTML code, less so for logos). Others still may not have anything at all. Getting around this may not be the easiest of tasks based on whatever service you use to run lazy load.

  • *This does not appear to be a feature in the free Lazy Load by WP Rocket- only the premium WP Rocket Pro.

Still, this change improved my CLS from 0.14 seconds to 0.02 seconds and was the missing piece of the puzzle I was looking for to achieve a passing score!

In this one, there are a few ways you can improve your scores that I should mention in greater detail:

  • Defining fixed width and height attributes for images, especially those above-the-fold, may help here. In this case, you're effectively defining the image box size before it is loaded which could help prevent a shift. This is great for sidebar images that may otherwise collapse the space (this is what I saw on my theme as in-body images seemed to have this defined already while sidebar images did not).
  • If you run WP Rocket Pro and want to exclude a specific image from lazy load as a one-off, you can also add the HTML attribute data-skip-lazy=”” into the image code. You may want to verify with tutorials from WP Rocket as this exact code could change.
  • Logo images are a bit trickier since you cannot typically edit that HTML in most themes, so you may need to exclude that image manually in WP Rocket's dashboard (CSS can also be used to define the logo size). Here, you could add is-logo-image to the “Exclude images or frames” section under LazyLoad settings to globally exclude the logo image. I like this over excluding the image URL because it will cover you if you ever change the logo in the future and forget to change this setting. Again, this exact code could change in the future and is always worth confirming.

When it comes down to it, Google measures CLS across all real-time performance data. No matter what we as bloggers do, we may not be able to fix shifts for everyone. But by doing many of the above optimizations we can average out our CLS times across all users and hopefully reach the target score!

To finish this one, we have to remind everyone that CLS is measured over time. Tools like PageSpeed Insights show scores for this under Origin Summary (real-world data) and Lab Data (spot checks). While the spot checks under Lab Data can help us monitor instantaneous improvements in our tests, it is the Origin Summary data that truly matters. As such, you should continue to check this score on popular posts over time to see how your averages change. But if you are passing Lab Data (especially if handily passing), then the real-world scores should catch up over a period of days or weeks. Patience is a virtue!

What have you done to fix your CLS score on your site? Comment below to share!

Recent Posts on This Week in Blogging

Check out our newest posts below!

Leave a Comment