A deep dive into optimizing LCP

Chrome for Developers
12 May 202229:15

Summary

TLDRIn this video, Philip Walton discusses optimizing Largest Contentful Paint (LCP), a core web vitals metric. He explains the importance of LCP, provides a framework for improving it, and offers practical steps like prioritizing LCP resource loading, reducing render delays, optimizing resource load times, and minimizing Time to First Byte (TTFB).

Takeaways

  • 😀 Largest Contentful Paint (LCP) is a core web vitals metric that measures the time from when a user starts loading a web page until the largest image or text block within the viewport finishes rendering.
  • 🏁 Google recommends aiming for an LCP of 2.5 seconds or less for at least 75% of all page visits to provide a good loading experience.
  • 📊 The 75th percentile of LCP times is crucial, and it represents the value that is 75% or 3/4 of the way through a sorted list of LCP times.
  • 🔍 Improving LCP involves optimizing the experience for enough users so that at least 75% of them are within the good threshold, not just targeting a specific set of users.
  • 🌐 LCP is the core web vitals metric that sites struggle with the most, with only 52.7% of sites meeting the good LCP threshold.
  • 🔍 Optimizing LCP involves breaking it down into smaller, more manageable problems: Time To First Byte (TTFB), LCP resource load delay, LCP resource load time, and element render delay.
  • 🚀 The key to improving LCP is to identify bottlenecks in the loading and rendering process and address them, such as reducing TTFB, optimizing resource load times, and ensuring elements render quickly.
  • đŸ› ïž General best practices for optimizing LCP include prioritizing the loading of the LCP resource, reducing render-blocking resources, optimizing image and font resources, and using CDNs for faster delivery.
  • 🌐 Real-world data from HTTP Archive suggests that resource load delay might be the biggest bottleneck for LCP, indicating a need for better prioritization and loading strategies.
  • 📈 A step-by-step approach to optimizing LCP includes eliminating unnecessary resource load delay, ensuring the LCP element can render as soon as its resource finishes loading, reducing the load time of the LCP resource, and improving Time To First Byte.

Q & A

  • What does LCP stand for and why is it important?

    -LCP stands for Largest Contentful Paint. It is important because it measures the time from when a user starts loading a web page until the largest image or text block within the viewport finishes rendering. Optimizing LCP is crucial for providing a good user experience and is one of the three core web vitals metrics recommended by Google.

  • What is the recommended LCP score and how does it relate to the user experience?

    -Google recommends that developers aim for an LCP of 2.5 seconds or less for at least 75% of all page visits. This means that if 75% of the time your pages can render the largest image or text block within 2.5 seconds, then those pages are classified as providing a good loading experience.

  • How is the 75th percentile of LCP determined and why is it significant?

    -The 75th percentile of LCP is determined by taking the value that is 75% or 3/4 of the way through a sorted list of LCP times from fastest to slowest. It is significant because it represents the LCP time for the majority of users, indicating the overall performance of the page load.

  • What happens if performance optimizations only improve the already fast LCP experiences?

    -If performance optimizations only make the already fast LCP experiences faster, the 75th percentile does not change. To improve the LCP scores at the 75th percentile, the experience for enough users must be improved so that at least 75% of them are within the good threshold.

  • Why do some developers struggle with optimizing LCP?

    -Developers struggle with optimizing LCP because there are many factors to consider when optimizing load performance. Often, the optimizations they try do not work or do not help much, making it difficult to identify what will actually make a difference for their specific site.

  • What are the four main subparts of LCP and how do they contribute to the total LCP time?

    -The four main subparts of LCP are: 1) Time To First Byte (TTFB), which is the time from when the user initiates page loading until the browser receives the first byte of the HTML document response. 2) LCP resource load delay, which is the time between TTFB and when the browser starts loading the resource needed for LCP. 3) The time it takes to load the LCP resource itself. 4) The element render delay, which is the time from when the LCP resource finishes loading until it's rendered on the screen. These subparts collectively add up to the full LCP time.

  • Why is it recommended to focus on optimizing the LCP resource load delay?

    -Optimizing the LCP resource load delay is recommended because it ensures that the LCP resource is prioritized and starts loading as early as possible after the HTML document is received. This can significantly impact the overall LCP time and user experience.

  • What are some general best practices for reducing the resource load time of the LCP element?

    -General best practices for reducing the resource load time include optimizing image and web font files, setting proper caching headers, using a CDN to serve resources closer to users, and potentially using server-side rendering or pre-rendering pages as static files.

  • How can developers use the 80-20 principle in optimizing LCP?

    -The 80-20 principle suggests that about 80% of the time should be spent making network requests needed to render the LCP element, and 20% of the time should be allocated to everything else. This principle helps in identifying opportunities to improve LCP by focusing on the most impactful optimizations first.

  • What is the role of server-side rendering in optimizing LCP?

    -Server-side rendering plays a crucial role in optimizing LCP by allowing the HTML that's delivered to already contain the markup when the browser receives it. This means the browser doesn't have to wait for the JavaScript to finish loading before it can render the images, reducing the element render delay.

Outlines

00:00

🌐 Introduction to LCP and Its Importance

Philip Walton introduces the concept of Largest Contentful Paint (LCP), one of the three core web vitals metrics, which measures the time it takes for the largest image or text block to finish rendering after the user starts loading a web page. He emphasizes the importance of optimizing LCP, aiming for a score of 2.5 seconds or less for at least 75% of all page visits. Walton also explains the concept of the 75th percentile and how it relates to user experience, highlighting the need for developers to focus on improving the loading experience for a broad spectrum of users.

05:01

