Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Optimising Largest Contentful Paint

Optimising Largest Contentful Paint

Since Google announced their Core Web Vitals (CWV) initiative, being fast is more important than ever. However, despite being by far the easiest CWV to monitor, debug, and optimise—in both the lab and field—Largest Contentful Paint (LCP) is still the one that most websites struggle with.

In this very practical talk, we’ll look at what exactly comprises LCP, how we might be working against ourselves, and how to make opportunistic optimisations to get ourselves back in the green (and beyond).

And even if none of those terms meant anything to you, don’t worry! You’ll leave this talk fully equipped to go back to your own projects and clients and make all the improvements they’ll need. Get ready to ask for a pay rise.

Harry Roberts

October 06, 2022

More Decks by Harry Roberts

Other Decks in Technology


  1. performance.now() – October 2022 Optimising 
 Largest Contentful Paint Harry

    Roberts – @csswizardry
  2. None
  3. What is LCP?

  4. —web.dev/lcp “Largest Contentful Paint (LCP) is an important, user-centric metric

    for measuring perceived load speed because it marks the point in the page load timeline when the page’s main content has likely loaded—a fast LCP helps reassure the user that the page is useful.”
  5. None
  6. None
  7. None
  8. None
  9. What Is ‘Good’?

  10. — web.dev/lcp

  11. — csswz.it/3ybF0NK To confirm that a threshold is achievable, we

    require that at least 10% of origins currently meet the ‘good’ threshold.
  12. Site-Speed Is More Than Just SEO

  13. €11,520,126/yr A 500ms improvement in LCP is worth

  14. Getting to ‘Good’

  15. Solve Everything Beforehand

  16. Solve Everything Beforehand LCP is a milestone timing DNS, TCP,

    TLS Redirects TTFB* First Paint First Contentful Paint* If any of these are slow, you’re already on the back foot.
  17. Treo

  18. Choose the Best Element

  19. Choose the Best Element Not all candidates are born equal

    <img> elements <image> elements inside an <svg> element <video> elements (the poster image is used) An element with a background image loaded via the url() function (as opposed to a CSS gradient) Block-level elements containing text nodes or other inline-level text element children. web.dev/lcp
  20. We Love Text-Based LCPs

  21. 844ms 1403ms

  22. @font-face { ... font-display: [ swap | optional ]; ...

  23. What About Imagery?

  24. <head> <script src=script.js></script> </head> <body> <!-- - LCP Candidate -

    - <img> | <image> in <svg> | poster | background-image --> </body>
  25. Time (s) 0 1 2 3 4 5 <img> <image>

    in <svg> poster background-image 4.373 3.164 2.619 3.144
  26. <img> background-image poster <image> in <svg>

  27. Time (s) 0 1 2 3 4 5 <img> <image>

    in <svg> poster background-image 4.373 3.164 3.901 3.144
  28. <img> background-image poster <image> in <svg>

  29. <img> Is Good

  30. <img src=lcp.jpg alt>

  31. <img>

  32. The Preload Scanner

  33. The Preload Scanner Faster for free… Invented in IE8 as

    the ‘Speculative Pre-Parser’. A secondary, inert, asynchronous, download-only parser. Decouples resource discovery/download from runtime executions. Made the web a lot, lot faster. In every single modern browser. Some resources are visible to the Preload Scanner, some are not.
  34. After: parsing and downloading are now decoupled and asynchronous. Before:

    parse, discover, download, execute, parse, discover, download, execute, parse…
  35. <image> in <svg> Is Bad

  36. <svg xmlns="http://www.w3.org/1000/svg"> <image href="lcp.jpg" /> </svg>

  37. <image> in <svg>

  38. <image> in <svg> Is Bad What’s going on here? Reporting

    is broken—<image> element not counted. <image> is hidden from the Preload Scanner—it’s inherently slow. Statistically unlikely to use it, but I still wouldn’t recommend it.
  39. poster Is Good

  40. <video poster=lcp.jpg></video>

  41. poster

  42. poster Is Good A pleasant surprise poster behaves much like

    <img>. poster is available to the Preload Scanner. If you do have a <video>-LCP, make sure you use poster…
  43. — csswz.it/3Cp1Js5 Currently, a video element with a poster image

    will have that poster image considered for LCP, but without one, whether it is not present or the video autoplays, the video is ignored (for LCP purposes). The first frame of a video, when painted, should be considered as an LCP candidate.
  44. background-image Is Bad

  45. <div style=background-image:url(lcp.jpg)>...</div>

  46. background-image

  47. background-image Is Bad No surprise at all background-image is hidden

    from the Preload Scanner. Background images are late-discovered resources. Browsers only request background images (and web fonts) if it knows the page needs them… And it doesn’t know that until it encounters the DOM node that needs it. background-image is inherently slow.
  48. CSS Doesn’t Download Images…

  49. …The Render Tree Does

  50. — csswz.it/3rvESok The reason these resources (in this specific case,

    background images) are slow is because they aren’t requested until the browser is ready to paint the DOM node that needs them.
  51. Not All Candidates Are Born Equal Takeaways… Ideally, a text-based

    candidate with appropriate font-display will be fastest. poster is nice and fast, but statistically unlikely to be used. <image> in <svg> is broken—reports very fast but is actually among the slowest. background-image is likely very common but also inherently slow. <img> is statistically most likely and is also pretty fast. Google may use the first frame of a video in future. That’s gonna hurt.
  52. Common Mistakes

  53. Don’t Lazy Load Your LCP

  54. None
  55. <img src=lcp.jpg alt loading=lazy>

  56. — Well-meaning developers Can’t we just add loading=lazy and the

    browser works out what to do?
  57. loading=lazy hides the <img> From the Preload Scanner

  58. <img> <img loading=lazy>

  59. img[loading=lazy] { outline: 10px solid red; }

  60. None
  61. Don’t Build Your LCP 
 with JavaScript

  62. None
  63. None
  64. LCPs built with JS contain extra steps. Use HTML. HTML

    is fast.
  65. None
  66. None
  67. Don’t Host Off-Site

  68. <img src=https://res.cloudinary.com/lcp.jpg alt>

  69. None
  70. Image was 1.5× smaller, but took 2.6× longer!

  71. Don’t Usurp* Your LCP 👑

  72. None
  73. 46,287px2 32,943px2 44,485px2 44,485px2

  74. Stretch Goals

  75. Resource Hints

  76. <head> <link rel=preload as=image 
 href=lcp.jpg> </head>

  77. Resource Hints preload rel=preload is useful for late discovered resources.

    The clue is in the name—remember the Preload Scanner? This exposes otherwise hidden resources to the Preload Scanner. Generally speaking, don’t use rel=preload for resources already available in HTML. Useful if your LCP candidate is a background-image.
  78. Discovered after CSS; fourth request; bandwidth heavily shared. LCP approx.

    2s. Requested in parallel with CSS; exclusive use of bandwidth. LCP approx. 1.2s.
  79. ! Beware Google Chrome — csswz.it/3TcnPUv

  80. Priority Hints

  81. <body> <img src=lcp.jpg alt 
 fetchpriority=high> </body>

  82. Priority Hints fetchpriority fetchpriority=high is useful for in-page resources. Also

    fetchpriority=[ low | auto ]. Reduces amount of time spent queueing. This is amazing. Allows us to control priorities! We need to understand what priorities are.
  83. — csswz.it/3SCCjNh

  84. None
  85. None
  86. None
  87. None
  88. None
  89. None
  90. None
  91. None
  92. auto high Change Discovered 794ms 771ms -23ms Queuing 2,160ms 0.85ms

    -2,159.15ms Duration 12,030ms 5,370ms -6,660ms LCP 13,795ms 4,960ms -8,835ms
  93. Image Decoding

  94. — csswz.it/3RwpeUk Image decoding is said to be synchronous if

    it prevents presentation of other content until it is finished.
  95. Image Decoding decoding Tells the browser how to deal with

    image decode tasks. decoding=sync is useful for LCP candidates. Also decoding=[ async | auto ].
  96. <img src=lcp.jpg alt 

  97. None
  98. <img src=lcp.jpg alt 

  99. None
  100. <img src=lcp.jpg alt 

  101. <img src=btf.jpg alt 

  102. Summary

  103. Summary Putting it all together… LCP is the final metric—solve

    everything that happens before it. Choose the best candidate—text is best; <img> is good. Expose the LCP candidate early—we love HTML! Don’t work against yourself—don’t make silly mistakes. Step in and help the browser—use new APIs to push things further. Use new features sparingly—test everything.
  104. Slides: csswz.it/lcp Thank You harry.is/for-hire