Defer background images

Defer or lazy load background images for a faster largest contentful paint

Arjen Karel Core Web Vitals Consultant
Arjen Karel
linkedin

Defer background images.

Background images are seldom a good thing for the Core Web Vitals. Background images are not responsive, background images cannot acces the native loading attribute and we cannot natively control the priority of background images.

Background images will often cause issues with the Core Web Vitals. Deferring unimportant background images will in many cases improve your Core Web Vtials.

All to often I see this anti-pattern including background images on websites. Especially on WordPress websites that use page-builders like elementor.

  1. All images, including the the LCP image (the hero image) is lazy loaded
  2. Some image element that are not really important (like a spacer, a search-box background etc) are linked as background images in a stylesheet

lazy vs eager background images

In this small article I will show you how to lazy-load these background images in order to prioritize other, more important, images on the page.

A word of warning!

Let me start of with a word of warning! When the LCP element is delayed by background images mistakes have been made and you should preferably fix them the right way (preload the LCP image, do not lazy load the LCP image, avoid background images altogether). Unfortunately sometimes there is just too much legacy and you have no other short-time alternative then to patch the site up as best as you can. That is when you can apply a fix like I am presenting you here today!

Method 1: Defer everything 

The defer everything method is just a barbarian way of doing things. But it is easy to implement, and it will work well to improve the Core Web Vitals!  With this method all background images are deferred until the DOMContentLoaded event has been fired. This will give the browser a little bit of extra time schedule the most important resources first.

Here are the steps involved: first we are going to overrule the background-image style property for all elements that have a background image. Once the DOM content has been loaded we are going to remove this override again. By that time the non-background images will be queued for download. This would be a great time to then enqueue the less important background images for download.

The code

First create a style and place it in the HEAD of the page. It is important that this style has and id because we are going to use this id to remove this style tag later on. Of course instead of the wildcard (*) you could also only add the CSS classnames that actually have a background image.

<style id="no-bg-img">
    *{background-image:none!important}
</style>

Next, when the DOM content has been loaded, the LCP image will probably allready be queued for download. At this point it is safe te queue the background images. 

<script>
    window.addEventListener('DOMContentLoaded',function(){
        document.getElementById('no-bg-img').remove();
    })
</script>

If the LCP does not trigger for an early download this is probably because of JavaScript. In that case you could try to switch the 'DOMContentLoaded' for the 'load' event.

Before

late lcp caused by background image

After

early lcb after lazy background images

Method 2: Lazy-Load background images

The lazy-load background images method is slightly more gentile, advanced and requires more of a personal touch. 

It works as followed: first we are manually going to identify all the elements with a background image. We need to add a classname to those elements (.lazybg). Next we will observe these elements with the intersection observer and once they are close to the visible viewport we are going to lazy-load the background image.

The code

First create a style and place it in the HEAD of the page. This style looks similar to the previous style but instead of removing the background image property for all elements on the page we are just going to remove it for elements with a certain classname.

<style>
    .lazybg {background-image: none !important}
</style>

Next, when the DOM content has been loaded, we will start observing the element that have a background image. When that element scrolls into the viewport we weill remove the .lazybg class to trigger a background download. 

<script>
window.addEventListener('DOMContentLoaded', (event) => {

  // all elements wioth background images that should be lazy loaded 
  const lazyImages = document.querySelectorAll('.lazybg');
 
  // options for the observer
  const backgroundOptions = {
     threshold: 0,
     rootMargin: "0px 0px 50px 0px"
  };

  // the observer
  const imageObserver = new IntersectionObserver((entries, imageObserver) => {
     entries.forEach(entry => {
         if (entry.isIntersecting) {
             showImageBackground(entry.target);
             imageObserver.unobserve(entry.target);
         }
     });
  }, backgroundOptions);

    
  // observe each image
  lazyImages.forEach(image => {
     imageObserver.observe(image);
  });

  // show background image
  function showImageBackground(node) {
     node.classList.remove('lazybg');
  }
});
</script>

The advantage of this method is that background iamges that are not in the visible viewport are not queued for download. This frees up resources for the browser during the loading stage.

Before

late lcp caused by background image

After

background images delayed with the intersection observer

Conclusion

Both methods are effective in deferring the background image in favor of more important images like the Largest Contentful Paint image. The first method is real easy to implement and gets quick results. The second method adds real lazy load behavior to to background images and will provide a bigger pagespeed boost. 

Be careful when applying any of these methods. If you need to defer background images you page is what I like to call 'slow by design'. The preferred methods of fixing this would be to rewrite your pages and avoid the use of background images.

I help teams pass the Core Web Vitals:

lighthouse 100 score

A slow website is likely to miss out on conversions and revenue. Nearly half of internet searchers don't wait three seconds for a page to load before going to another site. Ask yourself: "Is my site fast enough to convert visitors into customers?"

Defer background imagesCore Web Vitals Defer background images