Largest Contentful Paint is just one of the new metrics measured under Core Web Vitals as part of Google’s summer algorithm update. In this video we learn a lot more about how to optimise to pass for these new scoring metrics. Don’t forget, if you need help with fixing your website to pass these Core Web Vitals, get in touch with Whitefish Marketing today on 01303 720 288 – we’ve helped to improve page experience for loads of websites. So let’s have a look at your site and see if we can do the same for you. Enjoy the video!

Source: https://www.youtube.com/watch?v=480m72yjZv8

“Have you ever thought about how hard it is to answer, “How long does it take for a webpage to load?”? I mean, seriously, think about it. It’s not as easy as it seems. The first time I did it, I confidently rattled off a couple of options. The document.onload, right? Well, that only fires when everything is done. I mean, if an ad that’s 10 trillion pixels below the article I’m reading took two seconds or 200 seconds to show an image, why would I care? I don’t, not really.

If that final image isn’t blocking me from my reading, document.onload is a pretty meaningless milestone as a user. Well, okay. What about DOMContentLoaded, right? Well, it’s sooner, but it’s too soon. I mean, it’s just when the content’s been downloaded. Images may not have rendered, style sheets may need to lay out the entire page still. I mean, scripts could still have tons of APIs that they have to call before the web app gets close to being useful.

So, we look to more modern options, like First Meaningful Paint. It’s better, for sure, but it’s the first thing that is painted and that’s not necessarily always that useful. I mean, if it’s just a spinner, it doesn’t really represent a loaded page, at least in a meaningful sense. It’s weird, but something that is as important as how fast our sites are loading is kind of complex to figure out. But, lucky for us, a lot of people feel that way.

With Core Web Vitals and Page Experience, we finally have a definitive answer to this question and so much more. That’s why, today, we’re taking a look at improving our largest ContentfulPaint. The issue with the older ways that we use to figure out when a page is loaded, is that we were looking at the wrong abstraction. We were mentally tying vaguely related DOM events to things that were much more nuanced than they could actually be used for.

That’s why I love Page Experience. It’s all about stepping back from the problems and getting an answer to the questions that matter the most to our users, on today’s web. I mean, each of its parts is trying to answer a fundamental and essential question, to make sure that we can give the best user experience possible. With LargestContentfulPaint or LCP, we answer that, “How fast?” Question.

People using our site don’t care when a DOM event is fired, they care about when a page is usable, or rather when they think it’s usable. That’s what LCP gives us, a new browser event to know the point at which most users perceive that a webpage has been loaded. But, before we can discuss improving LCP, let’s make sure that we all understand just what LCP is.

LargestContentfulPaint is admittedly a mouthful, but once we break it down to what its individual parts mean, it is a fairly straightforward idea. The paint and LargestContentfulPaint is referring to a paint event. This is browser terminology, it’s for when pixels on your screen are rendered or painted in. Every time that a pixel changes the colour on your screen, that’s a paint event. Well, maybe. It gets murky, but we’ll talk about that later on.

For now, you can just remember, when a pixel changes, that’s a paint. Just like the other Web Vital events discussed throughout this series, paint events are exposed as performance entries that we track and analyze through the PerformanceObserver browser API. Every time the browser paints, we know about it and understand each and every element on the page has loaded. It’s cool. I mean, it’s very powerful, but it’s overwhelming.

I mean, every paint, every time a pixel is updated, it’s a lot of events, which is why there’s a separate, more useful LargestContentfulPaint event. A Contentful Paint is a paint event that specifically draws the pixels of a handful of DOM elements. Namely, image elements, the image elements used within SVG, elements with the background image, video elements. Block-level elements, as in display block, yeah, those, when they contain text.

In short, Contentful Paint is a paint event that paints content. Kind of makes sense. So, what is the largest Contentful Paint? Well, that’s when the element that uses the most amount of pixels of all the elements on our user screen is painted. Mostly, but again, we’ll talk about the edge cases later on. When we measure LCP, we do so in seconds. It’s the number of seconds between the very first bite that the page has loaded and the final largest Contentful Paint event.

