Written by Harry Roberts on CSS Wizardry.
Desk of Contents
Largest Contentful Paint (LCP) is my favorite Core Net
Important. It’s the best to optimise, and it’s the one one of many three that
works the very same within the lab because it does within the subject (don’t even get me
began on this…). But, surprisingly, it’s the least optimised CWV in CrUX—at
the time of writing, solely half of origins within the dataset had a Good LCP:
As soon as extra, we noticed an
enhance within the variety of origins having good Core Net Vitals (CWV) pushed by
improved good CLS.52.7% of origins had good LCP
94.9% of origins had
good FID
70.6% of origins had good CLS
39.0% of origins had good LCP, FID,
and CLS— Chrome UX Report 📊 (@ChromeUXReport) 8 March
2022
This genuinely surprises me, as a result of LCP is the best metric to enhance. So,
on this submit, I wish to go deep and present you some fascinating methods and
optimisations, in addition to some pitfalls and bugs, beginning with some quite simple
ideas.
Let’s go.
Remedy All the things Beforehand
Let’s begin with the simple stuff. LCP is a milestone timing—it measures…
…the render time of the most important picture or textual content block seen inside the
viewport, relative to when the web page first began loading.
The necessary factor to notice right here is that Google doesn’t care the way you get to LCP,
so long as you get there quick. There are a variety of different issues that might occur
between the beginning of the web page load lifecycle and its LCP. These embrace (however are
not restricted to):
- DNS, TCP, TLS negotiation
- Redirects
- TTFB
- First Paint
- First Contentful Paint
If any of those are gradual, you’re already on the again foot, they usually’re going to
have a knock-on impact in your LCP. The metrics above don’t matter in and of
themselves, nevertheless it’s going to assist your LCP if you will get them as little as
attainable.

is an unimaginable device for getting timings knowledge from CrUX.
An analogy I take advantage of with non-technical stakeholders goes a bit of like this:
It’s essential to get the children to high school for 08:30. That’s all the college cares
about—that the children are there on time. You are able to do loads to assist make this
occur: put together their garments the night time earlier than; put together their lunches the night time
earlier than (do the identical for your self). Set acceptable alarms. Have a morning
routine that everybody follows. Go away the home with loads of time to spare.
Plan in appropriate buffer time for visitors points, and many others.
The varsity doesn’t care when you laid out uniforms the night time earlier than. You’re being
judged in your capability to get the children to high school on time; it’s simply frequent sense
to do as a lot as you possibly can to make that occur.
Similar together with your LCP. Google doesn’t (at the moment) care about your TTFB, however an excellent
TTFB goes to assist get nearer to an excellent LCP.
Optimise the complete chain. Be sure you get all the things beforehand as quick as
attainable so that you just’re arrange for achievement.
Optimise Your LCP Candidate
A tip that hopefully doesn’t want me to enter any actual element: if in case you have an
image-based LCP, ensure that it’s nicely optimised—appropriate format, appropriately
sized, sensibly compressed, and many others. Don’t have a 3MB TIFF as your LCP candidate.
Keep away from Picture-Primarily based LCPs
This isn’t going to work for lots, if not most, websites. However the easiest way to get
a quick LCP is to make sure that your LCP is text-based. This, in impact, makes your
FCP and LCP synonymous. That’s it. So simple as that. If attainable, keep away from
image-based LCP candidates and decide as a substitute for textual LCPs.
The possibilities are, nevertheless, that gained’t give you the results you want. Let’s take a look at our different
choices.
Use the Finest Candidate
Okay. Now we’re moving into the enjoyable stuff. Let’s take a look at which LCP candidates
we’ve, and whether or not there are any relative deserves to every.
There are a number of potential candidates in your LCP. Taken straight from
internet.dev’s Largest Contentful Paint (LCP) web page, these
are:
<img>
parts<picture>
parts inside an<svg>
component<video>
parts (the poster picture is used)- A component with a background picture loaded by way of the
url()
perform (as
against a CSS
gradient) - Block-level
parts containing textual content nodes or different inline-level textual content parts kids.
Demos
For the needs of this text, I constructed a sequence of decreased demos exhibiting how
every of the LCP sorts behave. Every of the demos additionally incorporates a reference to
a blocking in-<head>
JavaScript file so as to:
- exaggerate the waterfalls, and;
- stall the parser to see if or how every LCP kind is impacted by the preload
scanner.
It’s additionally value noting that every demo may be very stripped again, and doesn’t
essentially symbolize real looking situations wherein many responses can be
in-flight on the identical time. As soon as we run into useful resource rivalry, LCP
candidates’ discovery may match in a different way to what’s exhibited in these decreased
check circumstances. In circumstances like these, we would look to Precedence
Hints or
Preload to assist. All I’m
interested by proper now could be inherent variations in how browsers deal with sure
sources.
The preliminary demos will be discovered at:
The WebPageTest
comparability
is on the market so that you can look by way of, although we’ll decide aside particular person
waterfalls later within the article. That each one comes out wanting like this:

<picture>
in<svg>
: extra on this later. (View full measurement.)<img>
and poster
are equivalent in LCP; <picture>
in <svg>
is the
subsequent quickest, though there’s a bug within the LCP time that Chrome stories;
background-image
-based LCPs are notably the slowest.

component. That is mounted in model 102.
As we will see, not all candidates are born equal. Let’s take a look at every in
extra element.
<img>
Components

Of the image-based LCPs, that is in all probability our favorite. <img>
parts, as
lengthy as we don’t mess issues up, are fast to be found by the preload
scanner,
and as such, will be requested in parallel to previous—even blocking—sources.
<image>
and <supply />
It’s value noting that the <image>
component behaves the identical manner because the <img
component. For this reason it is advisable to write a lot verbose syntax in your
/>
srcset
and sizes
attributes: the concept is that you just give the browser sufficient
details about the picture that it might probably request the related file by way of the
preload scanner
and never have to attend till format. (Though, I suppose—technically—there have to be
like just a few milliseconds compute overhead understanding which mixture of
<supply />
, srcset
, sizes
to make use of, however that shall be mooted fairly shortly
by nearly another transferring half alongside the way in which.)
<picture>
in <svg>
<picture>
parts outlined in <svg>
s show two very fascinating behaviours.
The primary of which is a straightforward bug wherein Chrome misreports the LCP candidate,
seemingly overlooking the <picture>
completely. Relying in your context, this
might imply far more beneficial and optimistic LCP scores.

the reported LCP comes again as not-the
<picture>
component. Inour demo, it’s truly flagged as being the a lot smaller
<p>
component.As soon as the repair rolls out in M102 (which is Canary on the time of writing, and can
attain Steady on 24 Might, 2022), we
can count on correct measurements. This does imply that you could be expertise
degraded LCP scores in your website.

Due to the present reporting bug, <picture>
in <svg>
is more likely to go from
being (inadvertently) one of many quickest LCP sorts, to one of many slowest. In
the unlikely occasion that you’re utilizing <picture>
in <svg>
, it’s in all probability
one thing that you just wish to verify on sooner somewhat than later—your scores are
more likely to change.
The bug pertains solely to reported LCP candidate, and doesn’t affect how the
browser truly offers with the sources. To that finish, waterfalls in all Chrome
variations look equivalent, and networking/scheduling behaviour stays unchanged.
Which brings me onto the second fascinating factor I noticed with <picture>
in
<svg>
:

<picture>
parts outlined in <svg>
s look like hidden from the preload
scanner: that’s to say, the href
attribute just isn’t parsed till the browser’s
major parser encounters it. I can solely guess that that is just because the
preload scanner is constructed to scan HTML and never SVG, and that that is by design
somewhat than an oversight. Maybe an optimisation that Chrome might make is to
preload scan embedded SVG in HTML…? However I’m positive that’s far more simply mentioned
than finished…
<video>
Components’ poster
Attribute
I’m pleasantly shocked by the behaviour exhibited by the <video>
’s poster
attribute. It appears to behave identically to the <img />
component, and is
found early by the preload scanner.

Which means poster
LCPs are inherently fairly quick, in order that’s good
information.
The opposite information is that it seems like there’s intent to take the primary body of
a video because the
LCP candidate if no poster
is current. That’s going to be a troublesome LCP to
get below 2.5s, so both don’t have a <video>
LCP in any respect, or be sure you
begin utilizing a poster
picture with it.
background-image: url();

blocked by synchronous JS).
Assets outlined in CSS (mainly something requested by way of the url()
perform) are gradual by
default. The most typical candidates listed here are background photos and internet fonts.
The rationale these sources (on this particular case, background photos) are gradual
is as a result of they aren’t requested till the browser is able to paint the DOM
node that wants them. You possibly can learn extra about that on this Twitter thread:
Easy but vital
factor all builders ought to consider: CSS sources (fonts, background
photos) aren’t requested by your CSS, however by the DOM node that wants them
[Note: slight oversimplification, but the correct way to think about
it.]— Harry Roberts (@csswizardry) 10 September
2021
Which means background-image
LCPs are requested on the final second,
which is way too late. We don’t like background-image
LCPs.
Getting Round background-image
Points
If you happen to at the moment have a website whose LCP is a background-image
, you is perhaps
pondering of refactoring or rebuilding that part proper now. However, fortunately,
there’s a really fast workaround that requires virtually zero effort: let’s
complement the background with a hidden <img />
that the browser can uncover
a lot earlier.
<div type="background-image: url(lcp.jpg)">
<img src="lcp.jpg" alt="" width="0" top="0" type="show: none !necessary;" />
</div>
This little hack permits the preload scanner to choose up the picture, somewhat than
ready till the browser is about to render the <div>
. This got here in 1.058s
quicker than the naive background-image
implementation. You’ll discover that this
waterfall virtually precisely mimics the quickest <img />
choice:

We might additionally preload
this picture, somewhat than utilizing an <img />
component, however
I typically really feel that preload
is a little bit of a code odor and must be averted
if attainable.
Abstract
In abstract:
- text-based LCPs are virtually all the time going to be the quickest;
<img />
andposter
LCPs are good and quick, discoverable by the preload
scanner;<video>
with out aposter
might need its first body thought of as an LCP
candidate in future variations of Chrome;<picture>
in<svg>
is at the moment misreported however is gradual as a result of thehref
is hidden from the preload scanner;background-image
s are gradual by default, due to how CSS works;- we will sidestep this difficulty by including an invisible
<img />
.
- we will sidestep this difficulty by including an invisible
Alright! Now we all know that are the very best candidates, is there anything can
do (or keep away from doing) to ensure we aren’t operating slowly? It turns on the market
are loads of issues that people do which inadvertently maintain again LCP scores.
Don’t Lazy-Load Your LCP
Each time I see this, my coronary heart sinks a bit of. Lazy-loading your LCP is
fully counter-intuitive. Please don’t do it!

Curiously, one of many options of loading="lazy"
is that it hides the
picture in query from the preload
scanner.
Which means, even when the picture is within the viewport, the browser will nonetheless
late-request it. For this reason you possibly can’t safely add loading="lazy"
to all of
your photos and easily hope the browser does (what you suppose is) the proper
factor.
In my exams, lazily loading our picture pushed LCP again to 4.418s: 1.274s slower
than the <img />
variant, and virtually equivalent to the background-image
check.
Don’t Fade-In Your LCP
Predictably, fading in our picture over 500ms pushes our LCP occasion again by 500ms.
Chrome takes the top of the animation interval because the LCP measurement, transferring us
to a 3.767s LCP occasion somewhat than 3.144s.

Keep away from fading in your LCP candidate, whether or not it’s image- or text-based.
Don’t Host Your LCP Off-Web site
The place attainable, we should always all the time self-host our static
property. This
consists of our LCP candidate.
It’s not unusual for website house owners to make use of third-party picture optimisation companies
corresponding to Cloudinary to serve each automated and
dynamically optimised photos: on the fly resizing, format switching,
compression, and many others. Nevertheless, even when considering the efficiency
enhancements of of those companies, the price of heading to a distinct origin
virtually all the time outweighs the advantages. In
testing,
the time spent resolving a brand new origin added 509ms to general time spend
downloading our LCP picture.
By all means, use third occasion companies for non-critical, non-LCP photos, but when
you possibly can, convey your LCP candidate onto the identical origin because the host web page. That’s
precisely what I do for this website.
N.B. Whereas preconnect
could assist a bit of, it’s nonetheless extremely unlikely
to be quicker than not opening a brand new connection in any respect.
Don’t Construct Your LCP on the Shopper
I see this all too usually, and it’s a part of the continued obsession with
JavaScript. Ideally, a browser will obtain your HTML response, and the
reference to the LCP candidate (ideally an <img />
component) shall be proper
there instantly. Nevertheless, when you construct your LCP candidate with JS, the method
is way, far more drawn out.
Constructing your LCP candidate in JS
might vary from a easy JS-based
picture gallery, proper the way in which by way of to a totally client-rendered web page. The beneath
waterfall exhibits the latter:

The primary response is the HTML. What we’d wish to have is an <img />
proper
there within the markup, ready to be found virtually instantly. As a substitute, the
HTML requests a defer
ed framework.js
at entry 12. This, in flip, finally
requests API knowledge concerning the present product, at entry 50. This response incorporates
details about associated product imagery, which is finally put into the
digital DOM as an <img />
, lastly initiating a request for the LCP candidate
at entry 53, nicely over 7s into the web page load lifecycle.
Don’t Usurp Your Personal LCP
This one breaks my coronary heart each time I see it… Don’t late-load any content material that
by accident turns into your LCP candidate. Often, these are issues like cookie
banners or e-newsletter modals that cowl content material and get flagged as a really late
LCP. I mocked up a late-loading modal for our exams, and what’s necessary to
bear in mind is that the rating is correct, simply not what we hope for:

Be sure that your LCP candidate is what you count on it to be. Design modals and
cookie banners and many others. to:
- load instantly, and;
- not truly be your largest piece of content material.
Abstract
Alright. We lined quite a bit there, however the takeaway is fairly easy:
text-based LCPs are the quickest, however unlikely to be attainable for many. Of
the picture primarily based LCP sorts, <img />
and poster
are the quickest.
<picture>
s outlined in <svg>
s are gradual as a result of they’re hidden from the
preload scanner. Past that, there are a number of issues that we have to keep away from:
don’t lazy load your LCP candidate, and don’t construct your LCP in JS.