🔍 Breaking Down LCP into Subparts

The script delves into the components that contribute to LCP, focusing on the HTML document and the resource needed to render the LCP element. It breaks down LCP into four subparts: Time To First Byte (TTFB), LCP resource load delay, the time to load the LCP resource, and the element render delay. The speaker illustrates how each part contributes to the overall LCP and emphasizes the importance of optimizing each subpart to achieve a faster loading experience. The goal is to minimize non-essential delays and prioritize network requests needed for the LCP element.

10:04

📈 Analyzing LCP Subpart Timings

Using data from the HTTP Archive, the script analyzes the distribution of LCP subpart timings across different web pages. It reveals that resource load delay might be the biggest bottleneck for LCP, contrary to the common belief that image load times are the primary issue. The speaker discusses the limitations of lab data and the need for real user data to accurately assess LCP optimization. Despite these limitations, the data suggests that there is room for improvement in optimizing the LCP resource load.

15:10

đŸ› ïž Step-by-Step LCP Optimization Strategy

The script outlines a four-step approach to optimize LCP: 1) Eliminate unnecessary resource load delay by prioritizing the LCP resource, 2) Ensure the LCP element can render as soon as its resource finishes loading, 3) Reduce the resource load time by optimizing images and web fonts, and 4) Deliver the initial HTML document as fast as possible. Each step is designed to address specific bottlenecks and improve the overall loading experience.

20:10

đŸ–Œïž Optimizing a Real-World Web Page

Philip Walton demonstrates the optimization process using a demo page featuring a photo slideshow viewer. He explains how to identify and address issues in the resource load delay and element render delay. Techniques such as using 'preload' or 'priority hints' to start loading the LCP image earlier, and server-side rendering to prevent JavaScript from blocking image rendering, are discussed. The goal is to ensure that the LCP image starts loading as early as possible and renders immediately after loading.

25:12

🌐 Final Steps and Additional Resources

The final part of the script covers the optimization of image formats and sizes, using tools like Squoosh CLI to convert images to more efficient formats like AVIF or WebP. It also discusses the use of the 'picture' element to conditionally load the best image version based on browser capabilities and screen size. The speaker highlights the importance of server-side optimizations, such as proper caching headers, and encourages developers to use real user performance data for effective optimization. Resources for further learning on optimizing LCP are provided.

Mindmap

Keywords

💡Largest Contentful Paint (LCP)

Largest Contentful Paint (LCP) is a core web vitals metric that measures the time from when a user starts loading a web page until the largest image or text block within the viewport finishes rendering. It is crucial for developers to optimize LCP as it significantly impacts user experience. In the video, LCP is emphasized as a key performance indicator, with a recommended target of 2.5 seconds or less for at least 75% of all page visits.

💡75th Percentile

The 75th percentile is a statistical measure that represents the value below which 75% of the data points in a dataset fall. In the context of the video, it is used to evaluate the LCP metric, aiming for 75% of page visits to meet the LCP threshold of 2.5 seconds. This concept is illustrated with an example distribution chart showing how to determine the 75th percentile value from a list of LCP times.

💡Resource Load Delay

Resource Load Delay refers to the time gap between when the Time To First Byte (TTFB) is received and when the browser starts loading the resource needed for LCP. Minimizing this delay is essential for improving LCP, as it directly affects the time taken for the LCP element to start loading. The video discusses strategies like preloading and priority hints to reduce this delay.

💡Time To First Byte (TTFB)

Time To First Byte (TTFB) is the time from when a user initiates the loading of a page until the browser receives the first byte of the HTML document response. TTFB is a critical component of LCP, as it marks the beginning of the page rendering process. The video emphasizes the importance of reducing TTFB to improve overall page load performance.

💡Core Web Vitals

Core Web Vitals are a set of metrics developed by Google to provide a comprehensive way to measure user experience on the web. LCP is one of the three core web vitals, along with Cumulative Layout Shift (CLS) and First Input Delay (FID). The video focuses on LCP but mentions that LCP is the metric where sites struggle the most compared to CLS and FID.

💡Performance Optimization

Performance optimization involves making changes to a website's code, resources, and infrastructure to improve its speed and responsiveness. In the video, performance optimization is discussed in the context of improving LCP by addressing bottlenecks in resource loading, rendering delays, and reducing TTFB.

💡Critical Rendering Path

The Critical Rendering Path refers to the sequence of steps the browser takes to convert HTML, CSS, and JavaScript into rendered content. Optimizing the critical rendering path is essential for improving LCP, as it directly impacts how quickly the LCP element is displayed. The video breaks down LCP into subparts that are part of the critical rendering path.

💡Real User Monitoring (RUM)

Real User Monitoring (RUM) is the practice of collecting performance data from actual users as they interact with a website. RUM is contrasted with lab data in the video, highlighting the importance of using real user data to understand and optimize LCP, as it provides a more accurate representation of user experience.

💡Server-Side Rendering (SSR)

Server-Side Rendering (SSR) is a technique where the server generates the HTML content, including the LCP element, before sending it to the browser. This approach can significantly reduce the element render delay in LCP by allowing the browser to render content as soon as it is received, rather than waiting for client-side JavaScript to execute.

💡Content Delivery Network (CDN)

A Content Delivery Network (CDN) is a network of servers distributed across different geographical locations, designed to deliver content to users from the server closest to them. In the video, CDNs are mentioned as a way to improve TTFB and resource load times by serving content from a location near the user, thereby reducing latency.

Highlights

Largest Contentful Paint (LCP) is one of the three core web vitals metrics, representing the time from when a user starts loading a web page until the largest image or text block within the viewport finishes rendering.