As soon as our users touch pap or interact with our page, that window of time closes. LCP stops being measured. Whatever element had the highest number of seconds between the first byte and when it was painted, is what is reported for the LCP for that URL. Like every other part of Page Experience, every page on your website has its own LCP score. Your homepage may have a less than stellar LCP, but your product or article pages could have fantastic results. Neither page impacts how the others perform on this metric.

All of these results are generated by and collected from people using your site. So, if you see that your page has an LCP of one second, that’s what your real-world users are seeing when they visit. We know what LCP is, but now, you may be wondering what a good or bad LCP result even looks like. Most important thing, I think, to keep in mind, is that LCP is just one part of Page Experience. Which, in turn, is just one of the many, many inputs that search has inside of its secret sauce.

There isn’t really a pass or fail number when we’re talking about these things. It’s just a number that’s used to compare similar sites, in order to figure out which one users may have a better experience with. Think of it like this, if you had two, otherwise identical pages that exist, but one takes twice as long to render and paint, which one would you rather use? I mean the faster one, right?

Knowing all that though, a good goal to aim for is that your largest Contentful Paint happens in less than 2.5 seconds, for at least 75% of the sessions on your page. If that sounds complicated to calculate and track, then I’ve got good news for you. If you’ve been with us throughout this series, you will already know that you can quickly get all of your Web Vitals and Page Experience results on your search console Page Experience report.

If you aren’t already using the search console, you need to sign up for a free account using one of the links below. I highly recommend that you do so. The search console gives you extremely valuable insights into how your webpages and entire website is performing. Once you’re logged in, we can check out the Web Vitals section of the Page Experience reports. Any URLs that are below a 2.5 second goal that we talked about for LCP, will be highlighted for you.

Hopefully, there’s nothing there, but if your sites are anything like mine, there could be a few places where things can be improved at least a little bit. Once we have a specific URL that we want to improve, we can roll up our sleeves and figure out the reason behind our score. We can open up Dev Tools in the browser, turn on Core Web Vitals overlay, load the URL. Huh, that’s weird. Even though we’re just starting, we’re coming up to the first potential issue. See, my LCP results that are showing here are a lot faster than what’s in the search console.

The only LCP score that truly matters is the one in the Page Experience report. This is the exact same information that search is going to get. Remember, this value is coming from your actual users. So, if we’re seeing a discrepancy between our search console report and when we view the site, we may need to tweak our development set up a little bit, to more closely align with the score being recorded inside of the search console.

If you’re working on a more powerful development machine, but your users are on five-year-old phones, you may have a much harder time noticing the problem or finding a solution. Ideally, when you’re working with Page Experience, especially the Web Vitals portion, like LCP, you’re testing on devices that are the same, or at least similar to what most of your users have.

If you aren’t sure what your users have, you can actually usually find this out inside of your site’s analytics. It doesn’t have to be exact device models, just an understanding of what the most common kind of experience is for people interested on that page of your site. Are they using a higher/lower-end device? Is it an older or newer browser? What’s their screen size? That sort of thing.

If you don’t have similar devices available, Chrome Dev Tools lets you set up mid-tier or low-end mobile device emulation, to get a little bit closer to what your users may have. Once we get our LCP reporting close to what is actually in the report, we can use the PerformanceObserver API to get more detailed information about this LCP event. Like exactly which element is taking that long to paint.

By observing the Largest Contentful Paint type, we can iterate over every LCP entry. From there, we can directly check out the element attribute for each and every entry. This will give you the actual live domino that triggered this LCP event. This could be any of the DOM types that we talked about before. Images, videos, even just text. I mean, web fonts can actually frequently inflate your LCP. It’s definitely something to look out for.

