Optimising LCP | Harry Roberts
Harry has created an absolutely phenomenal talk here that provides an immense amount of depth whilst still being completely accessible to someone like me who largely doesn't deal with the technical side of web performance.
If you're trying to wrap your head around LCP, this is the talk you need to cover. There are also slides available here and what appears to be a blog post of additional content on Harry's site here, both of which supplement the video well. My own notes are below.
What is LCP?
It's the time it takes for the largest piece of content on a page to finish rendering on screen. Though not all elements are counted. Those that are include text blocks, headings, and images (including background images). Videos are partially included (more details below). Critically, this means that the LCP element may not be considered the focal point, the most useful, or even the most relevant piece of content on your page; context irrelevant, all that matters is size. (This feels very gameable. I wonder if you hide a large white background image or something, if that would trick it into always using the wrong element.)
It's a proxy for how fast was the site, visually.
What does "good" look like?
Google's LCP measurements regard 2.5s as "good". This is apparently ambitious (only around 10% of sites achieve this). It's also a moveable threshold; Google's own definition is clear that the threshold can change and Harry reckons it is likely to do so.
Why bother about it at all?
Google uses it to rank pages, but SEO is far from the only valid reason.
I was working with a client two months ago, and we worked out that a 500ms improvement in LCP would be worth an extra €11.5 million a year to them. This is staggering!
The better your overall sight speed, the better the customer experience, and the more likely you will get meaningful business engagement from the customer as a result.
What elements are used?
<img>
;<image>
within<svg>
;- block-level elements that contain text nodes (e.g. paragraphs and headings);
<video>
elements that have aposter
image (only the loading of theposter
image is actually counted);- And any element with a background image loaded via
url()
(as opposed to background gradients, for instance).
How to improve LCP?
You can't really optimise LCP in its own right; it's a culmination of several other preceding steps.
Your best bet is to optimise the steps that happen first. Optimise these steps and LCP will naturally improve.
The biggest red flag is a bad Time To First Byte. If your first byte takes several seconds, then you cannot have a good LCP.
The time delta between TTFB and LCP is normally your critical path... Very crudely, but it's a good proxy.
Note: If there's a gap between First Paint and First Contentful Paint then that's likely a web fonts issue, and this can also really hurt LCP.
The biggest thing is focus on the element used.
Which is faster? Well, testing shows that text nodes are inherently the fastest option. SVGs appear to be faster than images or videos, which are about the same, and background images are the worst. But the SVG being faster is a bug in Chrome's reporting, and should actually be behind standard images and videos. So:
text > image > video > svg > background image
Also, all of the above assumes raw HTML.
We love HTML! HTML is super fast. Giving this to a browser is a good idea.
Why are some elements faster?
The big issue is that <image>
elements in <svg>
are hidden from the browser's preload scanner, which means they cannot be requested until most scripts have been run (Harry provides a whole technical reason for how browser engines work, but this is the tl;dr version).
Video is the inverse; the poster
attribute is available to the preload scanner and therefore downloads in parallel with other resources, allowing the LCP to be much faster.
Note: there are rumours that <video>
element handling may change in the near future for LCP calculations. Currently, a video without a poster
attribute is just ignored, so you can have a fullscreen video playing and LCP will likely pick some floated text or a navigation element instead (fast). If the suggested changes occur, this will switch to be the first frame of the video, which will be slow. Hopefully, they keep the poster attribute caveat alongside any changes, at which point using poster will be massively beneficial for LCP (you will always download an image quicker than a video).
Background image will always be slow. Avoid them as much as possible!
External stylesheets, CSS-in-JS, even inline styles are all partially blocked. All CSS is hidden from the preload scanner, so any images will be downloaded towards the end of the waterfall.
This is a necessary part of CSS. You can have thousands of background images in a stylesheet, but the browser only wants to download the ones it needs, so it waits until the DOM is fully calculated to parse those styles (even if they are inline).
Overall, this massively helps with web performance, but it hurts LCP significantly, because it shunts your final paint to the very end of the rendering process.
Avoid background images above the fold, try to use simple <img>
and text nodes instead. (I'm interested that <picture>
isn't mentioned.)
Common mistakes to avoid
DOM and browser APIs
Never lazy load anything above the fold! It slows the page load down significantly. And if you do need to do this (though you really don't), please don't use JavaScript-based lazy loading 🤦♂️
In particular, lazy loading hides the image from the preload scanner. Again, this is super beneficial, so long as you aren't using lazy loading when you shouldn't be. This means that a natively lazy loaded image will still be much slower, even though the browser technically "ignores" that part of the HTML, because none of the resources are preloaded.
Similarly, avoid overusing preload
on linked resources. Try to avoid using it for anything that is already directly linked from the URL (e.g. a src
attribute), but you can (and should) use it for things you want to pull in earlier than they otherwise would be, such as background images that you know you will need for LCP.
The more you preload, though, the more bandwidth is split between multiple other resources, so the slower this benefit will get. There be dragons with preload in general too, so read up on it first (and rely on it last). And don't overuse it:
If you make everything important, then you have made nothing important.
Same goes for fetch priority levels/hints. All images are requested with low priority, but once the browser knows it's rendered in the viewport, it will upgrade it to high priority. Adding fetchpriority="high"
to certain images can therefore help to tell the browser "hey, this will always be in the viewport on load" and skip that internal logic.
More recently, the decoding
attribute can be used to make images decode synchronously, which is faster because it further prioritises things in the rendering order (needs testing per website though).
Content woes
Avoid using JavaScript wherever possible, but in particular for rendering your LCP. If the LCP element is only being built and rendered on the client, it will be blocked by the execution time of that script and then all of the normal DOM rendering.
Make sure your LCP candidate is right there, in HTML, ready for discovery. HTML is fast!
Don't host your LCP on a third-party site. Even hyper-optimised CDNs will massively hurt (even with significant compression enabled) purely because the round trip to another service will always be slower than self-hosted.
As an example, Harry moved his homepage image (LCP element) onto Cloudinary, and saw a 2.6x increase in LCP, even though the file size was halved.
Always self host your static assets. Never host them on someone else's origin. There is never any performance benefit to doing that.
Be very careful about dynamic content, and late-loaded JavaScript elements such as cookie banners.
Dynamic content can fundamentally change the LCP element. If you have a large title on the page, and then change this to have fewer characters or words, your LCP might jump to a different, less optimal element, such as an image.
Or even worse, if that new element is now a cookie banner or something later in the waterfall, you can hammer your LCP.