Core Web Vitals LCP Image How to
Optimizing The Image Element LCP
Largest Contentful Paint is especially tricky to optimize when the LCP element is an image. Learn some of the best practices to integrate your LCP image.
Eloïse Martin February 16, 2023 · 16 min read
Largest Contentful Paint (or LCP) is one of three metrics of the Core Web Vitals. These metrics are used by Google to evaluate the quality of user experience. LCP measures the time it takes for the browser to load the main content in the viewport.
Since recently becoming a ranking factor, this major web KPI is still a new concept for many web developers – and becomes tricky to optimize when the LCP element is an image. Although there are some solutions to apply best practices to all the below-the-fold images, the LCP image requires special treatment.
To address this complexity, I’ll provide an overview of the best practices for the integration and optimization of an LCP image. I’ll cover the following:
- How to improve the LCP Resource load time subpart with the help of the
<img>
tag and a focus on proper LCP image sizing. - Some explanations on the browsers’ behavior for this
<img>
tag and its attributes, so you can really understand and integrate your LCP image correctly. - How to improve LCP Resource load delay subpart.
All the explanations of the article will be illustrated by an example that we will iterate on. To save time on the creation of the image variants and their CDN delivery, I will integrate them with the TwicPics API.
<img>
Tag
Improve LCP Resource Load Time With The Help Of The The goal here is to decrease the loading time of your LCP image as much as possible without compromising its visual quality. To succeed in this operation, one of the key points is to let the browser choose the best-suited version of the image.
Instead of declaring a single image file via the src
attribute — whose dimensions would be identical for all users across any devices — we need to let the browser choose the image file best suited to each context. This approach allows images with smaller dimensions to be downloaded for users on smaller devices. This way, the image loads faster, and your website will get a better LCP score.
To do this, we need to give the browser the information it needs using the <img>
tag and its srcset
and sizes
attributes:
- The
srcset
attribute points the browser to a list of image files, describing their intrinsic width. - The
sizes
attribute tells the browser the intended display width of the image. It can be combined with CSS media queries to select the appropriate image for the width of the screen.
<picture>
tag could be used instead of
<img>
to allow the browser to load different images depending on device characteristics, such as the width of the viewport or image format compatibility. For simplicity, we will only use the
<img>
tag in our examples.
srcset
attribute of the <img>
tag
The The srcset
attribute of the <img>
tag indicates a list of image files and their intrinsic width. The browser uses this list to choose the image that it thinks is best suited to the users’ context.
This makes the srcset
attribute the first step towards optimizing your LCP image.
<img srcset="" src="" alt="" />
To declare this list of images, it is recommended to follow a mobile-first approach. That means first declaring the smallest image as the default option with the src
attribute and then declaring it again within the srcset
attribute, followed by the larger dimension variants.
The intrinsic width of each image, expressed in pixels, should also be indicated using the unit w
(w
for “width”):
<img
src="https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=300"
srcset="
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=300 300w,
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=900 900w,
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=1800 1800w
"
alt="Photo by Marek Piwnicki on Unsplash"
/>
Our example above defines three images separated by a comma, with respective intrinsic widths of 300, 900, and 1800 pixels:
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=300
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=900
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=1800
To respect the mobile-first approach, https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=300
is the default image here, defined in the src
and srcset
attributes.
srcset
attribute, it is important to keep the
src
attribute, so the browser knows which is the default image and to ensure that it is displayed on browsers that do not support the
srcset
attribute.
srcset
attribute by declaring the Device Pixel Ratio (DPR) of images rather than their intrinsic width; however, this approach will not be discussed here.
Knowing the user’s context, the browser can now choose the image to load from the list declared in the srcset
attribute.
However, in order to avoid unexpected results, we need to understand how the browser selects the best-suited image:
- Within the
srcset
attribute, the browser always chooses the image according to the viewport size; it ignores the image display dimensions. Consequently, your CSS styles and media queries do not impact its choice. You will also have to keep in mind the landscape orientation of mobiles and tablets when defining the list of images. - By default (without the
size
attribute being specified), the browser chooses the image, assuming it is intended to occupy 100% of the viewport width. - As the browser knows the resolution of the devices, it also adjusts for the Device Pixel Ratio (DPR) in its calculations. Therefore, to optimize the LCP image, it will also be necessary to define a variant for each DPR.
Based on these behaviors, the default formula applied by the browser to choose the image according to the viewport width is:
viewportWidth x 100% x DPR
Let’s illustrate the browser’s behavior with an example using our previous code:
<img
src="https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=300"
srcset="
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=300 300w,
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=900 900w,
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=1800 1800w
"
alt="Photo by Marek Piwnicki on Unsplash"
/>
👉 See live: Codepen demo 1.
Network > Img
tabs to see the image chosen by the browser according to each viewport and DPR.
The image in this demo is displayed at a fixed width of 280px
on all devices. But on a 900px
wide screen with DPR 1
, the browser will search the srcset
attribute image list for an image that is:
900px (viewport width) x 100% (viewport width occupied by the image by default) x 1 (DPR)
In this case, the browser is looking for an image 900px
wide.
In our example, while the display width of the image is 280px
, the image with the intrinsic width of 900px
will be loaded anyway.
We can see from this example that the images declared within our srcset
attribute are not adapted to the display width of 280px on all devices.
While the browser applies the formula viewportWidth x 100% x DPR
to pick the image to load, we can use a more straightforward formula to generate the different images declared in the srcset
attribute: imageRenderedSize x DPR
Taking into account DPRs 1, 2, and 3 on the first demo, we would need to create and define the following three images:
- DPR 1:
280 x 1
=> an image280
pixels wide; - DPR 2:
280 x 2
=> an image560
pixels wide; - DPR 3:
280 x 3
=> an image840
pixels wide.
And finally, update our code:
<img
src="https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=280"
srcset="
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=280 280w,
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=560 560w,
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=840 840w
"
alt="Photo by Marek Piwnicki on Unsplash"
/>
👉 See live: Codepen demo 2.
Still, the updated code is not fully optimized for all devices yet.
In fact, for a laptop device with a viewport of 1024px
and DPR 2
, the optimal image should be an intrinsic width of 560px
(imageRenderedSize x DPR
= 280 x 2
= 560
). But with this code, the browser will want to load an image of 1024 x 100% x 2
, i.e., an image of 2048px
, which does not exist in the srcset
attribute.
Let’s see how the sizes
attribute can help us solve this issue.
sizes
attribute of the <img>
tag
The The sizes
attribute gives developers additional control over the browser’s behavior, preventing the browser from assuming images occupy 100% of the viewport. This helps to ensure the optimal image will be loaded and, consequently, optimize your LCP metric.
<img srcset="" sizes="" src="" alt="" />
sizes
attribute is therefore not necessary when the image actually occupies 100% of the viewport.
To assign the value of the sizes
attribute, we can use and combine the following approaches:
- Define the image width as a percentage of the viewport width, e.g.,
sizes="50vw"
, when the image is responsive (for example, when the CSSwidth
property is defined as a percentage). - Define the width of the image in pixels, e.g.,
sizes="500px"
, when it has a fixed display width regardless of the users’ devices. - Use one or more media queries to allow the browser to make the best choice of an image according to different viewports, e.g.,
sizes="(min-width: 1024px) 800px, (min-width: 768px) 80vw, 100vw"
(with100vw
being the default when no media query is applied).
<img
src="https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=280"
srcset="
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=280 280w,
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=480 480w,
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=560 560w,
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=840 840w,
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=960 960w,
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=1440 1440w
"
sizes="(min-width: 768px) 480px, 87.5vw"
alt="Photo by Marek Piwnicki on Unsplash"
/>
(imageRenderedSize / viewportWidth) x 100
, e.g.,
(280 / 320) x 100
=
87.5%
of the viewport width (
87.5vw
).
👉 See live: Codepen demo 3.
Now, with the source code of the third demo, and regardless of the DPR, the browser can load the perfect image:
- For a display width of
280px
for a viewport of320px
(87.5%
of the viewport). - And for a display width of
480px
on all devices with a viewport of at least768px
.
At this stage, if the integration of the LCP image has been correctly carried out thanks to the <img>
tag and its srcset
and sizes
attributes, the optimization of LCP Resource load time should be optimal.
<img>
tag, remember that this would not be sufficient if your page contains render-blocking resources such as JavaScript files. Refer to
this talk
by Philip Walton to see how you can eliminate unnecessary element render delay.
Now, let’s see how we can further optimize the LCP score by reducing its loading time.
Improve LCP Resource Load Delay Subpart
In this section, we will complete the code example used earlier to see how to load the LCP image as soon as possible.
Don't lazy-load the LCP image element
When users browse the internet to visit a website, the LCP image’s loading is often delayed by the lazy loading technique, whether applied by native lazy loading or a JavaScript library. But, it’s important to note that this loading delay is included in calculating the LCP time.
For instance, with a lazy-loading JavaScript library, the script must first be loaded in order to load the final image. This additional step contributes to a significant delay in loading the LCP resource.
In other words, lazy loading an LCP image penalizes the LCP score of the web page.
Similarly, progressive loading or Low-Quality Image Placeholder (or LQIP) is not considered for LCP by Google at the time of this writing. However, discussions are ongoing regarding LQIP, so the situation may change in the future.
Use priority hints
We have just seen that the LCP image must not be lazy-loaded. But how can we prioritize its loading?
To manage the resources of a page, the browser natively applies a loading order according to a priority defined, among other parameters, by the type of the resource.
Images have a lower priority than render-blocking resources. In addition to meaning the resource is fetched with a lower priority, it also means the fetch is actually delayed, as browsers deliberately delay “low” resources initially so that they can concentrate on more critical, often render-blocking, resources.
This means you need to change the browser’s default load order and its behavior towards hero images so that the LCP image is not delayed and can be loaded as soon as the HTML document is received.
It is the fetchpriority="high"
attribute that allows us to modify this loading priority.
<img fetchpriority="high" src="" alt="" />
Let’s update our code example. Simply add the attribute to the <img>
tag, and the browser will automatically determine the right version of the image for prioritizing based on the sources declared in the srcset
attribute:
<img
fetchpriority="high"
src="https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=280"
srcset="
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=280 280w,
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=480 480w,
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=560 560w,
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=840 840w,
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=960 960w,
https://demo.twic.pics/marek-piwnicki-qZbvW30BOkw-unsplash.jpg?twic=v1/resize=1440 1440w
"
sizes="(min-width: 768px) 480px, 87.5vw"
alt="Photo by Marek Piwnicki on Unsplash"
/>
👉 See the final demo.
fetchpriority
attribute also works on the
<img>
tag of the
<picture>
element. For now, it’s mainly compatible with Chromium but
should be supported by Firefox in early 2023
. However, note that you can still use the
fetchpriority
attribute without any downsides: the attribute will be simply ignored by browsers that do not support it.
As shown in our demo, the <img>
tag and its src
and srcset
attributes are present in the initial HTML source code. This means there is no need to preload the LCP resource here, as the browser’s preload scanner can discover it.
href
attribute on the
<link>
element so that non-supporting browsers don’t request a useless image.
Read this article
to learn more about this topic and see when you might need to preload your LCP resource.
In this last demo, not only can the browser now know the optimal image required for devices with a viewport of 320px
and for devices with a viewport of at least 768px
, but it can also prioritize its loading compared to other resources. As developers, all the points illustrated in this article should therefore help you improve the LCP score of your website.
Conclusion
In conclusion, here is a summary of the steps required to optimize the LCP image:
- Once the page template for your website has been defined, create and store all the images needed for each device and each DPR based on the
imageRenderedSize x DPR
formula. To simplify this step, you can use the TwicPics API to generate multiple versions at different dimensions from the high-quality original image. - Use the
srcset
attribute of the<img>
tag to include the list of images defined in step 1, following a mobile-first approach (remembering to put the default image within thesrc
attribute). - Using the
sizes
attribute of the<img>
tag, combined with the media queries as needed, tell the browser the display width the image would occupy according to the different device contexts:- If the image is responsive, define its width as a percentage of the viewport width using the formula
(imageRenderedSize / viewportWidth) x 100
. - If the image is fixed for a range of viewport widths, simply define its width in pixels.
- If the image is responsive, define its width as a percentage of the viewport width using the formula
- To further improve your LCP score, use priority hints with the attribute
fetchpriority="high"
to prioritize the loading of the LCP image. And remember not to lazy load your LCP image resource :)
Finally, if you use a JavaScript framework and don't want to struggle to manage the picture
complexity manually, you can use the TwicPicture
component to simplify the process. Learn more about it in this post.