Now that we know our page’s LCP score and the elements that’s causing it, we can finally dig into some solutions. We can pretty much reduce all LCP problems down to slow servers, slow network and/or slow code. Whenever I troubleshoot my problem, I like to start from the start and go to the finish. So, we can start with the server. LCP measures from the very first byte that the browser receives, until our users interact with the page. If our server is running slow or just not fully optimized, we are inflating our LCP before we even get a chance to start running our browser code.

We could create an entire series just based off of optimizing a server, so I can’t go into too much depth here. But, we can share some high-level guidance to get you started. You’ll want to reduce your server logic and operations to just what’s essential. Make sure that your CMS or whatever the backend is, is caching pages rather than rebuilding them on every request. Speaking of caching, make sure that your static files, like images, style sheets and scripts are served with long-lived caching headers. This will reduce the number of files your server has to send out over and over again.

Once we’ve verified that our servers are doing their best, we can move on to the next step. The network. Even if our server is turbocharged and our front end is the height of performance, if our network is slow, it will undermine all of that work. That’s why it’s essential to be using a CDN. CDNs, or content delivery networks are services you can get, to serve copies of the content on your server, on their servers.

So, if your server is in San Francisco, but your user is in Lagos, rather than every file having to criss-cross a whole bunch of oceans and continents on every single request, a CDN will copy those files and store them a lot closer to the end user. Less distance means less time spent on the wire, so files load faster, bringing down our LCP.

The most popular CDNs will have data centers located near the vast majority of people in the world, but, make sure that you’re looking at your analytics for your sites, to make sure that the CDN that you choose is the best for them. Of course, the fastest way to fulfill a network request is to just never leave the user’s device. So, using a service worker is a great choice to make our sites load instantly. If you’re unfamiliar, service workers are special JavaScript files that let you intercept and respond to network requests directly within the browser.

Philip Walton, an engineer on the Chrome team had a post on his blog recently about how we lowered the LCP for pages on his site by nearly 80%, by using a service worker with a cash-first strategy. You can read more about this specifically in the links below. Well, much like server optimizations, though, we could create an entire series based off of service workers. So, we can’t get into too much detail in this video, but there is a ton of great information available online. I really, really encourage you to check it out yourself.

If you’ve never used them before, there are tools like Workbox, which is linked in the description below. It gives you a fantastic starting point and pre-written samples that you can start using today. So, our servers are tight, our networks are screaming fast, but our LCP still isn’t perfect. The next place to look would be our own front end code.

We learned that LCP measures only what is on the user screen and stops being reported once the page is interacted with. That means that the element that triggers our LCP will likely be in that initial area shown at the top of your page, when the URL is first loaded. We’ll call that the initial viewport. Our job is to make sure whatever is being rendered in that initial viewport is able to do so as fast as possible.

The first thing we can do to accomplish this is just remove stuff. Remove any scripts and style sheets in the head of our document that isn’t being used on this page. Those can block or slow the browser down while rendering what is actually being used. It’s just eating into our critical LCP budget. Taking this a step further, we could remove an entire network-round trip. Rather than linking separate files, we can directly add the CSS and JavaScript essential to the initial viewport right inside of our head.

This means that the browser does not have to find our CSS, download it, parse it and then lay out the page. It can just jump straight to the layout the millisecond it gets there. It’s a great way to squeeze just a little bit more performance out of each and every page. If we still use scripts on the page, we’re going to be looking into both defer and async. These are attributes that we can add to any script tag. Think of them as signals we can give to the browser, of different ways that they can speed up its rendering.

See, a browser can only do one thing at a time. By default, it will go from the top to the bottom of our code, downloading and parsing everything pretty much one line at a time. If we add the async attribute to our script tag, then we’re telling the browser that we are not going to be relying on any other resource on the page, that it can download the other stuff in the background. It’ll execute as soon as it’s done downloading.