Google recommends aiming for an LCP of 2.5 seconds or less for at least 75% of all page visits to provide a good loading experience.

Understanding the 75th percentile is crucial, as it represents the value at 75% or 3/4 of the way through a sorted list of LCP times.

Performance optimizations that only improve already fast LCP experiences or slightly improve poor experiences will not change the 75th percentile.

To improve LCP scores at the 75th percentile, focus on optimizations that improve the experience for enough users so that at least 75% are within the good threshold.

LCP is the core web vitals metric that sites struggle with the most, with only 52.7% of sites meeting the good LCP threshold.

Optimizing LCP is complex due to the many factors involved in load performance, making it difficult for developers to identify effective optimizations.

Breaking down LCP into smaller, more manageable problems can help in addressing each separately and effectively.

The two most important resources for LCP optimization are the HTML document and the resource needed to render the LCP element.

LCP can be broken down into four subparts: Time To First Byte (TTFB), LCP resource load delay, LCP resource load time, and element render delay.

Understanding the ideal values for each LCP subpart is key to identifying bottlenecks and making effective optimizations.

A well-optimized page should spend about 80% of the time making network requests and 20% of the time on everything else.

Lab data from HTTP Archive suggests that resource load delay might be the biggest bottleneck for LCP on the web, rather than image load times.

A step-by-step approach to optimizing LCP includes eliminating unnecessary resource load delay, ensuring the LCP element can render as soon as its resource finishes loading, reducing the load time of the LCP resource, and delivering the initial HTML document as fast as possible.

Preloading the LCP image or using priority hints can help in starting the load of the LCP resource earlier.

Server-side rendering or pre-rendering pages as static files can help in reducing element render delay by ensuring the LCP element is contained within the HTML response received from the server.

Optimizing image formats and sizes, and using the picture element to conditionally load the best version, can significantly reduce the load time of the LCP resource.

Applying proper caching headers and using a CDN can help in reducing the time to first byte (TTFB), which affects everything that comes after it.

Transcripts

play00:00

[MUSIC PLAYING]

play00:04

PHILIP WALTON: Hey, everyone.

play00:05

I'm Philip Walton.

play00:06

And, today, we're going to be doing a deep dive

play00:08

into optimizing LCP.

play00:11

But, first, I want to quickly recap what LCP is

play00:14

and explain why I think it's so important for developers

play00:17

to fully understand this metric and know

play00:19

how to improve their scores.

play00:21

LCP stands for Largest Contentful Paint.

play00:25

It's one of the three core web vitals metrics.

play00:27

And it represents the time from when

play00:29

the user starts loading a web page until the moment when

play00:32

the largest image or text block within the viewport

play00:35

finishes rendering.

play00:37

At Google, we recommend that developers

play00:39

aim for an LCP of 2.5 seconds or less for at least 75%

play00:44

of all page visits.

play00:46

In other words, if, 75% of the time, your pages

play00:50

can render the largest image or text block within 2.5 seconds,

play00:54

then we would classify those pages

play00:55

as providing a good loading experience.

play00:59

But I know that sometimes that whole 75th percentile

play01:01

bit can be confusing.

play01:03

So let's take a closer look at exactly what that means.

play01:07

Here is an example of distribution

play01:09

of all visits to a particular page sorted in order of LCP

play01:13

times from fastest to slowest.

play01:16

In this chart, each bar represents a single loading

play01:19

experience of a real person visiting this page.

play01:23

On most sites, you'd probably have thousands or millions

play01:25

of bars.

play01:26

But, for the purposes of making it easy to visualize,

play01:29

I'm showing an example page that only has 36 visits.

play01:34

So to get the 75th percentile from a list

play01:36

of values like this one.

play01:38

All you have to do is take the value

play01:40

that is 75%, or 3/4, of the way through the list.

play01:44

In this example of 36 data points,

play01:46

the 75th percentile corresponds to the 27th value

play01:50

in the list, which, here, is just under three seconds.

play01:53

So it's classified as needs improvement.

play01:56

Remember that to be classified as good,

play01:58

an LC value must be 2.5 seconds or less.

play02:02

But, anyway, the main reason I'm showing this

play02:04

to you, this distribution of real user values,

play02:06

is I want you to take a look at what

play02:08

happens if we were to implement a performance optimization that

play02:11

would make all of the already fast LCP experiences even

play02:15

faster.

play02:19

Did you notice that, even though the LCP times improved

play02:21

for these users, the 75th percentile did not change?

play02:25

Similarly, if the site were to improve the poor experiences,

play02:29

so that they were slightly faster though still poor,

play02:32

it would also not change the 75th percentile.

play02:36

If you want to improve your LCP scores at the 75th percentile,

play02:39

the only way to do that is to improve the experience

play02:42

for enough users, so that at least 75% of them

play02:44

are within that good threshold.

play02:46

And, generally speaking, the best way to do that

play02:49

is to make optimizations that improve the experience

play02:51

across the board, not just targeting

play02:53

a specific set of users.

play02:56

Of course, you can make optimizations

play02:58

that target specific users if you notice specific problems.

play03:01

But, in this talk, I'm going to focus on general LCP

play03:04

best practices that apply to all types of situations.

play03:08

So I've covered what LCP is, as well as

play03:10

how you should approach optimizing it

play03:12

for a broad spectrum of users.

play03:14

But another important topic is why

play03:16

I'm focusing on just LCP in this talk today instead

play03:19

of the other core vitals metrics.

play03:21

Well, based on data from the Chrome User Experience Report,

play03:25

of the three core web vitals metrics,

play03:27

LCP is the one that sites struggle with the most.

