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
Tweet

More Decks by Harry Roberts

Other Decks in Technology

Transcript

  1. performance.now() – October 2022
    Optimising

    Largest Contentful Paint
    Harry Roberts – @csswizardry

    View Slide

  2. View Slide

  3. What is LCP?

    View Slide

  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.”

    View Slide

  5. View Slide

  6. View Slide

  7. View Slide

  8. View Slide

  9. What Is ‘Good’?

    View Slide

  10. — web.dev/lcp

    View Slide

  11. — csswz.it/3ybF0NK
    To confirm that a threshold is
    achievable, we require that at
    least 10% of origins currently
    meet the ‘good’ threshold.

    View Slide

  12. Site-Speed Is More Than Just SEO

    View Slide

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

    View Slide

  14. Getting to ‘Good’

    View Slide

  15. Solve Everything Beforehand

    View Slide

  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.

    View Slide

  17. Treo

    View Slide

  18. Choose the Best Element

    View Slide

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


    elements inside an element


    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

    View Slide

  20. We Love Text-Based LCPs

    View Slide

  21. 844ms
    1403ms

    View Slide

  22. @font-face {


    ...


    font-display: [ swap | optional ];


    ...


    }

    View Slide

  23. What About Imagery?

    View Slide

















  24. View Slide

  25. Time (s)
    0
    1
    2
    3
    4
    5
    in poster background-image
    4.373
    3.164
    2.619
    3.144

    View Slide


  26. background-image
    poster
    in

    View Slide

  27. Time (s)
    0
    1
    2
    3
    4
    5
    in poster background-image
    4.373
    3.164
    3.901
    3.144

    View Slide


  28. background-image
    poster
    in

    View Slide

  29. Is Good

    View Slide


  30. View Slide


  31. View Slide

  32. The Preload Scanner

    View Slide

  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.

    View Slide

  34. After: parsing and downloading are now decoupled and asynchronous.
    Before: parse, discover, download, execute, parse, discover, download, execute, parse…

    View Slide

  35. in Is Bad

    View Slide








  36. View Slide

  37. in

    View Slide

  38. in Is Bad
    What’s going on here?
    Reporting is broken— element not counted.


    is hidden from the Preload Scanner—it’s inherently slow.


    Statistically unlikely to use it, but I still wouldn’t recommend it.

    View Slide

  39. poster Is Good

    View Slide


  40. View Slide

  41. poster

    View Slide

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


    poster is available to the Preload Scanner.


    If you do have a -LCP, make sure you use poster…

    View Slide

  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.

    View Slide

  44. background-image Is Bad

    View Slide

  45. ...

    View Slide

  46. background-image

    View Slide

  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.

    View Slide

  48. CSS Doesn’t Download Images…

    View Slide

  49. …The Render Tree Does

    View Slide

  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.

    View Slide

  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.


    in is broken—reports very fast but is actually among the slowest.


    background-image is likely very common but also inherently slow.


    is statistically most likely and is also pretty fast.


    Google may use the first frame of a video in future. That’s gonna hurt.

    View Slide

  52. Common Mistakes

    View Slide

  53. Don’t Lazy Load Your LCP

    View Slide

  54. View Slide



  55. loading=lazy>

    View Slide

  56. — Well-meaning developers
    Can’t we just add loading=lazy and
    the browser works out what to do?

    View Slide

  57. loading=lazy hides the
    From the Preload Scanner

    View Slide



  58. View Slide

  59. img[loading=lazy] {


    outline: 10px solid red;


    }

    View Slide

  60. View Slide

  61. Don’t Build Your LCP

    with JavaScript

    View Slide

  62. View Slide

  63. View Slide

  64. LCPs built with JS contain extra steps.
    Use HTML. HTML is fast.

    View Slide

  65. View Slide

  66. View Slide

  67. Don’t Host Off-Site

    View Slide


  68. View Slide

  69. View Slide

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

    View Slide

  71. Don’t Usurp* Your LCP
    👑

    View Slide

  72. View Slide

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

    View Slide

  74. Stretch Goals

    View Slide

  75. Resource Hints

    View Slide





  76. href=lcp.jpg>



    View Slide

  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.

    View Slide

  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.

    View Slide

  79. !
    Beware Google Chrome
    — csswz.it/3TcnPUv

    View Slide

  80. Priority Hints

    View Slide





  81. fetchpriority=high>



    View Slide

  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.

    View Slide

  83. — csswz.it/3SCCjNh

    View Slide

  84. View Slide

  85. View Slide

  86. View Slide

  87. View Slide

  88. View Slide

  89. View Slide

  90. View Slide

  91. View Slide

  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

    View Slide

  93. Image Decoding

    View Slide

  94. — csswz.it/3RwpeUk
    Image decoding is said to be
    synchronous if it prevents presentation
    of other content until it is finished.

    View Slide

  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 ].


    View Slide


  96. decoding=async>

    View Slide

  97. View Slide


  98. decoding=sync>

    View Slide

  99. View Slide


  100. fetchpriority=high

    decoding=sync>

    View Slide


  101. loading=lazy

    decoding=async>

    View Slide

  102. Summary

    View Slide

  103. Summary
    Putting it all together…
    LCP is the final metric—solve everything that happens before it.


    Choose the best candidate—text is best; 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.

    View Slide

  104. Slides: csswz.it/lcp
    Thank You
    harry.is/for-hire

    View Slide