If a script is important to the site and needs to run as soon as possible, the async attribute is a great second choice, after directly injecting the script into the head. If the script needs to be on the page, but can wait a little bit, then I like to use Defer. Scripts that use async may interrupt the browser from rendering other parts of the DOM. They execute the second that they’re finished downloading.

Defer is more polite. It will also tell the browser that it can download and process other stuff on the page, but it won’t interrupt the browser to be executed. It will run after the page has been fully parsed. Defer is a great idea for anything that isn’t critical for our initial viewport, things like libraries, video players or widgets used lower down the page.

Removing extra network downloads is great, but it isn’t always practical. I mean, some remote resources, like images or web fonts, can’t be inlined without really bloating our file size. If any of our assets are hosted on other domains, like those on a CDN, we can jumpstart the work the browser needs to do, by adding DNS prefetch or preload metadata to our page. These are more hints that we can share with the browser to let it do more work simultaneously.

DNS prefetch is a signal saying, “I’m going to need to download content from this domain in the future.” It may seem kind of silly, but it can actually help quite a bit, depending on how our site’s structured. The browsers, they don’t know how to get to every single website in the world. When they look up something new, they use a system called DNS. Normally, as the browser processes our code, it will discover a URL as something that we want to be downloaded. It will use DNS to figure out the IP address of that site’s server and then figure out how to get to it on the internet.

Even though it’s pretty quick normally, it still takes time. Even possibly dozens of trips through space or around the world. Using DNS prefetch, we’re telling the browser to do all that work in the background while processing the page, so that by the time we get to that URL and the code, that DNS has already been resolved, making our site all the faster. That’s not the only work that needs to be done, however.

If we’re following best practices and only loading content over HTTPS, the browser needs to initiate what’s called a handshake to the server, before bytes can be downloaded from it. That handshake is when a few messages get passed back and forth, from the browser to the server and back. To secure any communication that happens between them.

Just like DNS, it happens incredibly fast already, but we can make it all the faster just by adding a preconnect statement to our page. Just like DNS prefetch is letting the server know it’s okay to do that work in the background. Therefore, we can kind of skip past it, once we get to it in the code.

Parallelizing these network tasks is especially helpful on slow networks, or less powerful devices. Two situations that can easily result in our LCP taking longer than that 2.5 second goal. If you have remote content in the initial viewport, DNS prefetch and preconnect can shave even more milliseconds off of our LCP.

A similar concept to DNS prefetch and preconnect is preload. This is yet another piece of metadata that will tell the browser that we can actually download and parse the content, so it’s going to be a lot more resource-intensive. If we use preload on too many things, the browser can actually get bogged down. It can perform even worse than it does without it. Therefore, it should only be used on content that is extremely important, like the stuff in our initial viewport.

We can use preload on scripts, style sheets, images, videos, web fonts. I mean, pretty much anything that can trigger an LCP event. I think the most common cause of bad LCP that I’ve seen has got to be images, which is not surprising. Images make up nearly half of the bytes on the average desktop or mobile page, according to the HTTP Archive. Yet again, image optimizations could be its own series, but there are some key things that you should be ensuring on your page, to make sure that your site is running efficiently as possible.

Number one, use efficient images. Make sure that you’re not shipping images that are way larger than they actually need to be displayed, and that you’re compressing your images down as much as possible. Tools like Squooch.app, linked below, can actually automatically compress the image and check for visual degradation. This allows you to get an even smaller version of an image that is going to be the absolute tiniest thing possible.

Every byte saved on our initial viewport means our LCP can happen that much faster. For images that are not in the initial viewport, We’re going to make sure to add, “loading=”lazy” to them. This is a fairly new attribute that can be added to any image tag, that will tell the browser that we can delay loading these images a little bit because they’re less likely to be seen right away.

This will allow it to free up other resources for more critical content. But, just make sure you’re not adding this to content that could be in your initial viewport, which will deprioritize it and more than likely negatively impact our LCP.