play03:30

Only 52.7% of sites meet the good LCP threshold

play03:34

compared to much higher rates for CLS and FID.

play03:38

Moreover, LCP is increasing at a slower pace

play03:41

than the other metrics, which also

play03:43

suggests that developers are having more trouble optimizing

play03:46

for it.

play03:47

We know that sites are definitely

play03:48

trying to improve their core web vital scores because we've

play03:51

seen lots of improvement in CLS over the past few months.

play03:54

But, clearly, LCP is giving developers a bit more trouble.

play03:59

So this raises the question, what

play04:00

makes LCP so hard to optimize?

play04:03

I'm sure there are many reasons for this.

play04:05

But I suspect that a big reason is

play04:07

that there are just so many things

play04:09

to think about when optimizing load performance.

play04:12

And I know from talking to developers that, in many cases,

play04:15

they're trying really hard to optimize LCP.

play04:17

But the things that they're trying just aren't working,

play04:20

or they aren't helping very much.

play04:21

They can't figure out what they need

play04:23

to do that will actually make a difference

play04:25

for their specific site.

play04:27

So LCP is a big, complex problem.

play04:30

But I find that when you're facing a big problem that's

play04:32

hard to solve, it's helpful if you first break it down

play04:35

into smaller, more manageable problems

play04:37

and address each of those separately.

play04:39

And I think we can do exactly that with LCP.

play04:43

So in the rest of this talk, I'm going

play04:44

to present a framework for how I recommend

play04:46

the developers approach improving LCP on their sites.

play04:51

OK, here, we have an example of waterfall

play04:53

from a pretty typical page load containing CSS, JavaScript,

play04:57

and image resources.

play04:59

And while all of these network requests

play05:01

are important, in general, for the sake of optimizing LCP,

play05:04

you really only need to be focusing

play05:06

on two, the HTML document and then

play05:09

whatever other resource may be needed

play05:11

to render the LCP element.

play05:13

In this case, the LCP element is an image.

play05:16

But the same principle would apply

play05:17

for a text node that needed to load a web

play05:19

font before it could render.

play05:22

So now that we've identified the two most important resources,

play05:24

we can use the relevant timing attributes of those resources

play05:27

to break down LCP into its most important subparts.

play05:32

The first subpart is the time from when the user initiates

play05:34

loading of the page until when the browser receives

play05:37

the first bite of the HTML document response.

play05:40

This is commonly referred to as Time To First Byte, or TTFB.

play05:45

The reason this time is important

play05:46

because it represents the first moment

play05:48

that the browser is able to start discovering

play05:50

additional resources that are needed to render the page,

play05:53

including the resource needed to render the LCP element,

play05:56

which we'll get to in a bit.

play05:58

The second subpart is the LCP resource load delay.

play06:02

This is the delta between TTFB and when

play06:05

the browser starts loading the resource needed for LCP.

play06:08

In some cases, the LCP element can

play06:10

be rendered without loading any additional resources,

play06:13

like if the LCP element is a text node using a system font.

play06:16

And for those pages, the resource load delay is zero.

play06:20

In general, you want your resource load delay

play06:22

to be as small as possible.

play06:25

The third subpart is the time it takes to load

play06:27

the LCP resource itself.

play06:29

Again, if the LCP element on your page

play06:32

doesn't require a research request,

play06:34

then this time will also be zero.

play06:36

And lastly, the fourth Subpart of LCP

play06:38

is the element render delay.

play06:40

This is the time from the moment your LCP resource finishes

play06:44

loading until it's actually rendered to the screen ever.

play06:47

So every single page can have its LCP value broken down

play06:50

into these four subparts.

play06:52

There's no overlap or gaps in between them.

play06:54

And, collectively, they add up to the full LCP time.

play06:59

To illustrate that point, let's take a look at what

play07:01

happens if we were to reduce the resource load time

play07:03

part in this example, in this case,

play07:11

when we reduce the network load time,

play07:13

the element render delay got extended

play07:14

by the exact same amount of time.

play07:17

The time just shifts from one part to a different part.

play07:20

And so LCP doesn't change.

play07:22

That's because, in this example, the page

play07:24

needs to wait for the JavaScript, those yellow bars

play07:27

at the bottom, to finish loading.

play07:29

Since, here, the JavaScript is responsible for adding the LCP

play07:32

element to the page.

play07:34

So I want to pause here for a moment

play07:36

and really emphasize that last point.

play07:37

Because I think this is where a lot of developers

play07:40

get frustrated.

play07:41

When they search for posts online telling them

play07:43

how to improve their LCP, one of the most common pieces

play07:46

of advice is to optimize their images.

play07:49

But optimizing your images will only

play07:51

affect this one part of LCP.

play07:53

And if this part isn't your bottleneck, then reducing it

play07:56

won't help you improve your score,

play07:58

as demonstrated in this example.

play08:01

So the key to improving LCP is to figure out

play08:04

where your bottlenecks are.

play08:05

And a good way to do that is to understand what

play08:08

the ideal or recommended values are for each of these LCP

play08:11

subparts that I've just introduced.

play08:14

And at a high level, the advice is pretty simple.

play08:17

You want to be spending the bulk of your time making

play08:19

network requests that are needed to render the LCP element.

play08:22

And you want to minimize all other time as much as possible.

play08:27

Anything that gets in the way of your pages

play08:29

starting to load the LCP resource as soon as possible

play08:32

or rendering the LCP element as soon as

play08:34

that resource is done loading is essentially wasted time.

play08:38

So it's important to eliminate those times, if you can.

play08:43

So given that general principle, this

play08:45

