Optimize images for Core Web Vitals

Lean how images affect the Core Web Vitals en how to optimize them

Arjen Karel Core Web Vitals Consultant
Arjen Karel
linkedin

How Images Can affect the Core Web Vitals?

Images play a significant role in enhancing the visual appeal of a website, but they can also have a significant impact on its loading speed. Core Web Vitals are a set of metrics that Google uses to measure the user experience of a website, and image optimization is a critical factor in achieving good scores. In this article, we will discuss how to optimize images for Core Web Vitals and improve your website's loading speed.

Understanding Core Web Vitals

core web vitals all metrics

Before we delve into image optimization, let's briefly review the Core Web Vitals. They are a set of user-centric metrics that measure the loading speed, interactivity, and visual stability of a web page. The three key metrics are:

Largest Contentful Paint (LCP): measures the loading speed of the largest element on the page.
First Input Delay (FID): measures the time it takes for the page to become interactive.
Cumulative Layout Shift (CLS): measures the visual stability of the page.

Which Core Web Vitals can images affect?

You might be surprised to learn that images can affect all of the Core Web Vitals. Images, if they are queued for download at a late time during rendering or if they are simply too large will usually result in a high LCP score. If image dimensions are not set or change during the loading phase images can also affect the CLS score. And finally, if image decoding takes up too much main thread work, they can even affect the INP. Let's take a closer look:

Largest Contentful Paint

One of the Core Web Vitals is Largest Contentful Paint (LCP), which measures how long it takes for the largest element on the page (such as an image or video) to become visible to the user. If an image is enqueued too late or takes too long to load, it can significantly slow down the page's LCP score.

Cumulative Layout Shift

Another Core Web Vital is Cumulative Layout Shift (CLS), which measures how much the content on a page shifts around as it loads. Images can cause layout shifts if they are not properly sized or if they are inserted into the page after it has already loaded, causing other elements to move around.

First Input Delay and INP

Finally, images can also impact the third Core Web Vital, the INP, which measures the time it takes for a page to visually respond after a users to interacts with a page. If there are too many large images that need to be decoded the page may take longer to respond to user interactions, leading to a poor INP score.

Step 1: Optimize the HTML image element for speed

The first thing to check when optimizing images is the HTML code for all images. Images are simple and browsers are great at doing simple tasks. So try to avoid tricks and clever solutions and just use the plain old html image tag <img> and use all the options you have to speed up your images!
cwv image and attributes

Src attribute

Specifies the URL of the image. This property is essential, as it tells the browser where to find the image.

Width and height attribute

Specifies the width and height of the image in pixels. These properties are important for rendering the image correctly on the page, as they define the size of the image container and how the image fits inside it.

Alt attribute

Specifies alternative text for the image if it cannot be displayed. This is important for accessibility purposes, as it helps visually impaired users understand what the image is about. It's also important for search engine optimization (SEO), as search engines use the alt text to understand the content of the image.

Loading attribute (lazy loading)

Specifies how the browser should load the image (lazy, eager, or auto). This property is important for improving page performance, as it allows images to be loaded asynchronously and only when they are needed.

Srcset attribute

Specifies a comma-separated list of image sources and their sizes, which allows the browser to choose the best image source based on the device's screen size and resolution. This property is important for responsive design, as it ensures that users get the best possible image quality regardless of their device.

Sizes attribute

Specifies the sizes of the image source to use based on the viewport size. This property works in tandem with srcset to ensure that the correct image size is loaded on different devices and screen sizes, improving the overall performance of the page.

Decoding attribute

Specifies how the browser should decode the image (async, sync, or auto). This property is also important for improving page performance, as it allows the browser to (de)prioritize the decoding of the image over rendering the rest of the page.

Fetchpriority attribute

The fetchpriority attribute specifies the priority of a resource's fetch relative to other resources on the page. The priority attribute can have one of three values: "high", "medium", or "low". A resource with a high priority is loaded before resources with medium or low priorities. A resource with a medium priority is loaded before resources with a low priority. Resources with the same priority are loaded in the order that they appear in the HTML.

Step 2: Ensure the image is enqueued for download as early as possible

The second thing to do, after you have optimized the html is look at image scheduling. In many cases, the biggest bottleneck, when it comes to images affecting the LCP metric is late scheduling. If a browser has a chance to download the LCP element early during the rendering process the image will be available to the browser as early as possible and the browser can start painting that element early in the rendering process.

Sound simple right? Well, how do we make sure the image is queued for download as early as possible.

Preload the LCP element

The most effective way to ensure an early download is to preload the image. Preloading the image is done with a simple tag at the start of the <head> element. For example:

<link rel="preload" as="image" href="image.jpg">

This simple tag will tell the browser that we will need "image.jpg" pretty soon and the browser will start downloading this file immediately.

Eager load the LCP element

You should alway avoid lazy loading the LCP element. If you do lazy load the LCP element JavaScript based lazy loading is especially bad for pagespeed! JavaScript based lazy loading relies on a script to rewrite your <img> tag. Usually the img will have a data-src attribute that is rewritten to a scr attribute by JavaScript. The problem with this is 2-fold
1. The browser preload scanner does not recognize the data-src attribute and will not proactively trigger the element for an early download.
2. Js based lazy loading needs to wait for a JavaScript to be loaded and executed. This usually happens relatively late during the rendering process. This causes an even greater delay to the image.

To avoid this issue altogether make sure the LCP element is always eager loaded. Since 'eager' is the default for any image you only need to make sure the image is not lazy loaded. Do this by removing the native 'loading="lazy" attribute' or if you are using an optimization plugin check the documentation on how to skip lazy loading for an image!

Use high fetchpriority