Perhaps, more importantly, is to use modern image formats. Browsers and servers have content negotiation. This is a really cool thing, it basically means that every time a browser sends a request to a server, it also tells the server the type of content that it supports. This is really helpful, because we can use it to know if our users’ browsers can support things like WebP or AVIF. See, those are modern image formats that we can use that can result in images that are up to 90% smaller than traditional JPEGs.

So, when the user’s browser is parsing our code and finds an image, it sends a request to our CDN or server to download that image. At the same time, it also says, “Hey, I support WebP.” Or, “I support AVIF.” On the server, we can check for this information and then, rather than reply with the JPEG or PNG that we would otherwise serve to other browsers, we can serve that modern, smaller file format.

This can have a gigantic impact on our LCP. I mean, it can cut double-digit percentages out of the bytes that are needed to download for our site. If you haven’t already, make sure that you look into shipping modern image formats today.

So, I know I’ve said a couple of times that we’ll talk about that later, well, later is now. Here are some of the gotchas and nuances to keep in mind when you’re exploring LCP. We’ve already gone pretty long in this video, so let’s run through these as fast as possible. Number one, LCP does not look at the largest element on the user’s screen. It looks at the element with the most number of pixels that are visible.

If you have a gigantic element that’s clipped or has its opacity set to zero, then those non-visible pixels aren’t counted towards its size. Number two, in order to make sure that we don’t hurt browser performance when checking our performance, LCP only looks at the initial position and size of an element. If it’s rendered on the screen and then is moved off, it can still count. Likewise, if it renders off the screen and then is animated onto the screen, it will not be counted by LCP.

Number three, since LCP is what your users are actually seeing, you may get some unusually bad LCP scores if a page gets loaded into the background window or tab. See, those pages are going to be loaded much more slowly and lower priority than tabs with the foreground. So, when in doubt, dive into your own analytics to look for patterns that may explain weird results.

Number four, due to the security model on the web, you can’t get LCP information from an iframe on your page, at least by default. As far as the browser is concerned, however, that content still impacts your URLs LCP. That means that if you’re monitoring your LCP via PerformanceObserver, you may actually miss the LCP events that are being reported by the browser. You can avoid this by adding cores to the iframes and having them report their values to the parent iframe, but just don’t have iframes. As little as you can. If you do, keep them out of your initial viewport, I mean, they’re a headache. Try to avoid them, if possible.

Number five, PerformanceObserver does not emit events when you get to the page by the back or forward browser buttons. This is because those pages are cached in the browser and so, the same events in the pipeline aren’t being executed. However, the browser still reports the LCP information for those situations, since, as a user, it’s still a page view. I mean, why would you split those hairs? Edge cases got to love them.

Finally, number six. Not so much an edge case, but more of a super important tip. Do you know you can actually get a sneak preview of your LCP scores? See, while the canonical information only comes out every 28 days, we can keep track of how we’re doing every day, using our own analytics and the performance of the [inaudible 00:20:59] we looked at earlier.

With Google Analytics, for example, we can create a custom event and then add the LCP information to it whenever a visitor comes to our page. This means that we get up-to-the-minute LCP results for any improvements we’re working on, rather than waiting an entire month to know if we even moved the needle.

There is a lot to LCP and all the Web Vitals. I hope that this has been able to shed a little bit of light on it for you, though. You know, they’re absolutely is more to the topic, so please, be sure to share any questions that you may have on Twitter or in the comments sections below. We’ll be sure to answer them for you. Make sure that you also subscribe if you haven’t already. We’ll be back soon to check out the final portion of Page Experience. See you soon.”

Avatar for Chris Surridge
About Chris Surridge

Chris is our marketing director. He comes from a split background in automotive and accountancy marketing, having worked with such brands in the past as Lookers PLC, Mercedes Benz and Vauxhall, as well as working with governing bodies such as ACCA and CIMA in the accounting world.