is roughly how these LCP subparts should break down

play08:47

on a well-optimized page.

play08:50

The total time in the right column

play08:51

is based on the goal of 2.5 seconds for LCP.

play08:55

And the other times are just a percentage of that.

play08:59

So notice how about 80% of the time

play09:01

is allocated to making network requests.

play09:03

And 20% of the time is allocated to everything else.

play09:07

Later in the talk, I'll walk through an example

play09:09

of a real-world performance optimization.

play09:11

And I'll use this 80-20 principle

play09:13

to identify opportunities to improve.

play09:17

And speaking of opportunities to improv,

play09:19

I bet you're probably curious to know what the breakdown of time

play09:21

spent in each LCP subpart looks like for sites

play09:24

in the real world.

play09:26

So, unfortunately, we don't have real user data

play09:29

for these specific metrics yet.

play09:30

But we do have lab data from HTTP archive.

play09:33

And that can give us some insight

play09:35

into answering this question.

play09:38

Here is a chart that shows the breakdown of LCP subpart

play09:41

timings from 5.3 million web page test runs

play09:45

which is every single page in HTTP Archive where

play09:48

the LCP element was an image with a URL source.

play09:52

To make this chart, I took all of those

play09:54

runs and sorted the results by LCP value

play09:57

from fastest to slowest.

play09:58

And then I broke it down into five buckets

play10:00

where the top bucket represents the fastest 20% of web pages,

play10:04

and the bottom bucket represents the slowest 20%.

play10:07

Within each bucket, I took the average LCP subpart time value

play10:10

to create each stacked bar, and so the total bar length

play10:14

is the average LCP value within that bucket.

play10:17

And while this chart shows the absolute timings

play10:19

for each subpart, and you can visually

play10:21

see how the total time spent in each part

play10:23

gets worse as you move from bucket to bucket,

play10:26

I actually think a more interesting way

play10:27

to visualize the same data is to look

play10:29

at each subpart as a percentage of the total LCP time.

play10:35

So here's what that looks like.

play10:36

And to be honest, when I first look at this data,

play10:39

I was pretty surprised at the results.

play10:41

I was expecting that the majority of the LCP time

play10:43

would be spent loading large unoptimized images.

play10:47

In other words, I was expecting the green bars

play10:49

to be a lot wider, especially in the bottom row.

play10:52

But this data suggests that image load times might not

play10:55

actually be the main bottleneck for LCP on the web.

play10:58

From the amount of purple I'm seeing in these results,

play11:00

it seems like the biggest bottleneck might actually

play11:03

be resource load delay.

play11:05

Now, I want to stress that this is lab data from web page test

play11:09

runs.

play11:09

It's not real user data from the wild.

play11:12

This data uses a single network and device configuration

play11:15

for every run.

play11:16

So it's definitely not representative of the myriad

play11:19

of devices and networks used in the real world.

play11:23

Also, the web page test runs used by HTTP Archive

play11:25

don't contain repeat visits.

play11:27

So things like the user's cache state

play11:29

are not factored into this at all.

play11:32

So I don't think we can say with any certainty

play11:34

that this is exactly how LCP breakdowns look

play11:37

in the real world.

play11:39

But what I think we can say with high confidence

play11:41

is that sites are definitely not optimizing LCP resource load as

play11:45

effectively as they could be.

play11:48

And while that's obviously a bummer for people like me,

play11:50

I do think there's reason to be optimistic about the data.

play11:54

The subparts of LCP that are hardest to improve

play11:57

are represented here by the blue and green segments in the chart

play12:00

above.

play12:01

And the parts of LCP that are easiest to improve,

play12:03

in my opinion, are represented by

play12:05

the purple and yellow segments.

play12:07

Given how much purple and yellow we see in this chart,

play12:10

I'm hopeful that if we can do a better job of helping

play12:12

developers discover where their bottlenecks are,

play12:15

then we should be able to see some big improvements.

play12:19

So I know I shared a lot of information so far,

play12:21

and I haven't really given any advice yet.

play12:23

So let's do that now.

play12:25

Here is a step-by-step approach, a recipe, if you will,

play12:29

for how to optimize LCP on any given page.

play12:32

There are only four steps, and I put them in order

play12:35

with the easiest and most high-impact optimizations

play12:38

first.

play12:40

Step one, eliminate unnecessary resource load delay.

play12:45

The key point in this step is to ensure that the LCP resource is

play12:49

prioritized so we can start loading immediately

play12:52

after the HTML document is received.

play12:55

And the best way to check on your pages,

play12:57

whether the LCP resource is loading early enough,

play13:00

is to compare its request start time

play13:02

with the start time of the first sub

play13:04

resource loaded by the page.

play13:06

In this case, the first sub resource is a style sheet,

play13:09

and it starts loading quite a bit

play13:10

before the LCP image resource starts.

play13:13

So that's a signal that there's opportunity to improve.

play13:16

To fix this we, could use preload or add priority hints

play13:19

to the image tag, and then the browser

play13:21

would know to start loading it earlier.

play13:24

You also want to check to make sure that the LCP image isn't

play13:26

being lazy loaded because that will result

play13:28

in additional resource load delay

play13:30

that you never want for your LCP image.

play13:33

In this case, preloading the image should do the trick,

play13:36

and here's what that looks like after it's been implemented.

play13:39

Now you can see that the image resource has started

play13:41

loading at the same time as the first stylesheet which

play13:44

is exactly what we want.

play13:45

And so step one is pretty much done.

play13:47

But before we move on to the next step,

play13:49

I want to once again point out that reducing the resource load

play13:52

delay here did not change LCP.

play13:54