If, for some reason, you cannot preload the LCP element at least make sure the element has the fetchpriority attribute set to high. This will hint to the browser that in images is important to the page and the browser will downloading it with a high priority. Please note that using fetchpriority="high" is usually not as efficient as preloading an image!

Avoid JavaScript based lazy loading

You should alway avoid lazy loading the LCP element. If you do lazy load the LCP element JavaScript based lazy loading is especially bad for pagespeed! JavaScript based lazy loading relies on a script to rewrite your <img> tag. Usually the img will have a data-src attribute that is rewritten to a scr attribute by JavaScript. The problem with this is 2-fold
1. The browser preload scanner does not recognize the data-src attribute and will not proactively trigger the element for an early download.
2. Js based lazy loading needs to wait for a JavaScript to be loaded and executed. This usually happens relatively late during the rendering process. This causes an even greater delay to the image.

Step 3: Ensure the image is downloaded as fast as possible

The third thing to do is to make sure you are not wasting precious network resources on images that are larger than they should be. You can do this using responsive image, using compression and using new and faster image containers

Responsive images

One of the most common problems with the LCP is sending a full sized desktop 'hero image' of 1920x1200px to a mobile device that renders the image at about 360x225. This means the image is about 28 times larger than it should be (sure, you could send images at a higher DPI, then the full size image would be 7 times larger!)!
That is where responsive images come in! Responsive images send a different version of an image to different viewports. This means we can send a small image to a mobile browser, a slightly larger image to a tablet and a full sized image to a Desktop to make sure no unneeded bytes are send!

Image Compression

Image compression let's you reduce file size of an image while preserving most of itś its visual quality. It involves various techniques that eliminate redundant or irrelevant data. Most modern CMS systems apply image compression when images are upload to the library. However if you bypass the library or use your own custom solution always check that images have the right compression level!

New and faster image containers

jpg web avif

Images are often one of the largest resources on a webpage, and they can significantly slow down the loading speed of a page if they are not optimized. Newer and faster image containers, such as the WebP and AVIF formats, can help reduce the file size of images without sacrificing their quality. This means that they can be loaded more quickly, which can improve the loading speed of the page.

Step 4: Eliminate layout Shift!

Images that change size while they are loading will cause a layout shift. It is as simple as that. There are 3 main reasons why images cause a layout shift (there are a lot more actually but these 3 are the most common ones)

1. Missing image dimensions

Image dimensions are the image width attribute and height attribute. If the width or the height attribute is not set the browser does not know how much space to reserve for the image during rendering and it will reserve 0 pixels for any missing dimension.

This means an image with no width and height set will render at 0x0 pixels and then, when the image has been loaded and decoded the browser will recalculate the lay-out to use the correct amount of space for the image.

2. Styling issues

Usually images are prevented from growing larger than the viewport by a simple CSS trick

img{
   max-width:100%;
height:auto; }

This is a great trick and you should use it. Unfortunately I regularly see variants of this trick that do cause a layout shift. For example by adding width:auto:

img{
   max-width:100%;
height:auto; width:auto; }

This will make any image render with an auto width and height. This usually means the browser will render the image at 0x0px before the image has downloaded

3. Placeholders

Some JavaScript based lazy loading scripts use placeholders. If you use some sort of CSS trick like the above mentioned max-width:100% and height:auto then the autoheight of the square placeholder will not match the height attribute of the image. Basically the image will first render with the square placeholder at 720x720 and when the final image has downloaded it will render at 720*180

<img 
  src="1x1placeholder.png" 
  data-src="hero.png" 
  width="720" 
  height="180"
  style="height:auto;max-width:100%"
>


Step 5: Protect the main thread

The next thing to make sure is that not too many images get decoded on the main thread at the same time. Usually this is not going to be a problem but I have seen it happen many times on product listing pages (where sometimes as many as 500 images compete for resources!).

The trick is to add decoding="async" to all image to make sure these images can be decoded on a seperate thread. Also try to avoid this many image decoded all at once by adding loading="lazy" to all below the fold and hidden images.

Step 6: Choose the right strategy for each image!

The final, and sometimes most important, step is to prioritize important images image and de-prioritize unimportant images!

Image strategies for the LCP element

The LCP element is usually the most important visual element. So we need to really prioritize this one."

  1. Preload the image early in the head of the page with this code: <link rel="preload" as="image" href="path-to-img.png">
  2. Tell the browser that this image should not be lazy loaded by setting loading="eager" or by omitting the loading attribute
  3. Hint to the browser that this image should be downloaded with a high priority by using fetchpriority="high" (if you are preloading the image, you can skip this part)
  4. Set decoding="sync" since this element is so important that we want to decode it on the main thread

Image strategy for logo's and other visible (non-LCP) images

Visible images should be loaded pretty soon during page load but preferably after the LCP element. We can achieve this by preloading the LCP element. That will give these visible images their natural, correct download order.

  1. Tell the browser that this image should not be lazy loaded by setting loading="eager" or by omitting the loading attribute
  2. Set decoding="async" since this element can be safely decoded off the main thread!

Image strategy for below-the-fold images

All below-the-fold images should be lazy loaded. It is that simple! There are no exceptions!

  1. Tell the browser that this image should be lazy loaded by setting loading="lazy"
  2. Set decoding="async" since this element can be safely decoded off the main thread!

Avoid background images

If you are using background images you need to reconsider. Background images cannot be lazy loaded and you cannot control the decoding property and you cannot set the fetchpriority. Background images are usually not responsive which will probably cost you a lot of bandwidth. But most important background images are usually discovered after the browser has downloaded the CSS files. This is almost never the right time to trigger an image download!

You can use normal image tags in combination with the CSS object-fit:cover to make normal images behave as 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?"

Optimize images for Core Web VitalsCore Web Vitals Optimize images for Core Web Vitals