That is still blocked on the JavaScript code

play13:57

as I mentioned earlier.

play13:59

Step two is to eliminate unnecessary element render

play14:02

delay.

play14:03

In other words, we need to make sure

play14:05

that as soon as the LCP resource finishes loading,

play14:08

nothing else on the page is preventing it

play14:09

from rendering right away.

play14:11

That can be things like render-blocking stylesheets

play14:14

and JavaScript files.

play14:15

It could also be something like an A/B test runner

play14:17

that is intentionally hiding content until it can figure out

play14:19

what experiment the user is in.

play14:22

In our example, one way we could reduce

play14:24

render times is to optimize the size of the JavaScript

play14:27

files we're loading.

play14:28

Techniques like [? mimification ?] and tree

play14:30

shaking can help with this and should

play14:31

reduce the overall script download times.

play14:37

So this is definitely an improvement,

play14:38

but it's still not great.

play14:40

The recipe said to ensure that nothing

play14:42

is blocking rendering after the LCP resource finishes loading.

play14:45

It doesn't just say to reduce the blocking time.

play14:49

In this example, the JavaScript code

play14:50

contains a framework that is client-side

play14:52

rendering the application.

play14:54

So if we update our framework to use server-side rendering

play14:57

or to pre-render these pages as static files,

play15:00

then the JavaScript will still load,

play15:01

but it will no longer be a bottleneck

play15:03

for rendering the LCP image.

play15:09

Ah, that's much better.

play15:11

Now the JavaScript code isn't blocking rendering at all.

play15:14

So the LCP image can render as soon as it's downloaded.

play15:18

Step three in this recipe is to reduce the resource load

play15:21

time as much as possible, and you can do that

play15:24

by following all of the general best practices

play15:26

around optimizing images and web fonts.

play15:29

Anything that you can do to reduce

play15:30

the file size of the resource should reduce its load times.

play15:34

You should also make sure that you're

play15:35

setting the proper caching headers

play15:37

and using a CDN so that you can serve those resources

play15:40

from a location as geographically close

play15:42

to your users as possible.

play15:45

If you reduce load times in this example,

play15:47

the results would look like this.

play15:52

So we're almost there.

play15:53

But as you can see, there's now a stylesheet

play15:55

that's taking a bit longer to download than the LCP image

play15:58

resource.

play15:59

We were actually able to optimize the LCP resource

play16:01

by so much that it's now smaller than the stylesheet which

play16:04

means the stylesheet is blocking rendering

play16:06

until it's done loading.

play16:09

I recommend looking at techniques like critical CSS,

play16:12

or you could find other ways to remove the unused styles

play16:14

wherever possible.

play16:16

Another option is to inline the CSS into the document,

play16:19

but that can have negative performance

play16:21

effects for repeat visitors.

play16:23

My recommendation is not necessarily

play16:25

to remove or in-line the style Sheets

play16:27

but to just reduce them so they're smaller in size

play16:30

than your LCP resource.

play16:32

That should help ensure that it's either not

play16:34

blocking or rarely blocking which

play16:36

is a pretty good compromise.

play16:43

In this case, reducing the size of the stylesheet

play16:45

slightly is enough to prevent it from blocking rendering

play16:48

of the LCP image which is good enough

play16:50

to move on to the final step.

play16:53

Step four is to reduce your time to first byte.

play16:56

This step is saved for last because it's usually

play16:58

the step that developers have the least control over.

play17:01

It's also one of the hardest to optimize.

play17:04

That being said, having a good time to first byte

play17:06

is critical because it affects everything that comes after it.

play17:10

One of the best ways to improve your time to first byte

play17:12

is to use a CDN.

play17:14

Just like with optimizing resource load times,

play17:16

it's important to get your servers as close to your users

play17:19

as possible.

play17:21

Here's what that looks like.

play17:25

As I said before, any improvements to this part

play17:28

will directly affect every other part that follows.

play17:31

That's because nothing can happen on the front end

play17:33

until the back end delivers that first bite of the response.

play17:38

So to recap, here are the four steps in the recipe.

play17:42

Step one, ensure the LCP resource starts

play17:45

loading as early as possible.

play17:47

Step two, ensure the LCP element can

play17:50

render as soon as its resource finishes loading.

play17:54

Step three, reduce the load time of the LCP resource

play17:57

as much as you can without sacrificing quality.

play18:01

And step four, deliver the initial HTML document

play18:04

as fast as possible.

play18:07

If you're able to follow these four steps in your pages,

play18:09

then you should feel confident that you're

play18:11

delivering an optimal loading experience to your users,

play18:14

and you should see that reflected

play18:16

in your real-world LCP scores.

play18:19

So now I want to go through a real-life example

play18:22

of actually applying this to a real web page.

play18:26

So here I have a demo page that I

play18:27

created to mimic a lot of the real-world issues I've

play18:30

seen recently on sites that are trying to optimize their LCP.

play18:33

The demo includes a photo slideshow viewer

play18:35

that consists of a main image as well as several image

play18:38

thumbnails.

play18:39

This type of pattern is common across the web on everything

play18:42

from news sites to e-commerce sites to general landing pages.

play18:46

The demo also loads two web fonts as well as the CSS

play18:49

framework, in this case, Bootstrap,

play18:51

because these types of dependencies

play18:52

are also common on the web, and I wanted the demo

play18:54

to be reasonably realistic.

play18:57

OK, let's head over to the code, and the first thing

play19:00

I want to show you before we start optimizing is I

play19:02

added a file called perf dot js that my demo is loading.

play19:05

This file calculates the four LCP subpart timings

play19:08

and logs them to the console.

play19:10

It also uses the performance dot measure method from the user

play19:13

timing API so that I can easily visualize

play19:15

these timings in DevTools.

play19:17

Let me open up DevTools to the Performance tab

play19:19

and show you a quick trace so you can see what I mean.

play19:24

So as this page loads, I want you

play19:26

to notice that the text comes in first,

play19:28

and then you can see a gap where the photos will go,

play19:30

and then eventually all the photos

play19:32

fade in together once they've all finished loading.

play19:36

Now that the load is done, take a look

play19:37

at the LCP subpart timings here in the timings track.

play19:41

I've staggered these timings so that the TTFB and the resource

play19:44

load time are on the top row, and then

play19:46

the resource load delay and element render delay timings

play19:49

are on the bottom row.

play19:50

Remember that the goal is to be spending about 80%

play19:53

of your time loading the main document and the LCP

play19:55

resource, which are the two timings here on the top row,

play19:58

and then less than 20% of your time in the load or render

play20:01

delay portions which are here on the bottom row.

play20:04

As you can see from this trace, we're spending most of our time

play20:07

in the resource load delay portion which is a problem.

play20:10

So let's fix that.

play20:11

But before we switch to the Code Editor,

play20:13

let's take a closer look at the network waterfall

play20:15

to see what's happening within the resource load delay

play20:17

portion of LCP.

play20:20

As you can see here, we're loading a few fonts

play20:21

and stylesheets.

play20:22

Once that's done, we have some JavaScript files.

play20:25

And once the main dot js file finishes loading,

play20:27

there's an API request for photos dot json.

play20:30

The LCP image doesn't start loading until this API

play20:33

request finishes.

play20:34

So if you want to reduce the resource load delay,

play20:36

then we have to start loading the LCP image resource earlier,

play20:40

and the two methods for doing that

play20:41

are preload or priority hints.

play20:44

But given that the load of this image

play20:45

was not initiated from the HTML, it

play20:47

was initiated from the JavaScript, then

play20:49

that limits our options to really just preload

play20:51

at this point.

play20:53

To preload your LCP image, you can add a link rel preload tag

play20:57

to the head of your HTML document,

play20:59

and you set the href value to your image URL.

play21:02

Also make sure to set the as attribute to image.

play21:06

Now the image resource is discoverable

play21:08

from within the HTML source, so the browser

play21:10

doesn't have to wait for the JavaScript and API request

play21:12

to finish before it can start loading the LCP image.

play21:16

Let's take a look at how that improves things.

play21:25

So you can see that the LCP image now

play21:27

started loading earlier, but it's still not

play21:29

loading as early as the fonts and stylesheets which means

play21:32

we have more work to do here.

play21:34

The reason it's starting later than those other resources

play21:37

is because Chrome is assigning it

play21:38

a low priority which it generally does for images

play21:41

that aren't in the view port.

play21:43

And these images actually aren't in the view port

play21:45

because the JavaScript code that puts them there hasn't run yet.

play21:49

One hack to get Chrome to load this earlier

play21:51

is to move our link rel preload tag

play21:52

above all the other linked tags, but that doesn't really

play21:56

address the priority issue.

play21:57

A better, more semantic option is

play21:59

to use the new priority hints API and just tell the browser

play22:03

that this request should be high priority, which

play22:05

we can do with the fetch priority attribute.

play22:08

Now that we've added that, let's run the trace again

play22:10

to see the difference.

play22:18

As you can see here, the image now

play22:20

starts loading at the same time as the other resources,

play22:22

and it's assigned a high priority.

play22:24

Also notice that the resource load delay segment is now tiny,

play22:27

so our job here is done.

play22:29

Let's move on to step two and address

play22:31

this long element-rendered delay.

play22:34

If you remember from earlier, I said

play22:35

that the image viewer component was built and rendered

play22:38

in JavaScript and that it programmatically waits

play22:40

for all images to finish loading before fading in.

play22:43

When the network is fast, this is a nice UX touch.

play22:46

But when the network is slow, it's

play22:48

painful to wait this long before you see anything.

play22:51

So let's fix that.

play22:52

Most popular JavaScript frameworks

play22:54

today support a feature called server-side rendering which

play22:57

essentially just means that your client-side code can

play22:59

be run on the server so that the HTML that's delivered already

play23:02

has the markup in it when the browser receives it.

play23:05

This is a great solution for pages like this one

play23:07

because it means that the browser doesn't

play23:08

have to wait for the JavaScript to finish loading before it

play23:11

can render the images.

play23:13

Now explaining how to enable service

play23:14

and rendering in your application

play23:16

is a bit outside of the scope of this demo,

play23:18

but we can easily mimic the results

play23:20

of what server-side rendering would give us

play23:21

by just copying the client-side rendered code

play23:24

and pasting it into our index to HTML file.

play23:27

After all, it doesn't really matter what stack we're using.

play23:30

From the browser's point of view, all that's relevant

play23:32

here is that the image element is

play23:34

contained within the HTML response received

play23:36

from the server.

play23:37

An aesthetic file is just as valid of a way

play23:39

to do that as a server-generated response.

play23:44

Now when we reload this page, notice

play23:45

how the images are being rendered as soon as they're

play23:47

loaded rather than waiting for everything to load and render

play23:50

all at once.

play23:52

And if we look at the timing values here in DevTools,

play23:54

you can see that the element render time segment is now

play23:57

also tiny.

play23:58

So let's move on to step three and see

play24:00

what we can do to reduce the load time of the LCP image.

play24:04

If we switch over to the Elements panel

play24:05

and take a closer look at this image,

play24:07

you can see that it's a JPEG which

play24:09

is not the most optimal format we could use here.

play24:12

And it's also loading a file that's

play24:14

1,600 pixels wide despite the fact

play24:16

that the rendered size is only 372 pixels.

play24:20

Which even on a tube DPR display is still more than twice

play24:23

as big as it needs to be.

play24:25

So let's address both of these issues.

play24:27

We can convert these files to a more optimal image

play24:29

format like ABIF or WebP, and we can also

play24:32

create a few different sized versions of each of them.

play24:36

Image conversion tools are going to be your best

play24:38

friend during this step, and in my case,

play24:40

I'm going to use the Squoosh CLI to do the conversion in bulk.

play24:44

If we head over to the terminal, I

play24:46

can resize and convert all of my source images

play24:48

to ABIF with just this one command.

play24:52

And if I repeat this process a few times for each

play24:55

of the formats and sizes I want, then in just a few minutes,

play24:58

I can have optimized versions of each of my images

play25:00

in a variety of formats and sizes.

play25:03

In this case, I created a ABIF, WebP, and JPEG versions of each

play25:08

at 1600 pixels wide to target desktop and 800 pixels wide

play25:12

to target mobile screen sizes.

play25:14

I could create more sizes if I wanted to, but at some point,

play25:17

there's going to be a trade off between file size savings

play25:19

and cache hit rate on your CDN edge nodes.

play25:23

So now that we have multiple versions of each image,

play25:25

let's update our HTML to conditionally load

play25:28

the best version given the user's browser's

play25:30

capabilities and screen size.

play25:32

We can use the picture element to do that.

play25:35

The picture element allows us to list several source options,

play25:38

and then the browser will automatically

play25:39

pick the best option for us.

play25:42

In this case, I'll list the source files

play25:43

for the ABIF version first because those ended up

play25:46

having the smallest file size.

play25:48

Next I'll list the WebP source files,

play25:50

and then finally I'll keep the image tag with the JPEG

play25:53

since JPEG is supported by pretty much every browser.

play25:56

With this change, let's reload our page

play25:58

and take a look at the trace.

play26:11

Notice how now our resource load delay got big again.

play26:15

And if you look at the waterfall above,

play26:16

you can see that the reason is because we didn't update

play26:19

our link rel preload tag to match the image loaded

play26:21

by our picture element.

play26:23

So we're preloading a JPEG but then ultimately rendering

play26:26

an ABIF image which is not good because now we're

play26:29

loading two images.

play26:31

But this raises an interesting question.

play26:33

Is it even possible to conditionally preload

play26:35

the same image that will eventually

play26:36

be used by the picture element?

play26:39

Well, the answer is yes and no.

play26:42

The link rel preload tag does take an image source set

play26:45

attribute which has all the capabilities of the source set

play26:48

attribute on the image tag.

play26:51

But it doesn't allow you to specify

play26:52

multiple different formats each with their own separate source

play26:55

sets like you can with a picture tag.

play26:58

Fortunately, though, priority hints

play27:00

provides a solution here as well.

play27:02

Rather than use the link rel preload tag,

play27:04

we can apply the fetch priority attribute directly

play27:07

to the image tag, and then the browser will automatically

play27:09

determine the right version of the image

play27:11

to prioritize, like this.

play27:15

So not only is it cleaner to do it this way, but in many cases,

play27:18

developers may not have the ability

play27:19

to modify the content of the head tag

play27:22

just on specific pages.

play27:24

But they probably do have the ability

play27:25

to modify their image tag attributes.

play27:29

So now when we reload and look at the trace,

play27:31

you can see that the ABIF version is being prioritized,

play27:34

and it's also the 800 pixel-wide file that's being loaded.

play27:38

At this point, all the remaining optimizations

play27:40

are going to be done on the server side.

play27:42

We'll want to make sure that we're

play27:44

applying the proper caching headers to our images

play27:46

as well as to our HTML document responses

play27:49

to ensure that they can be cached when appropriate.

play27:52

Note that we want to ensure that they're

play27:53

being cached by both the browser, which

play27:55

helps with repeat visits from the same user,

play27:58

but also on the CDN, which helps all users

play28:01

in the same geographic region who may be requesting many

play28:04

of those same resources.

play28:05

In the interest of time, I'm not going

play28:07

to cover those optimizations in this demo,

play28:09

and honestly, any optimizations you're

play28:11

making to your server, CDN, or to your network configurations

play28:15

should be based on real user performance data,

play28:18

not lab-based simulations like we've

play28:19

been looking at here today.

play28:21

So much of your server side, CDN, and network performance

play28:25

depends on factors like cache hit rate

play28:27

and other things that are directly related

play28:29

to user behavior which is why you need real user performance

play28:32

data to effectively optimize them.

play28:36

If you want to learn more about measuring and optimizing

play28:38

performance with real user performance

play28:40

data from the field, head on over to web

play28:42

dot dev where you'll find lots of resources on that topic.

play28:47

So that's it for me today.

play28:48

If you want to learn more, make sure to check out

play28:50

web.dev/optimize-lcp.

play28:53

It goes into a lot more detail on all of the techniques

play28:56

that I covered today.

play28:57

As always, thanks for watching, and happy optimizing.

play29:00

[MUSIC PLAYING]

Rate This
★
★
★
★
★

5.0 / 5 (0 votes)

Étiquettes Connexes
LCP OptimizationWeb VitalsUser ExperiencePerformance MetricsLoad TimesResource PrioritizationImage OptimizationServer-Side RenderingCDN UsageReal User DataPerformance Framework
Besoin d'un résumé en anglais ?