<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://web.dev/</id>
  <title>Barry Pollard on web.dev</title>
  <updated>2026-04-15T23:21:06Z</updated>
  <author>
    <name>Barry Pollard</name>
  </author>
  <link href="https://web.dev/authors/tunetheweb/feed.xml" rel="self"/>
  <link href="https://web.dev/"/>
  <icon>https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FzCdsVVLfgdqxfgs6BDc.jpeg?auto=format</icon>
  <logo>https://web.dev/images/shared/rss-banner.png</logo>
  <subtitle>Web Performance Developer Advocate for Google</subtitle>
  
  
  <entry>
    <title>Using the Web Vitals extension to debug Core Web Vitals issues</title>
    <link href="https://web.dev/debug-cwvs-with-web-vitals-extension/"/>
    <updated>2023-05-04T00:00:00Z</updated>
    <id>https://web.dev/debug-cwvs-with-web-vitals-extension/</id>
    <content type="html" mode="escaped">&lt;p&gt;The &lt;a href=&quot;https://chrome.google.com/webstore/detail/web-vitals/ahfhijdlegdabablpippeagghigmibma&quot; rel=&quot;noopener&quot;&gt;Web Vitals extension&lt;/a&gt; provides easy access to Core Web Vitals diagnostic information to help developers measure, and address Core Web Vitals issues. It supplements the other tools provided by the Chrome team to aid developers in improving the experiences on their websites.&lt;/p&gt;
&lt;p&gt;We have updated the extension to provide additional debug information to developers to make it easier to understand and address their performance problems.&lt;/p&gt;
&lt;h2 id=&quot;showing-debug-information-in-the-console&quot;&gt;Showing debug information in the console &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-cwvs-with-web-vitals-extension/#showing-debug-information-in-the-console&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Web Vitals extension has had a &amp;quot;Console Logging&amp;quot; debug option for some time now. It can be enabled in the Options screen:&lt;/p&gt;
&lt;img alt=&quot;Web Vitals Extension Options screen&quot; decoding=&quot;async&quot; height=&quot;434&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SjuszdY0PADWgETicJNl.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SjuszdY0PADWgETicJNl.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SjuszdY0PADWgETicJNl.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SjuszdY0PADWgETicJNl.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SjuszdY0PADWgETicJNl.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SjuszdY0PADWgETicJNl.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SjuszdY0PADWgETicJNl.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SjuszdY0PADWgETicJNl.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SjuszdY0PADWgETicJNl.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SjuszdY0PADWgETicJNl.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SjuszdY0PADWgETicJNl.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SjuszdY0PADWgETicJNl.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SjuszdY0PADWgETicJNl.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SjuszdY0PADWgETicJNl.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SjuszdY0PADWgETicJNl.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SjuszdY0PADWgETicJNl.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SjuszdY0PADWgETicJNl.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SjuszdY0PADWgETicJNl.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Prior to the this last upgrade, this logged the outputs from the &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;web-vitals library&lt;/code&gt;&lt;/a&gt; (that underpins the extension) in a JSON object:&lt;/p&gt;
&lt;img alt=&quot;Web Vitals Extension old console logging&quot; decoding=&quot;async&quot; height=&quot;458&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/iUoyoe41Ik4zNDNRPHKo.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/iUoyoe41Ik4zNDNRPHKo.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/iUoyoe41Ik4zNDNRPHKo.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/iUoyoe41Ik4zNDNRPHKo.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/iUoyoe41Ik4zNDNRPHKo.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/iUoyoe41Ik4zNDNRPHKo.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/iUoyoe41Ik4zNDNRPHKo.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/iUoyoe41Ik4zNDNRPHKo.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/iUoyoe41Ik4zNDNRPHKo.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/iUoyoe41Ik4zNDNRPHKo.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/iUoyoe41Ik4zNDNRPHKo.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/iUoyoe41Ik4zNDNRPHKo.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/iUoyoe41Ik4zNDNRPHKo.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/iUoyoe41Ik4zNDNRPHKo.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/iUoyoe41Ik4zNDNRPHKo.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/iUoyoe41Ik4zNDNRPHKo.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/iUoyoe41Ik4zNDNRPHKo.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/iUoyoe41Ik4zNDNRPHKo.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;This object could then be expanded to get the full details, and elements such as the LCP image, could be hovered over to highlight them in the main panel:&lt;/p&gt;
&lt;img alt=&quot;Web Vitals Extension old console logging with element highlighting&quot; decoding=&quot;async&quot; height=&quot;514&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oNVOo321R5U3Lqg1xrH6.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oNVOo321R5U3Lqg1xrH6.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oNVOo321R5U3Lqg1xrH6.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oNVOo321R5U3Lqg1xrH6.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oNVOo321R5U3Lqg1xrH6.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oNVOo321R5U3Lqg1xrH6.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oNVOo321R5U3Lqg1xrH6.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oNVOo321R5U3Lqg1xrH6.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oNVOo321R5U3Lqg1xrH6.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oNVOo321R5U3Lqg1xrH6.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oNVOo321R5U3Lqg1xrH6.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oNVOo321R5U3Lqg1xrH6.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oNVOo321R5U3Lqg1xrH6.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oNVOo321R5U3Lqg1xrH6.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oNVOo321R5U3Lqg1xrH6.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oNVOo321R5U3Lqg1xrH6.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oNVOo321R5U3Lqg1xrH6.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oNVOo321R5U3Lqg1xrH6.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;This was helpful, but the output format was not particularly user friendly, and we thought we could provide a better developer experience. So we have improved the extension to make the most important information more visible—while still including the full object for those wanting more details.&lt;/p&gt;
&lt;h2 id=&quot;new-debug-information-for-each-metric&quot;&gt;New debug information for each metric &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-cwvs-with-web-vitals-extension/#new-debug-information-for-each-metric&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With the new release, we have added new debug information in a more readable format to help you find and address issues. Different information is provided for each of the metrics, as each one is different.&lt;/p&gt;
&lt;h3 id=&quot;lcp-debug-information&quot;&gt;LCP debug information &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-cwvs-with-web-vitals-extension/#lcp-debug-information&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt;, we show both the element, and the breakdown of the 4 phases detailed in our &lt;a href=&quot;https://web.dev/optimize-lcp/#lcp-breakdown&quot;&gt;Optimize LCP&lt;/a&gt; guide:&lt;/p&gt;
&lt;img alt=&quot;Web Vitals Extension new console logging showing LCP elements and sub-parts&quot; decoding=&quot;async&quot; height=&quot;535&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/HXzKcJORE2nRUI9Pl8Mw.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/HXzKcJORE2nRUI9Pl8Mw.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/HXzKcJORE2nRUI9Pl8Mw.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/HXzKcJORE2nRUI9Pl8Mw.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/HXzKcJORE2nRUI9Pl8Mw.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/HXzKcJORE2nRUI9Pl8Mw.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/HXzKcJORE2nRUI9Pl8Mw.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/HXzKcJORE2nRUI9Pl8Mw.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/HXzKcJORE2nRUI9Pl8Mw.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/HXzKcJORE2nRUI9Pl8Mw.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/HXzKcJORE2nRUI9Pl8Mw.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/HXzKcJORE2nRUI9Pl8Mw.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/HXzKcJORE2nRUI9Pl8Mw.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/HXzKcJORE2nRUI9Pl8Mw.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/HXzKcJORE2nRUI9Pl8Mw.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/HXzKcJORE2nRUI9Pl8Mw.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/HXzKcJORE2nRUI9Pl8Mw.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/HXzKcJORE2nRUI9Pl8Mw.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;The LCP time (2,876 milliseconds—or about 2.9 seconds) is highlighted in amber as it is in the &amp;quot;Needs Improvement&amp;quot; category.&lt;/p&gt;
&lt;p&gt;In this example, we see the &lt;code&gt;Resource load time&lt;/code&gt; is the longest time, so to improve your LCP time you would look to optimize that - perhaps by avoiding hosting them on a separate domain, or by using smaller images or more efficient formats. In this case it&#39;s due to being artificially slowed down to demonstrate the output—web.dev is a fast site 😀&lt;/p&gt;
&lt;p&gt;The element can also be hovered over to highlight the image:&lt;/p&gt;
&lt;img alt=&quot;Web Vitals Extension new console logging retains element highlighting on hover&quot; decoding=&quot;async&quot; height=&quot;535&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/8H8ZldOw8y953w7vTpuA.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/8H8ZldOw8y953w7vTpuA.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/8H8ZldOw8y953w7vTpuA.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/8H8ZldOw8y953w7vTpuA.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/8H8ZldOw8y953w7vTpuA.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/8H8ZldOw8y953w7vTpuA.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/8H8ZldOw8y953w7vTpuA.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/8H8ZldOw8y953w7vTpuA.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/8H8ZldOw8y953w7vTpuA.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/8H8ZldOw8y953w7vTpuA.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/8H8ZldOw8y953w7vTpuA.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/8H8ZldOw8y953w7vTpuA.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/8H8ZldOw8y953w7vTpuA.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/8H8ZldOw8y953w7vTpuA.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/8H8ZldOw8y953w7vTpuA.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/8H8ZldOw8y953w7vTpuA.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/8H8ZldOw8y953w7vTpuA.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/8H8ZldOw8y953w7vTpuA.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Right clicking on the element also allows you to reveal it in the elements panel.&lt;/p&gt;
&lt;p&gt;Here the LCP element is an image, and hovering over that in the console on the right, also highlights that element on the site on the left.&lt;/p&gt;
&lt;h3 id=&quot;cls-debug-information&quot;&gt;CLS debug information &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-cwvs-with-web-vitals-extension/#cls-debug-information&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Shifts contributing to &lt;a href=&quot;https://web.dev/cls/&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt; are now also listed, and can be hovered over to highlight the relevant element:&lt;/p&gt;
&lt;img alt=&quot;Web Vitals Extension new console logging showing each CLS element shift&quot; decoding=&quot;async&quot; height=&quot;448&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/v1kaVvcekk5oLWuGHAkd.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/v1kaVvcekk5oLWuGHAkd.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/v1kaVvcekk5oLWuGHAkd.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/v1kaVvcekk5oLWuGHAkd.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/v1kaVvcekk5oLWuGHAkd.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/v1kaVvcekk5oLWuGHAkd.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/v1kaVvcekk5oLWuGHAkd.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/v1kaVvcekk5oLWuGHAkd.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/v1kaVvcekk5oLWuGHAkd.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/v1kaVvcekk5oLWuGHAkd.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/v1kaVvcekk5oLWuGHAkd.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/v1kaVvcekk5oLWuGHAkd.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/v1kaVvcekk5oLWuGHAkd.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/v1kaVvcekk5oLWuGHAkd.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/v1kaVvcekk5oLWuGHAkd.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/v1kaVvcekk5oLWuGHAkd.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/v1kaVvcekk5oLWuGHAkd.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/v1kaVvcekk5oLWuGHAkd.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;The above screenshot shows 2 shifts, the first made up of two elements (when the banner image is loaded and the content beneath it is shifted downloaded), and the second of 4 elements (when the dynamic ad is loaded and most of the page is shifted downwards).&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;h2&lt;/code&gt; element is being hovered over in this screenshot in the console on the right, and you can see this highlights the element on the site on the left.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Important&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;p&gt; Note that the shifted elements are not the elements &lt;em&gt;causing&lt;/em&gt; the shift, but the ones that were &lt;em&gt;impacted&lt;/em&gt; by any shifts. &lt;/p&gt; &lt;p&gt; However, as per the above example, this should usually be enough to help you identify the cause of the shift by looking at the element which is either above the first shifted element (for inserted elements) or the first element itself (if this is expanded and so shifts itself). &lt;/p&gt; &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;fid-debug-information&quot;&gt;FID debug information &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-cwvs-with-web-vitals-extension/#fid-debug-information&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For &lt;a href=&quot;https://web.dev/fid/&quot;&gt;First Input Delay (FID)&lt;/a&gt; we show the affected element (which again, can be hovered over to highlight it on the page) and the interaction type, along with the full JSON object as usual:&lt;/p&gt;
&lt;img alt=&quot;Web Vitals Extension new console logging showing FID target and type&quot; decoding=&quot;async&quot; height=&quot;448&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/f8JI8akIhiqfA7WHWfRz.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/f8JI8akIhiqfA7WHWfRz.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/f8JI8akIhiqfA7WHWfRz.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/f8JI8akIhiqfA7WHWfRz.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/f8JI8akIhiqfA7WHWfRz.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/f8JI8akIhiqfA7WHWfRz.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/f8JI8akIhiqfA7WHWfRz.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/f8JI8akIhiqfA7WHWfRz.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/f8JI8akIhiqfA7WHWfRz.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/f8JI8akIhiqfA7WHWfRz.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/f8JI8akIhiqfA7WHWfRz.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/f8JI8akIhiqfA7WHWfRz.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/f8JI8akIhiqfA7WHWfRz.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/f8JI8akIhiqfA7WHWfRz.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/f8JI8akIhiqfA7WHWfRz.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/f8JI8akIhiqfA7WHWfRz.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/f8JI8akIhiqfA7WHWfRz.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/f8JI8akIhiqfA7WHWfRz.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h3 id=&quot;inp-debug-information&quot;&gt;INP debug information &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-cwvs-with-web-vitals-extension/#inp-debug-information&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For &lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt;, we have added two new logs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;INP - the longest interaction&lt;/li&gt;
&lt;li&gt;Interactions - all interactions&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;inp-metric&quot;&gt;INP metric &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-cwvs-with-web-vitals-extension/#inp-metric&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;First up, we highlight the INP metric when it changes:&lt;/p&gt;
&lt;img alt=&quot;Web Vitals Extension new console logging showing INP target, event type, and breakdown&quot; decoding=&quot;async&quot; height=&quot;448&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Similar to LCP, the extension breaks down the INP time to show three phases:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Input delay&lt;/li&gt;
&lt;li&gt;Processing time&lt;/li&gt;
&lt;li&gt;Presentation delay&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This helps you identify if the event was slow due to being held up by other events (&lt;strong&gt;input delay&lt;/strong&gt;), as the event handler itself was slow due to your code (&lt;strong&gt;processing time&lt;/strong&gt;), if the post-processing render delay was the reason (&lt;strong&gt;presentation delay&lt;/strong&gt;), or a combination of two or more of these.&lt;/p&gt;
&lt;h4 id=&quot;interactions&quot;&gt;Interactions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-cwvs-with-web-vitals-extension/#interactions&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;INP can be slow due to previous interactions blocking the main thread, and thus causing a high input delay. For this reason, we list all interactions in a similar format to the INP logging:&lt;/p&gt;
&lt;img alt=&quot;Web Vitals Extension new console logging showing all interactions&quot; decoding=&quot;async&quot; height=&quot;448&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/k2uHWUQXoq5fWGOQ4yyF.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/k2uHWUQXoq5fWGOQ4yyF.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/k2uHWUQXoq5fWGOQ4yyF.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/k2uHWUQXoq5fWGOQ4yyF.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/k2uHWUQXoq5fWGOQ4yyF.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/k2uHWUQXoq5fWGOQ4yyF.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/k2uHWUQXoq5fWGOQ4yyF.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/k2uHWUQXoq5fWGOQ4yyF.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/k2uHWUQXoq5fWGOQ4yyF.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/k2uHWUQXoq5fWGOQ4yyF.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/k2uHWUQXoq5fWGOQ4yyF.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/k2uHWUQXoq5fWGOQ4yyF.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/k2uHWUQXoq5fWGOQ4yyF.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/k2uHWUQXoq5fWGOQ4yyF.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/k2uHWUQXoq5fWGOQ4yyF.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/k2uHWUQXoq5fWGOQ4yyF.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/k2uHWUQXoq5fWGOQ4yyF.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/k2uHWUQXoq5fWGOQ4yyF.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;This allows you to &amp;quot;live trace&amp;quot; a website by interacting with it and seeing in the console which interactions, in which combinations, are likely to cause an INP problem.&lt;/p&gt;
&lt;p&gt;This also allows you to identify multiple slow interactions, rather than just the largest INP interaction to help you avoid the feeling of chasing your tail when improving your responsiveness.&lt;/p&gt;
&lt;h2 id=&quot;filtering-the-console-logs&quot;&gt;Filtering the console logs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-cwvs-with-web-vitals-extension/#filtering-the-console-logs&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;All this extra information, while useful, can be distracting if you are doing other development unrelated to Core Web Vitals, or are only interested in one particular Core Web Vital at that time.&lt;/p&gt;
&lt;p&gt;You can use the &lt;a href=&quot;https://developer.chrome.com/docs/devtools/console/reference/#filter&quot; rel=&quot;noopener&quot;&gt;Console filtering options in DevTools&lt;/a&gt; to filter out some or all of the messages:&lt;/p&gt;
&lt;img alt=&quot;Using console filtering options&quot; decoding=&quot;async&quot; height=&quot;448&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/NOA3kNu5rKMzkw7iAyW7.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/NOA3kNu5rKMzkw7iAyW7.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/NOA3kNu5rKMzkw7iAyW7.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/NOA3kNu5rKMzkw7iAyW7.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/NOA3kNu5rKMzkw7iAyW7.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/NOA3kNu5rKMzkw7iAyW7.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/NOA3kNu5rKMzkw7iAyW7.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/NOA3kNu5rKMzkw7iAyW7.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/NOA3kNu5rKMzkw7iAyW7.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/NOA3kNu5rKMzkw7iAyW7.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/NOA3kNu5rKMzkw7iAyW7.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/NOA3kNu5rKMzkw7iAyW7.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/NOA3kNu5rKMzkw7iAyW7.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/NOA3kNu5rKMzkw7iAyW7.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/NOA3kNu5rKMzkw7iAyW7.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/NOA3kNu5rKMzkw7iAyW7.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/NOA3kNu5rKMzkw7iAyW7.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/NOA3kNu5rKMzkw7iAyW7.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;ul&gt;
&lt;li&gt;To remove all the extension messages, you can either turn this off in the options or use the &lt;code&gt;-Extension&lt;/code&gt; filter.&lt;/li&gt;
&lt;li&gt;To look at just LCP you can use the &lt;code&gt;Web Vitals Extension LCP&lt;/code&gt; filter.&lt;/li&gt;
&lt;li&gt;To look at just INP and interactions you can use the &lt;code&gt;Web Vitals Extension -LCP -CLS -FID&lt;/code&gt; filter.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We&#39;re trying to keep the number of options for this extension down, but do let us know by raising a &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals-extension/issues&quot; rel=&quot;noopener&quot;&gt;GitHub issue&lt;/a&gt; if DevTools filtering is not sufficient and you would prefer options here.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-cwvs-with-web-vitals-extension/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We hope you find the new debug options in the latest version of the extension useful and that they make it easier to identify and resolve Core Web Vitals issues, improving the  user experiences on your website.&lt;/p&gt;
&lt;p&gt;Do remember that your experiences, on your developer computer, may not be representative of what your real users are experiencing. Check out our &lt;a href=&quot;https://web.dev/field-data-in-the-web-vitals-extension/&quot;&gt;previous blog post on how you can view the CrUX field data for your site in the extension&lt;/a&gt; to get a sense of how aligned your experiences are with your users.&lt;/p&gt;
&lt;p&gt;We would be grateful to hear any feedback on these improvements, or any other suggestions on our &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals-extension/issues&quot; rel=&quot;noopener&quot;&gt;GitHub issues tracker&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;acknowledgements&quot;&gt;Acknowledgements &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-cwvs-with-web-vitals-extension/#acknowledgements&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Hero image by &lt;a href=&quot;https://unsplash.com/@euwars?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot; rel=&quot;noopener&quot;&gt;Farzad&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/p-xSl33Wxyc?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>Barry Pollard</name>
    </author><author>
      <name>Michal Mocny</name>
    </author><author>
      <name>Rick Viscomi</name>
    </author><author>
      <name>Brendan Kenny</name>
    </author>
  </entry>
  
  <entry>
    <title>Optimize Time to First Byte</title>
    <link href="https://web.dev/optimize-ttfb/"/>
    <updated>2023-01-19T00:00:00Z</updated>
    <id>https://web.dev/optimize-ttfb/</id>
    <content type="html" mode="escaped">&lt;p&gt;&lt;a href=&quot;https://web.dev/ttfb/&quot;&gt;Time to First Byte (TTFB)&lt;/a&gt; is a foundational web performance metric that precedes every other meaningful user experience metric such as &lt;a href=&quot;https://web.dev/fcp/&quot;&gt;First Contentful Paint (FCP)&lt;/a&gt; and &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt;. This means that high TTFB values add time to the metrics that follow it.&lt;/p&gt;
&lt;p&gt;It&#39;s recommended that your server responds to navigation requests quickly enough so that the &lt;strong&gt;75th percentile&lt;/strong&gt; of users experience an FCP &lt;a href=&quot;https://web.dev/fcp/#what-is-a-good-fcp-score&quot;&gt;within the &amp;quot;good&amp;quot; threshold&lt;/a&gt;. As a rough guide, most sites should strive to have a TTFB of &lt;strong&gt;0.8 seconds or less&lt;/strong&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;picture&gt;
    &lt;source srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/ILJ1xKjzVisqOPPyHYVA.svg&quot; media=&quot;(min-width: 640px)&quot; width=&quot;800&quot; height=&quot;200&quot; /&gt;
    &lt;img alt=&quot;Good TTFB values are 0.8 seconds or less, poor values are greater than 1.8 seconds, and anything in between needs improvement&quot; decoding=&quot;async&quot; height=&quot;480&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/EcKicxW5ErYYhf8RvpeO.svg&quot; width=&quot;640&quot; /&gt;
  &lt;/picture&gt;
&lt;/figure&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Important&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;p&gt;TTFB is not a &lt;a href=&quot;https://web.dev/vitals/&quot;&gt;Core Web Vitals&lt;/a&gt; metric, so it&#39;s not absolutely necessary that sites meet the &amp;quot;good&amp;quot; TTFB threshold, provided that it doesn&#39;t impede their ability to score well on the metrics that matter.&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt;Websites vary in how they deliver content. A low TTFB is crucial for getting markup out to the client as soon as possible. However, if a website delivers the initial markup quickly, but that markup then requires JavaScript to populate it with meaningful content—as is the the case with Single Page Applications (SPAs)—then achieving the lowest possible TTFB is especially important so that the client-rendering of markup can occur sooner.&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt;Conversely, a server-rendered site that does not require as much client-side work could have a higher TTFB, but better FCP and LCP values than an entirely client-rendered experience. This is why the TTFB thresholds are a “rough guide”, and will need to be weighed against how your site delivers its core content.&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;how-to-measure-ttfb&quot;&gt;How to Measure TTFB &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-ttfb/#how-to-measure-ttfb&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before you can optimize TTFB, you need to observe how it affects your website&#39;s users. You should rely on &lt;a href=&quot;https://web.dev/lab-and-field-data-differences/#field-data&quot;&gt;field data&lt;/a&gt; as a primary source of observing TTFB as it affected by redirects, whereas lab-based tools are often measured using the final URL therefore missing this extra delay.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://pagespeed.web.dev/&quot; rel=&quot;noopener&quot;&gt;PageSpeed Insights&lt;/a&gt; is a simple way to get both field and lab information for public websites that are available in the &lt;a href=&quot;https://developer.chrome.com/docs/crux/&quot; rel=&quot;noopener&quot;&gt;Chrome User Experience Report&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;TTFB for real users is shown in the top &lt;strong&gt;Discover what your real users are experiencing&lt;/strong&gt; section:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;PageSpeed Insights real user data&quot; decoding=&quot;async&quot; height=&quot;478&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;A subset of TTFB is shown in the &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/server-response-time/&quot; rel=&quot;noopener&quot;&gt;server response time audit&lt;/a&gt;:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Server response time audit&quot; decoding=&quot;async&quot; height=&quot;213&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SYH4MlnwyQtWoeGNRmHh.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SYH4MlnwyQtWoeGNRmHh.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SYH4MlnwyQtWoeGNRmHh.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SYH4MlnwyQtWoeGNRmHh.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SYH4MlnwyQtWoeGNRmHh.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SYH4MlnwyQtWoeGNRmHh.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SYH4MlnwyQtWoeGNRmHh.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SYH4MlnwyQtWoeGNRmHh.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SYH4MlnwyQtWoeGNRmHh.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SYH4MlnwyQtWoeGNRmHh.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SYH4MlnwyQtWoeGNRmHh.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SYH4MlnwyQtWoeGNRmHh.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SYH4MlnwyQtWoeGNRmHh.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SYH4MlnwyQtWoeGNRmHh.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SYH4MlnwyQtWoeGNRmHh.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SYH4MlnwyQtWoeGNRmHh.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SYH4MlnwyQtWoeGNRmHh.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/SYH4MlnwyQtWoeGNRmHh.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Important&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; The server response time audit in Lighthouse excludes DNS lookup and redirect times, so it only represents a subset of TTFB. A large difference between real user data and Lighthouse data can indicate issues not apparent during the lab run, such as redirects or network differences. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;To find out more ways how to measure TTFB in both the field and the lab, &lt;a href=&quot;https://web.dev/ttfb/#how-to-measure-ttfb&quot;&gt;consult the TTFB metric page&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;understanding-high-ttfb-with-server-timing&quot;&gt;Understanding high TTFB with &lt;code&gt;Server-Timing&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-ttfb/#understanding-high-ttfb-with-server-timing&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Server-Timing&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Server-Timing&lt;/code&gt; response header&lt;/a&gt; can be used in your application backend to measure distinct backend processes that could contribute to high latency. The header value&#39;s structure is flexible, accepting, at minimum, a handle that you define. Optional values include a duration value (via &lt;code&gt;dur&lt;/code&gt;), as well as an optional human-readable description (via &lt;code&gt;desc&lt;/code&gt;).&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; If measuring backend latency with &lt;code&gt;Server-Timing&lt;/code&gt; is not feasible, then a suitable alternative may be to rely on &lt;a href=&quot;https://en.wikipedia.org/wiki/Application_performance_management&quot;&gt;Application Performance Monitoring (APM)&lt;/a&gt; to detect and diagnose backend performance problems. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;code&gt;Serving-Timing&lt;/code&gt; can be used to measure many application backend processes, but there are some that you may want to pay special attention to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Database queries&lt;/li&gt;
&lt;li&gt;Server-side rendering time, if applicable&lt;/li&gt;
&lt;li&gt;Disk seeks&lt;/li&gt;
&lt;li&gt;Edge server cache hits/misses (if using a CDN)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All parts of a &lt;code&gt;Server-Timing&lt;/code&gt; entry are colon-separated, and multiple entries can be separated by a comma:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;// Two metrics with descriptions and values&lt;br /&gt;&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Server-Timing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;db;desc=&quot;Database&quot;;dur=121.3, ssr;desc=&quot;Server-side Rendering&quot;;dur=212.2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The header can be set using your application backend&#39;s language of choice. In PHP, for example, you could set the header like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Get a high-resolution timestamp before&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// the database query is performed:&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token variable&quot;&gt;$dbReadStartTime&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hrtime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Perform a database query and get results...&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Get a high-resolution timestamp after&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// the database query is performed:&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token variable&quot;&gt;$dbReadEndTime&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hrtime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Get the total time, converting nanoseconds to&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// milliseconds (or whatever granularity you need):&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token variable&quot;&gt;$dbReadTotalTime&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$dbReadEndTime&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$dbReadStarTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1e+6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Set the Server-Timing header:&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Server-Timing: db;desc=&quot;Database&quot;;dur=&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$dbReadTotalTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token delimiter important&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;When this header is set, it will surface information you can use in both &lt;a href=&quot;https://web.dev/lab-and-field-data-differences/#lab-data&quot;&gt;the lab&lt;/a&gt;, and in &lt;a href=&quot;https://web.dev/lab-and-field-data-differences/#field-data&quot;&gt;the field&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the field, any page with a &lt;code&gt;Server-Timing&lt;/code&gt; response header set will populate the &lt;code&gt;serverTiming&lt;/code&gt; property in the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Navigation_timing_API&quot; rel=&quot;noopener&quot;&gt;Navigation Timing API&lt;/a&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Get the serverTiming entry for the first navigation request:&lt;/span&gt;&lt;br /&gt;performance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEntries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;navigation&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;serverTiming&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entry&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Log the server timing data:&lt;/span&gt;&lt;br /&gt;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;description&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;duration&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In the lab, data from the &lt;code&gt;Server-Timing&lt;/code&gt; response header will be visualized in the timings panel of the &lt;strong&gt;Network&lt;/strong&gt; tab in Chrome DevTools:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A visualization of Server-Timing header values in the Network tab of Chrome DevTools. In this image, the Server-Timing header values are measuring whether or not a CDN edge server encountered a cache hit or miss, as well as the time to retrieve the resource from the edge and the origin server.&quot; decoding=&quot;async&quot; height=&quot;143&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 777px) 777px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WzultrITdMX9H67w006x.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WzultrITdMX9H67w006x.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WzultrITdMX9H67w006x.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WzultrITdMX9H67w006x.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WzultrITdMX9H67w006x.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WzultrITdMX9H67w006x.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WzultrITdMX9H67w006x.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WzultrITdMX9H67w006x.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WzultrITdMX9H67w006x.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WzultrITdMX9H67w006x.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WzultrITdMX9H67w006x.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WzultrITdMX9H67w006x.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WzultrITdMX9H67w006x.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WzultrITdMX9H67w006x.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WzultrITdMX9H67w006x.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WzultrITdMX9H67w006x.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WzultrITdMX9H67w006x.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WzultrITdMX9H67w006x.png?auto=format&amp;w=1554 1554w&quot; width=&quot;777&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;Server-Timing&lt;/code&gt; response headers visualized in the network tab of Chrome DevTools. Here, &lt;code&gt;Server-Timing&lt;/code&gt; is used to measure whether a request for a resource has hit the CDN cache, and how long it takes for the request to hit the CDN&#39;s edge server and then the origin.&lt;/p&gt;
&lt;p&gt;Once you&#39;ve determined that you have a problematic TTFB by analyzing the data available, then you can move onto fixing the problem.&lt;/p&gt;
&lt;h2 id=&quot;ways-to-optimize-ttfb&quot;&gt;Ways to optimize TTFB &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-ttfb/#ways-to-optimize-ttfb&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The most challenging aspect of optimizing TTFB is that, while the web&#39;s frontend stack will always be HTML, CSS, and JavaScript, backend stacks can vary significantly. There are numerous backend stacks and database products that each have their own optimization techniques. Therefore, this guide will focus on what applies to most architectures, rather than focusing solely on stack-specific guidance.&lt;/p&gt;
&lt;h3 id=&quot;platform-specific-guidance&quot;&gt;Platform-specific guidance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-ttfb/#platform-specific-guidance&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The platform you use for your website can heavily impact TTFB. For example, WordPress performance is impacted by the number and quality of plugins, or what themes are used. Other platforms are similarly impacted when the platform is customized. You should consult the documentation for your platform for vendor-specific advice to supplement the more general performance advice in this post. The Lighthouse audit for reducing server response times also includes some &lt;a href=&quot;https://developer.chrome.com/en/docs/lighthouse/performance/time-to-first-byte/#stack-specific-guidance&quot; rel=&quot;noopener&quot;&gt;limited stack-specific guidance&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;hosting,-hosting,-hosting&quot;&gt;Hosting, hosting, hosting &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-ttfb/#hosting,-hosting,-hosting&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Before you even consider other optimization approaches, hosting should be the first thing you consider. There&#39;s not much in the way of specific guidance that can be offered here, but a general rule of thumb is to ensure that your website&#39;s host is capable of handling the traffic you send to it.&lt;/p&gt;
&lt;p&gt;Shared hosting will generally be slower. If you&#39;re running a small personal website that serves mostly static files, this is probably fine, and some of the optimization techniques that follow will help you get that TTFB down as much as possible.&lt;/p&gt;
&lt;p&gt;However, if you&#39;re running a larger application with many users that involves personalization, database querying, and other intensive server-side operations, your choice of hosting becomes critical to lower TTFB in the field.&lt;/p&gt;
&lt;p&gt;When choosing a hosting provider, these are some things to look out for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How much memory is your application instance allocated? If your application has insufficient memory, it will thrash and struggle to serve pages up as fast as possible.&lt;/li&gt;
&lt;li&gt;Does your hosting provider keep your backend stack up to date? As new versions of application backend languages, HTTP implementations, and database software are released, performance in that software will be improved over time. It&#39;s key to partner with a hosting provider that prioritizes this kind of crucial maintenance.&lt;/li&gt;
&lt;li&gt;If you have very specific application requirements and want the lowest level access to server configuration files, ask if it makes sense to customize your own application instance&#39;s backend.&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; If you&#39;re unsure how your hosting provider&#39;s real-user TTFB performance stacks up against competitors, &lt;a href=&quot;https://ismyhostfastyet.com/&quot;&gt;ismyhostfastyet.com&lt;/a&gt; is a good place to get that information. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;There are many hosting providers that will take care of these things for you, but if you start to observe long TTFB values even in dedicated hosting providers, it may be a sign that you might need to re-evaluate your current hosting provider&#39;s capabilities so that you can deliver the best possible user experience.&lt;/p&gt;
&lt;h3 id=&quot;use-a-content-delivery-network-cdn&quot;&gt;Use a Content Delivery Network (CDN) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-ttfb/#use-a-content-delivery-network-cdn&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The topic &lt;a href=&quot;https://web.dev/content-delivery-networks/&quot;&gt;CDN usage&lt;/a&gt; is a well-worn one, but for good reason: you could have a very well-optimized application backend, but users located far from your origin server may still experience high TTFB in the field.&lt;/p&gt;
&lt;p&gt;CDNs solve the problem of user proximity from your origin server by using a distributed network of servers that cache resources on servers that are physically closer to your users. These servers are called &lt;em&gt;edge servers&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;CDN providers may also offer benefits beyond edge servers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CDN providers usually offer extremely fast DNS resolution times.&lt;/li&gt;
&lt;li&gt;A CDN will likely serve your content from edge servers using modern protocols such as HTTP/2 or HTTP/3.&lt;/li&gt;
&lt;li&gt;HTTP/3 in particular solves the head-of-line blocking problem present in TCP (which HTTP/2 relies on) by using the &lt;a href=&quot;https://en.wikipedia.org/wiki/User_Datagram_Protocol&quot; rel=&quot;noopener&quot;&gt;UDP protocol&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A CDN will likely also provide modern versions of TLS, which lowers the latency involved in TLS negotiation time. &lt;a href=&quot;https://en.wikipedia.org/wiki/Transport_Layer_Security#TLS_1.3_handshake&quot; rel=&quot;noopener&quot;&gt;TLS 1.3&lt;/a&gt; in particular is designed to keep TLS negotiation as short as possible.&lt;/li&gt;
&lt;li&gt;Some CDN providers provide a feature often called an &amp;quot;edge worker&amp;quot;, which uses an API similar to that of &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Service_Worker_API&quot; rel=&quot;noopener&quot;&gt;the Service Worker API&lt;/a&gt; to intercept requests, programmatically manage responses in edge caches, or rewrite responses altogether.&lt;/li&gt;
&lt;li&gt;CDN providers are very good at optimizing for compression. Compression is tricky to get right on your own, and may lead to slower response times in certain cases with dynamically generated markup, which must be compressed on the fly.&lt;/li&gt;
&lt;li&gt;CDN providers will also automatically cache compressed responses for static resources, leading to the best mix of compression ratio and response time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While adopting a CDN involves a varying amount of effort from trivial to significant, it should be a high priority to pursue in optimizing your TTFB if your website is not already using one.&lt;/p&gt;
&lt;h3 id=&quot;used-cached-content-where-possible&quot;&gt;Used cached content where possible &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-ttfb/#used-cached-content-where-possible&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;CDNs allow content to be cached at edge servers which are located physically closer to visitors, provided the content is configured with the appropriate &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Cache-Control&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Cache-Control&lt;/code&gt; HTTP headers&lt;/a&gt;. While this is not appropriate for personalized content, requiring a trip all the way back to the origin can negate much of the value of a CDN.&lt;/p&gt;
&lt;p&gt;For sites that frequently update their content, even a short caching time can result in noticeable performance gains for busy sites, since only the first visitor during that time experiences the fully latency back to the origin server, while all other visitors can reuse the cached resource from the edge server. Some CDNs allow cache invalidation on site releases allowing the best of both worlds—long cache times, but instant updates when needed.&lt;/p&gt;
&lt;p&gt;Even where caching is correctly configured, this can be ignored through the use of unique query string parameters for analytics measurement. These may look like different content to the CDN despite being the same, and so the cached version will not be used.&lt;/p&gt;
&lt;p&gt;Older or less visited content may also not be cached, which can result in higher TTFB values on some pages than others. Increasing caching times can reduce the impact of this, but be aware that with increased caching times comes a greater possibility of serving potentially stale content.&lt;/p&gt;
&lt;p&gt;The impact of cached content does not just affect those using CDNs. Server infrastructure may need to generate content from costly database lookups when cached content cannot be reused. More frequently accessed data or precached pages can often perform better.&lt;/p&gt;
&lt;h3 id=&quot;avoid-multiple-page-redirects&quot;&gt;Avoid multiple page redirects &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-ttfb/#avoid-multiple-page-redirects&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One common contributor to a high TTFB is &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Redirections&quot; rel=&quot;noopener&quot;&gt;redirects&lt;/a&gt;. Redirects occur when a navigation request for a document receives a response that informs the browser that the resource exists at another location. One redirect can certainly add unwanted latency to a navigation request, but it can certainly get worse if that redirect points to another resource that results in &lt;em&gt;another&lt;/em&gt; redirect—and so on. This can particularly impact sites that receive high volumes of visitors from advertisements or newsletters, since they often redirect via analytics services for measurement purposes. Eliminating redirects under your direct control can help to achieve a good TTFB.&lt;/p&gt;
&lt;p&gt;There are two types of redirects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Same-origin redirects&lt;/strong&gt;, where the redirect occurs entirely on your website.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-origin redirects&lt;/strong&gt;, where the redirect occurs initially on another origin—such as from a social media URL shortening service, for example—before arriving at your website.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You want to focus on eliminating same-origin redirects, as this is something you will have direct control over. This would involve checking links on your website to see if any of them result in a &lt;code&gt;302&lt;/code&gt; or &lt;code&gt;301&lt;/code&gt; response code. Often this can be the result of not including the &lt;code&gt;https://&lt;/code&gt; scheme (so browsers default to &lt;code&gt;http://&lt;/code&gt; which then redirects) or because trailing slashes are not appropriately included or excluded in the URL (for example, visiting this page via &lt;a href=&quot;https://web.dev/optimize-ttfb&quot;&gt;https://web.dev/optimize-ttfb&lt;/a&gt; (without the final slash) works but requires a redirect to &lt;a href=&quot;https://web.dev/optimize-ttfb/&quot;&gt;https://web.dev/optimize-ttfb/&lt;/a&gt;) (with the final slash).&lt;/p&gt;
&lt;p&gt;Cross-origin redirects are trickier as these are often outside of your control, but try to avoid multiple redirects where possible—for example, by using multiple link shorteners when sharing links. Ensure the URL provided to advertisers or newsletters is the correct final URL, so as not to add another redirect to the ones used by those services.&lt;/p&gt;
&lt;p&gt;Another important source of redirect time can come from HTTP-to-HTTPS redirects. One way you can get around this is to use the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Strict-Transport-Security&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Strict-Transport-Security&lt;/code&gt; header&lt;/a&gt; (HSTS), which will enforce HTTPS on the first visit to an origin, and then will tell the browser to immediately access the origin through the HTTPS scheme on future visits.&lt;/p&gt;
&lt;p&gt;Once you have a good HSTS policy in place, you can speed things up on the first visit to an origin &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Strict-Transport-Security#preloading_strict_transport_security&quot; rel=&quot;noopener&quot;&gt;by adding your site to the HSTS preload list&lt;/a&gt;.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Be &lt;strong&gt;very careful&lt;/strong&gt; when implementing HSTS, as setting too aggressive a policy without sufficient testing &lt;a href=&quot;https://web.dev/bbc-hsts/#deploying-hsts&quot;&gt;can break your website&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;stream-markup-to-the-browser&quot;&gt;Stream markup to the browser &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-ttfb/#stream-markup-to-the-browser&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Browsers are optimized to process markup efficiently when it is streamed, meaning that markup is handled in chunks as it arrives from the server. This is crucial where large markup payloads are concerned, as it means the browser can parse that the chunks of markup incrementally, as opposed to waiting for the entire response to arrive before parsing can begin.&lt;/p&gt;
&lt;p&gt;Though browsers are great at handling streaming markup, it&#39;s crucial to do all that you can to keep that stream flowing so those initial bits of markup are on their way as soon as possible. If the backend is holding things up, that&#39;s a problem. Because backend stacks are numerous, it would be beyond the scope of this guide to cover every single stack and the issues that could arise in each specific one.&lt;/p&gt;
&lt;p&gt;React, for example—and other frameworks that can &lt;a href=&quot;https://web.dev/rendering-on-the-web/#server-rendering&quot;&gt;render markup on demand on the server&lt;/a&gt;—have used a synchronous approach to server-side rendering. However, newer versions of React have implemented &lt;a href=&quot;https://reactjs.org/docs/react-dom-server.html#overview&quot; rel=&quot;noopener&quot;&gt;server methods for streaming markup&lt;/a&gt; as it is being rendered. This means you don&#39;t have to wait for a React server API method to render the entire response before it&#39;s sent.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Not every language runtime can take advantage of streaming server-side rendering. JavaScript runtimes such as &lt;a href=&quot;https://deno.land/&quot;&gt;Deno&lt;/a&gt; and &lt;a href=&quot;https://nodejs.org/&quot;&gt;Node.js&lt;/a&gt; support this out of the box, but other platforms may not support it. Check to see if this is the case for you, and see what you can do to upgrade or switch your runtime for better server-side rendering performance. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Another way to ensure markup is streamed to the browser quickly is to rely on &lt;a href=&quot;https://web.dev/rendering-on-the-web/#static-rendering&quot;&gt;static rendering&lt;/a&gt; which generates HTML files during build time. With the full file available immediately, web servers can start sending the file immediately and the inherent nature of HTTP will result in streaming markup. While this approach isn&#39;t suitable for every page on every website—such as those requiring a dynamic response as part of the user experience—it can be beneficial for those pages that don&#39;t require markup to be personalized to a specific user.&lt;/p&gt;
&lt;h3 id=&quot;use-a-service-worker&quot;&gt;Use a service worker &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-ttfb/#use-a-service-worker&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Service_Worker_API&quot; rel=&quot;noopener&quot;&gt;Service Worker API&lt;/a&gt; can have a big impact on the TTFB for both documents and the resources they load. The reason for this is that a service worker acts as a proxy between the browser and the server—but whether there is an impact on your website&#39;s TTFB depends on how you set up your service worker, and if that setup aligns with your application requirements.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Use a &lt;a href=&quot;https://developer.chrome.com/docs/workbox/caching-strategies-overview/#stale-while-revalidate&quot; rel=&quot;noopener&quot;&gt;stale-while-revalidate strategy&lt;/a&gt; for assets.&lt;/strong&gt; If an asset is in the service worker cache—be it a document or a resource the document requires—the stale-while-revalidate strategy will service that resource from the cache &lt;em&gt;first&lt;/em&gt;, then will download that asset in the background and serve it from the cache for future interactions.
&lt;ul&gt;
&lt;li&gt;If you have document resources that don&#39;t change very often, using a stale-while-revalidate strategy can make a page&#39;s TTFB nearly instant. However, this doesn&#39;t work so well if your website sends dynamically generated markup—such as markup that changes based on whether a user is authenticated. In such cases, you&#39;ll always want to hit the network &lt;em&gt;first&lt;/em&gt;, so the document is as fresh as possible.&lt;/li&gt;
&lt;li&gt;If your document loads non-critical resources that change with some frequency, but fetching the stale resource won&#39;t greatly affect the user experience—such as select images or other resources that aren&#39;t critical—the TTFB for those resources can be greatly reduced using a stale-while-revalidate strategy.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use a &lt;a href=&quot;https://developer.chrome.com/docs/workbox/faster-multipage-applications-with-streams/&quot; rel=&quot;noopener&quot;&gt;streaming service worker architecture&lt;/a&gt; if possible.&lt;/strong&gt; This service worker architecture uses an approach where parts of a document resource are stored in the service worker cache, and combined with content partials during the navigation request. The resulting effect of using this service worker pattern is that your navigation will be quite fast, while smaller HTML payloads are downloaded from the network. While this service worker pattern doesn&#39;t work for every website, TTFB times for document resources can be practically instant for sites that can use it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use &lt;a href=&quot;https://developer.chrome.com/blog/app-shell/&quot; rel=&quot;noopener&quot;&gt;the app shell model&lt;/a&gt; for client-rendered applications.&lt;/strong&gt; This model fits best for SPAs where the &amp;quot;shell&amp;quot; of the page can be delivered instantly from the service worker cache, and the dynamic content of the page is populated and rendered later on in the page lifecycle.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;use-103-early-hints-for-render-critical-resources&quot;&gt;Use &lt;code&gt;103 Early Hints&lt;/code&gt; for render-critical resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-ttfb/#use-103-early-hints-for-render-critical-resources&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;No matter how well your application backend is optimized, there could still be a significant amount of work the server needs to do in order to prepare a response, including expensive (yet necessary) database work that delays the navigation response from arriving as quickly as it could. The potential effect of this is that some subsequent render-critical resources could be delayed, such as CSS or—in some cases—JavaScript that renders markup on the client.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/blog/early-hints/&quot; rel=&quot;noopener&quot;&gt;The &lt;code&gt;103 Early Hints&lt;/code&gt; header&lt;/a&gt; is an early response code that the server can send to the browser while the backend is busy preparing markup. This header can be used to hint to the browser that there are render-critical resources the page should begin downloading while the markup is being prepared. For &lt;a href=&quot;https://caniuse.com/mdn-http_status_103&quot; rel=&quot;noopener&quot;&gt;supporting browsers&lt;/a&gt;, the effect can be faster document rendering (CSS) and quicker availability of core page functionality (JavaScript).&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; If your website doesn&#39;t do a lot of processing on the backend to prepare markup—for example, static sites—then &lt;code&gt;103 Early Hints&lt;/code&gt; probably won&#39;t help much. It tends to work best for sites that involve considerable backend time before markup can be sent to the user. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-ttfb/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Since there are so many combinations of backend application stacks, there&#39;s no one article that can encapsulate &lt;em&gt;everything&lt;/em&gt; you can do to lower your website&#39;s TTFB. However, these are some options you can explore to try and get things going just a little bit faster on the server side of things.&lt;/p&gt;
&lt;p&gt;As with optimizing every metric, the approach is largely similar: measure your TTFB in the field, use lab tools to drill down into the causes, and then apply optimizations where possible. Not every single technique here may be viable for your situation, but some will be. As always, you&#39;ll need to keep a close eye on your field data, and make adjustments as needed to ensure the fastest possible user experience.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hero image by &lt;a href=&quot;https://unsplash.com/@tvick&quot; rel=&quot;noopener&quot;&gt;Taylor Vick&lt;/a&gt;, sourced from Unsplash.&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>Jeremy Wagner</name>
    </author><author>
      <name>Barry Pollard</name>
    </author>
  </entry>
  
  <entry>
    <title>Our top Core Web Vitals recommendations for 2023</title>
    <link href="https://web.dev/top-cwv-2023/"/>
    <updated>2023-01-10T00:00:00Z</updated>
    <id>https://web.dev/top-cwv-2023/</id>
    <content type="html" mode="escaped">&lt;p&gt;Over the years, we at Google have made a lot of recommendations to web developers on how to improve performance.&lt;/p&gt;
&lt;p&gt;While each of these recommendations, individually, may improve performance for many sites, the full set of recommendations is admittedly overwhelming and, realistically, there&#39;s no way any one person or site could follow all of them.&lt;/p&gt;
&lt;p&gt;Unless web performance is your day job, it&#39;s probably not obvious which recommendations are going to have the largest positive impact on your site. For example, you might have read that implementing critical CSS can improve load performance, and you may have also heard that it&#39;s important to optimize your images. But, if you don&#39;t have time to work on both things, how would you decide which one to pick?&lt;/p&gt;
&lt;p&gt;On the Chrome team, we&#39;ve spent the last year trying to answer this question: &lt;em&gt;what are the most important recommendations we can give to developers to help them improve performance for their users?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To adequately answer this question we have to consider not just the technical merits of any given recommendation, but also human and organizational factors that influence the likelihood that developers will actually be able to adopt these recommendations. In other words, some recommendations may be hugely impactful in theory, but in reality very few sites will have the time or resources to implement them. Similarly, some recommendations are critical, but most websites are already following these practices.&lt;/p&gt;
&lt;p&gt;In short, we wanted our list of top web performance recommendations to focus on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Recommendations we believe will have the &lt;strong&gt;largest real-world impact&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Recommendations that are &lt;strong&gt;relevant and applicable to most sites&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Recommendations that are &lt;strong&gt;realistic for most developers to implement&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Over the past year we&#39;ve spent a lot of time auditing the full set of performance recommendations we make, and assessing each of them (both qualitatively and quantitatively) against the above three criteria.&lt;/p&gt;
&lt;p&gt;This post outlines our top recommendations to improve performance for each of the &lt;a href=&quot;https://web.dev/vitals/#core-web-vitals&quot;&gt;Core Web Vitals&lt;/a&gt; metrics. If you&#39;re new to web performance, or if you&#39;re trying to decide what will give you the biggest bang for your buck, we think these recommendations are the best place to start.&lt;/p&gt;
&lt;h2 id=&quot;largest-contentful-paint-lcp&quot;&gt;Largest Contentful Paint (LCP) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/top-cwv-2023/#largest-contentful-paint-lcp&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Our first set of recommendations are for &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt;, which is a measure of load performance. Of the three Core Web Vitals metrics, LCP is the one that the largest number of sites struggle with—only &lt;a href=&quot;https://datastudio.google.com/s/nw4gcbKA5o4&quot; rel=&quot;noopener&quot;&gt;about half&lt;/a&gt; of all sites on the web today meet the &lt;a href=&quot;https://web.dev/lcp/#what-is-a-good-lcp-score&quot;&gt;recommended threshold&lt;/a&gt;—so let&#39;s start there.&lt;/p&gt;
&lt;h3 id=&quot;ensure-the-lcp-resource-is-discoverable-from-the-html-source&quot;&gt;Ensure the LCP resource is discoverable from the HTML source &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/top-cwv-2023/#ensure-the-lcp-resource-is-discoverable-from-the-html-source&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;According to the &lt;a href=&quot;https://almanac.httparchive.org/en/2022/&quot; rel=&quot;noopener&quot;&gt;2022 Web Almanac&lt;/a&gt; by HTTP Archive, &lt;a href=&quot;https://almanac.httparchive.org/en/2022/performance#fig-8&quot; rel=&quot;noopener&quot;&gt;72%&lt;/a&gt; of mobile pages have an image as their LCP element, which means that for most sites to optimize their LCP, they&#39;ll need to ensure those images can load quickly.&lt;/p&gt;
&lt;p&gt;What may not be obvious to many developers is that the time it takes to load an image is just one part of the challenge. Another critical part is the time &lt;em&gt;before&lt;/em&gt; an image starts loading, and HTTP Archive data suggests that&#39;s actually where many sites get tripped up.&lt;/p&gt;
&lt;p&gt;In fact, of the pages where the LCP element was an image, &lt;a href=&quot;https://almanac.httparchive.org/en/2022/performance#lcp-static-discoverability&quot; rel=&quot;noopener&quot;&gt;39%&lt;/a&gt; of those images had source URLs that were not &lt;a href=&quot;https://web.dev/optimize-lcp/#optimize-when-the-resource-is-discovered&quot;&gt;discoverable&lt;/a&gt; from the HTML document source. In other words, those URLs were not found in standard HTML attributes (such as &lt;code&gt;&amp;lt;img src=&amp;quot;...&amp;quot;&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;link rel=&amp;quot;preload&amp;quot; href=&amp;quot;...&amp;quot;&amp;gt;&lt;/code&gt;), which would allow the browser to quickly discover them and start loading them right away.&lt;/p&gt;
&lt;p&gt;If a page needs to wait for CSS or JavaScript files to be fully downloaded, parsed, and processed before the image can even start loading, it may already be too late.&lt;/p&gt;
&lt;p&gt;As a general rule, if your LCP element is an image, the image&#39;s URL should always be discoverable from the HTML source. Some tips to make that possible are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Load the image using an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element with the &lt;code&gt;src&lt;/code&gt; or &lt;code&gt;srcset&lt;/code&gt; attribute.&lt;/strong&gt; Do not use non-standard attributes like &lt;code&gt;data-src&lt;/code&gt; that require JavaScript in order to render, as that will always be slower. &lt;a href=&quot;https://almanac.httparchive.org/en/2022/performance#lcp-lazy-loading&quot; rel=&quot;noopener&quot;&gt;9%&lt;/a&gt; of pages obscure their LCP image behind &lt;code&gt;data-src&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Prefer server-side rendering (SSR) over client-side rendering (CSR),&lt;/strong&gt; as SSR implies that the full page markup (including the image) is present in the HTML source. CSR solutions require JavaScript to run before the image can be discovered.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;If your image needs to be referenced from an external CSS or JS file, you can still include it in the HTML source via a &lt;code&gt;&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&lt;/code&gt; tag.&lt;/strong&gt; Note that images referenced by inline styles are not discoverable by the browser&#39;s &lt;a href=&quot;https://web.dev/preload-scanner/&quot;&gt;preload scanner&lt;/a&gt;, so even though they&#39;re found in the HTML source, discovery of them might still be blocked on the loading of other resources, so preloading can help in these cases.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To help you understand if your LCP image has discoverability problems, Lighthouse will be releasing a &lt;a href=&quot;https://github.com/GoogleChrome/lighthouse/issues/13738&quot; rel=&quot;noopener&quot;&gt;new audit&lt;/a&gt; in version 10.0 (expected January 2023).&lt;/p&gt;
&lt;p&gt;Ensuring the LCP resource is discoverable from the HTML source can lead to measurable improvements and it also unlocks additional opportunities to prioritize the resource, which is our next recommendation.&lt;/p&gt;
&lt;h3 id=&quot;ensure-the-lcp-resource-is-prioritized&quot;&gt;Ensure the LCP resource is prioritized &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/top-cwv-2023/#ensure-the-lcp-resource-is-prioritized&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Making sure the LCP resource can be discovered from the HTML source is a critical first step in ensuring the LCP resource can start loading early, but another important step is ensuring that the loading of that resource is &lt;a href=&quot;https://web.dev/optimize-lcp/#optimize-the-priority-the-resource-is-given&quot;&gt;prioritized&lt;/a&gt; and doesn&#39;t get queued behind a bunch of other, less important resources.&lt;/p&gt;
&lt;p&gt;For example, even if your LCP image is present in the HTML source using a standard &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag, if your page includes a dozen &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of your document before that &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag, it may be a while before your image resource starts loading.&lt;/p&gt;
&lt;p&gt;The easiest way to solve this problem is to provide a hint to the browser about what resources are the highest priority by setting the new &lt;a href=&quot;https://web.dev/fetch-priority/&quot;&gt;&lt;code&gt;fetchpriority=&amp;quot;high&amp;quot;&lt;/code&gt;&lt;/a&gt; attribute on the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag that loads your LCP image. This instructs the browser to load it earlier, rather than waiting for those scripts to complete.&lt;/p&gt;
&lt;p&gt;According to the Web Almanac, only &lt;a href=&quot;https://almanac.httparchive.org/en/2022/performance#lcp-prioritization&quot; rel=&quot;noopener&quot;&gt;0.03%&lt;/a&gt; of eligible pages are taking advantage of this new API, meaning there is plenty of opportunity for most sites on the web to improve LCP with very little work. While the &lt;code&gt;fetchpriority&lt;/code&gt; attribute is currently only supported in Chromium-based browsers, this API is a progressive enhancement that other browsers just ignore, so we strongly recommend developers use it now.&lt;/p&gt;
&lt;p&gt;For non-Chromium browsers, the only way to ensure the LCP resource is prioritized above other resources is to reference it earlier in the document. Using the example again of a site with lots of &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of the document, if you wanted to ensure your LCP resource was prioritized ahead of those script resources, you could add a &lt;code&gt;&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&lt;/code&gt; tag before any of those scripts, or you could move those scripts to below the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; later in the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;. While this works, it&#39;s less ergonomic than using &lt;code&gt;fetchpriority&lt;/code&gt;, so we hope other browsers add support soon.&lt;/p&gt;
&lt;p&gt;Another critical aspect of prioritizing the LCP resource is to ensure you don&#39;t do anything that causes it to be &lt;strong&gt;deprioritized&lt;/strong&gt;, such as adding the &lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt; attribute. Today, &lt;a href=&quot;https://almanac.httparchive.org/en/2022/performance#lcp-lazy-loading&quot; rel=&quot;noopener&quot;&gt;10%&lt;/a&gt; of pages actually set &lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt; on their LCP image. Beware of image optimization solutions that indiscriminately apply lazy-loading behavior to all images. If they provide a way to override that behavior, be sure to use it for the LCP image. If you&#39;re not sure which image will be the LCP, try using heuristics to pick a reasonable candidate.&lt;/p&gt;
&lt;p&gt;Deferring non-critical resources is another way to effectively boost the relative priority of the LCP resource. For example, scripts that are not powering the user interface (like analytics scripts or social widgets) can be safely postponed until after the &lt;code&gt;load&lt;/code&gt; event fires, which ensures they won&#39;t compete with other critical resources (such as the LCP resource) for network bandwidth.&lt;/p&gt;
&lt;p&gt;To summarize, you should follow these best practices to ensure that the LCP resource is loaded early, and at high priority:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Add &lt;code&gt;fetchpriority=&amp;quot;high&amp;quot;&lt;/code&gt; to the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag of your LCP image.&lt;/strong&gt; If the LCP resource is loaded via a&lt;code&gt; &amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&lt;/code&gt; tag, fear not because you can also set &lt;code&gt;fetchpriority=&amp;quot;high&amp;quot;&lt;/code&gt; on that!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Never set &lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt; on the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag of your LCP image.&lt;/strong&gt; Doing this will deprioritize your image and delay when it starts loading.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Defer non-critical resources when possible.&lt;/strong&gt; Either by moving them to the end of your document, using native lazy-loading for &lt;a href=&quot;https://web.dev/browser-level-image-lazy-loading/&quot;&gt;images&lt;/a&gt; or &lt;a href=&quot;https://web.dev/iframe-lazy-loading/&quot;&gt;iframes&lt;/a&gt;, or loading them asynchronously via JavaScript.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;use-a-cdn-to-optimize-document-and-resource-ttfb&quot;&gt;Use a CDN to optimize document and resource TTFB &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/top-cwv-2023/#use-a-cdn-to-optimize-document-and-resource-ttfb&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The previous two recommendations focused on making sure your LCP resource is discovered early and prioritized so it can start loading right away. The final piece to this puzzle is making sure the initial document response arrives as quickly as possible too.&lt;/p&gt;
&lt;p&gt;The browser cannot start loading any subresources until it receives the first byte of the initial HTML document response, and the sooner that happens, the sooner everything else can start happening as well.&lt;/p&gt;
&lt;p&gt;This time is known as &lt;a href=&quot;https://web.dev/ttfb/&quot;&gt;Time to First Byte (TTFB)&lt;/a&gt;, and the best way to reduce TTFB is to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Serve your content as geographically close to your users as possible&lt;/li&gt;
&lt;li&gt;Cache that content so recently-requested content can be served again quickly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The best way to do both of these things is to &lt;a href=&quot;https://web.dev/content-delivery-networks/&quot;&gt;use a CDN&lt;/a&gt;. CDNs distribute your resources to edge servers, which are spread across the globe, thus limiting the distance those resources have to travel over the wire to your users. CDNs also usually have fine-grained caching controls that can be customized and optimized for your site&#39;s needs.&lt;/p&gt;
&lt;p&gt;Many developers are familiar with using a CDN to host static assets, but CDNs can serve and cache HTML documents as well, even those that are dynamically generated.&lt;/p&gt;
&lt;p&gt;According to the Web Almanac, only &lt;a href=&quot;https://almanac.httparchive.org/en/2022/cdn#cdn-adoption&quot; rel=&quot;noopener&quot;&gt;29%&lt;/a&gt; of HTML document requests were served from a CDN, which means there is significant opportunity for sites to claim additional savings.&lt;/p&gt;
&lt;p&gt;Some tips for configuring your CDNs are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Consider increasing how long content is cached for (for example, is it actually critical that content is always fresh? Or can it be a few minutes stale?).&lt;/li&gt;
&lt;li&gt;Consider maybe even caching content indefinitely, and then purging the cache if/when you make an update.&lt;/li&gt;
&lt;li&gt;Explore whether you can move dynamic logic currently running on your origin server to the &lt;a href=&quot;https://en.wikipedia.org/wiki/Edge_computing&quot; rel=&quot;noopener&quot;&gt;edge&lt;/a&gt; (a feature of most modern CDNs).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In general, any time you can serve content directly from the edge (avoiding a trip to your origin server) it&#39;s a performance win. And even in cases where you &lt;em&gt;do&lt;/em&gt; have to make the journey all the way back to your origin server, CDNs are generally optimized to do that much more quickly, so it&#39;s a win either way.&lt;/p&gt;
&lt;h2 id=&quot;cumulative-layout-shift-cls&quot;&gt;Cumulative Layout Shift (CLS) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/top-cwv-2023/#cumulative-layout-shift-cls&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The next set of recommendations are for &lt;a href=&quot;https://web.dev/cls/&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt;, which is a measure of visual stability on web pages. While CLS has &lt;a href=&quot;https://datastudio.google.com/s/gFjrTptD140&quot; rel=&quot;noopener&quot;&gt;improved a lot&lt;/a&gt; on the web since 2020, about a quarter of websites still do not meet the &lt;a href=&quot;https://web.dev/cls/#what-is-a-good-cls-score&quot;&gt;recommended threshold&lt;/a&gt;, so there remains a big opportunity for many sites to improve their user experience.&lt;/p&gt;
&lt;h3 id=&quot;set-explicit-sizes-on-any-content-loaded-from-the-page&quot;&gt;Set explicit sizes on any content loaded from the page &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/top-cwv-2023/#set-explicit-sizes-on-any-content-loaded-from-the-page&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/cls/#layout-shifts-in-detail&quot;&gt;Layout shifts&lt;/a&gt; usually happen when existing content moves after other content finishes loading. Therefore, the primary way to mitigate this is to reserve any required space in advance as much as possible.&lt;/p&gt;
&lt;p&gt;The most straightforward way to fix layout shifts caused by unsized images is to &lt;strong&gt;explicitly set &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes&lt;/strong&gt; (or equivalent CSS properties). However, according to HTTP Archive, &lt;a href=&quot;https://almanac.httparchive.org/en/2022/performance#explicit-dimensions&quot; rel=&quot;noopener&quot;&gt;72%&lt;/a&gt; of pages have at least one unsized image. Without an explicit size, browsers will initially set a default height of &lt;code&gt;0px&lt;/code&gt; and may cause a noticeable layout shift when the image is finally loaded and the dimensions are discovered. This represents both a huge opportunity for the collective web—and that opportunity requires much less effort than some of the other recommendations suggested in this article.&lt;/p&gt;
&lt;p&gt;It&#39;s also important to keep in mind that images are not the only contributors to CLS. Layout shifts may be caused by other content that typically loads in after the page is initially rendered, including third-party ads or embedded videos. The &lt;a href=&quot;https://web.dev/aspect-ratio/&quot;&gt;&lt;code&gt;aspect-ratio&lt;/code&gt;&lt;/a&gt; property can help combat this. It&#39;s a relatively new CSS feature that allows developers to explicitly provide an aspect ratio to images as well as non-image elements. This will allow you to set a dynamic  &lt;code&gt;width&lt;/code&gt; (for example based on screen size), and have the browser automatically calculate the appropriate height, in much the same way as they do for images with dimensions.&lt;/p&gt;
&lt;p&gt;Sometimes it&#39;s not possible to know the exact size of dynamic content since it is, by its very nature, dynamic. However, even if you don&#39;t know the exact size, you can still take steps to reduce the severity of layout shifts. &lt;strong&gt;Setting a sensible &lt;code&gt;min-height&lt;/code&gt;&lt;/strong&gt; is almost always better than allowing the browser to use the default height of &lt;code&gt;0px&lt;/code&gt; for an empty element. Using a &lt;code&gt;min-height&lt;/code&gt; is also usually an easy fix as it still allows the container to grow to the final content height if needed—it has just reduced that amount of growth from the full amount to a hopefully more tolerable level.&lt;/p&gt;
&lt;h3 id=&quot;ensure-pages-are-eligible-for-bfcache&quot;&gt;Ensure pages are eligible for bfcache &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/top-cwv-2023/#ensure-pages-are-eligible-for-bfcache&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Browsers use a navigation mechanism called the &lt;a href=&quot;https://web.dev/bfcache/&quot;&gt;back/forward cache&lt;/a&gt;—or bfcache for short—to instantly load a page from earlier or later in the browser history directly from a memory snapshot.&lt;/p&gt;
&lt;p&gt;The bfcache is a significant browser-level performance optimization, and it entirely eliminates the layout shifts during page load, which for many sites is where most of their CLS occurs. The introduction of the bfcache caused &lt;a href=&quot;https://twitter.com/anniesullie/status/1491399685961293828?s=20&amp;amp;t=k7JgTjdO21uMpeOuOofroA&quot; rel=&quot;noopener&quot;&gt;the biggest improvement in CLS&lt;/a&gt; that we saw in 2022.&lt;/p&gt;
&lt;p&gt;Despite this, &lt;a href=&quot;https://almanac.httparchive.org/en/2022/performance#bfcache-eligibility&quot; rel=&quot;noopener&quot;&gt;a significant number of websites&lt;/a&gt; are ineligible for the bfcache and so are missing out on this free web performance win for a significant number of navigations. Unless your page is loading sensitive information that you don&#39;t want to be restored from memory, you&#39;ll want to make sure that your pages are eligible.&lt;/p&gt;
&lt;p&gt;Site owners should check that their pages are &lt;a href=&quot;https://web.dev/bfcache/#optimize-your-pages-for-bfcache&quot;&gt;eligible for the bfcache&lt;/a&gt; and work on any reasons why they are not. Chrome already &lt;a href=&quot;https://web.dev/bfcache/#test-to-ensure-your-pages-are-cacheable&quot;&gt;has a bfcache tester in DevTools&lt;/a&gt; and this year we plan to enhance tooling here with &lt;a href=&quot;https://github.com/GoogleChrome/lighthouse/issues/13960&quot; rel=&quot;noopener&quot;&gt;a new Lighthouse audit performing a similar test&lt;/a&gt; and &lt;a href=&quot;https://chromestatus.com/feature/5684908759449600&quot; rel=&quot;noopener&quot;&gt;an API to measure this in the field&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While we have included the bfcache in the CLS section, as we saw the biggest gains there so far, the bfcache will generally also improve other Core Web Vitals too. It is one of &lt;a href=&quot;https://calendar.perfplanet.com/2022/fast-is-good-instant-is-better/&quot; rel=&quot;noopener&quot;&gt;a number of instant navigations&lt;/a&gt; available to drastically improve page navigations.&lt;/p&gt;
&lt;h3 id=&quot;avoid-animationstransitions-that-use-layout-inducing-css-properties&quot;&gt;Avoid animations/transitions that use layout-inducing CSS properties &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/top-cwv-2023/#avoid-animationstransitions-that-use-layout-inducing-css-properties&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Another common source of layout shifts is when elements are animated. For example, cookie banners or other notification banners that slide in from the top or bottom are often a contributor to CLS. This is particularly problematic when these banners push other content out of the way, but even when they don&#39;t, animating them can still impact CLS.&lt;/p&gt;
&lt;p&gt;While HTTP Archive data can&#39;t conclusively connect animations to layout shifts, the data does show that pages that animate any CSS property that &lt;em&gt;could&lt;/em&gt; affect layout are 15% less likely to have &amp;quot;good&amp;quot; CLS than pages overall. Some properties are associated with worse CLS than others. For instance, pages that animate &lt;code&gt;margin&lt;/code&gt; or &lt;code&gt;border&lt;/code&gt; widths have &amp;quot;poor&amp;quot; CLS at almost twice the rate that pages overall are assessed as poor.&lt;/p&gt;
&lt;p&gt;This is perhaps not surprising, because any time you transition or animate &lt;em&gt;any&lt;/em&gt; layout-inducing CSS property, it will result in &lt;a href=&quot;https://web.dev/cls/#layout-shifts-in-detail&quot;&gt;layout shifts&lt;/a&gt;, and if those layout shifts are not within 500 milliseconds of a user interaction, they will impact CLS.&lt;/p&gt;
&lt;p&gt;What may be surprising to some developers is that this is true even in cases where the element is taken outside of the normal document flow. For example, absolutely positioned elements that animate &lt;code&gt;top&lt;/code&gt; or &lt;code&gt;left&lt;/code&gt; will cause layout shifts, even if they aren&#39;t pushing other content around. However, if instead of animating &lt;code&gt;top&lt;/code&gt; or &lt;code&gt;left&lt;/code&gt; you animate &lt;code&gt;transform:translateX()&lt;/code&gt; or &lt;code&gt;transform:translateY()&lt;/code&gt;, it won&#39;t cause the browser to update page layout and thus won&#39;t produce any layout shifts.&lt;/p&gt;
&lt;p&gt;Preferring animation of CSS properties that can be updated on the browser&#39;s compositor thread has long been &lt;a href=&quot;https://web.dev/animations-guide/&quot;&gt;a performance best practice&lt;/a&gt; because it moves that work onto the GPU and off the main thread. And in addition to it being a general performance best practice, it can also help improve CLS.&lt;/p&gt;
&lt;p&gt;As a general rule, never animate or transition any CSS property that requires the browser to update the page layout, unless you&#39;re doing it in response to a user tap or key press (though &lt;a href=&quot;https://web.dev/cls/#user-initiated-layout-shifts&quot;&gt;not &lt;code&gt;hover&lt;/code&gt;&lt;/a&gt;). And whenever possible, prefer transitions and animations using the CSS &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/transform&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;transform&lt;/code&gt;&lt;/a&gt; property.&lt;/p&gt;
&lt;p&gt;The Lighthouse audit &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/non-composited-animations/&quot; rel=&quot;noopener&quot;&gt;Avoid non-composited animations&lt;/a&gt; will warn when a page animates potentially slow CSS properties.&lt;/p&gt;
&lt;h2 id=&quot;first-input-delay-fid&quot;&gt;First Input Delay (FID) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/top-cwv-2023/#first-input-delay-fid&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Our last set of recommendations are for &lt;a href=&quot;https://web.dev/fid/&quot;&gt;First Input Delay (FID)&lt;/a&gt;, which is a measure of a page&#39;s responsiveness to user interactions. While most sites on the web currently &lt;a href=&quot;https://datastudio.google.com/s/vax9YUKhRN0&quot; rel=&quot;noopener&quot;&gt;score very well&lt;/a&gt; on FID, we&#39;ve &lt;a href=&quot;https://web.dev/better-responsiveness-metric/#what-improvements-are-we-considering&quot;&gt;documented&lt;/a&gt; shortcomings of the FID metric in the past, and we believe there is still a lot of opportunity for sites to improve their overall responsiveness to user interactions.&lt;/p&gt;
&lt;p&gt;Our new &lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt; metric is a possible successor to FID, and all of the recommendations below apply equally well to both FID and INP. Given that sites &lt;a href=&quot;https://almanac.httparchive.org/en/2022/performance#inp-as-a-hypothetical-cwv-metric&quot; rel=&quot;noopener&quot;&gt;perform worse&lt;/a&gt; on INP than FID, especially on mobile, we encourage developers to seriously consider these responsiveness recommendations, despite having &amp;quot;good&amp;quot; FID.&lt;/p&gt;
&lt;h3 id=&quot;avoid-or-break-up-long-tasks&quot;&gt;Avoid or break up long tasks &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/top-cwv-2023/#avoid-or-break-up-long-tasks&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tasks are any piece of discrete work that the browser does. Tasks include rendering, layout, parsing, and compiling and executing scripts. When tasks become &lt;a href=&quot;https://web.dev/long-tasks-devtools/#what-are-long-tasks&quot;&gt;long tasks&lt;/a&gt;—that is, 50 milliseconds or longer—they block the main thread from being able to respond quickly to user inputs.&lt;/p&gt;
&lt;p&gt;Per the Web Almanac, there&#39;s &lt;a href=&quot;https://almanac.httparchive.org/en/2022/javascript#long-tasksblocking-time&quot; rel=&quot;noopener&quot;&gt;plenty of evidence&lt;/a&gt; to suggest that developers could be doing more to avoid or break up long tasks. While breaking up long tasks may not be as low of an effort as other recommendations in this article, it&#39;s less effort than other techniques not offered in this article.&lt;/p&gt;
&lt;p&gt;While you should always strive to do as little work as possible in JavaScript, you can help the main thread quite a bit by &lt;a href=&quot;https://web.dev/optimize-long-tasks/&quot;&gt;breaking up long tasks into smaller ones&lt;/a&gt;. You can accomplish this by &lt;a href=&quot;https://web.dev/optimize-long-tasks/#use-asyncawait-to-create-yield-points&quot;&gt;yielding to the main thread&lt;/a&gt; often so that rendering updates and other user interactions can occur more quickly.&lt;/p&gt;
&lt;p&gt;Another option is to consider using APIs such as &lt;a href=&quot;https://web.dev/optimize-long-tasks/#yield-only-when-necessary&quot;&gt;&lt;code&gt;isInputPending&lt;/code&gt;&lt;/a&gt; and the &lt;a href=&quot;https://web.dev/optimize-long-tasks/#a-dedicated-scheduler-api&quot;&gt;Scheduler API&lt;/a&gt;. &lt;code&gt;isInputPending&lt;/code&gt; is a function that returns a boolean value that indicates whether a user input is pending. If it returns &lt;code&gt;true&lt;/code&gt;, you can yield to the main thread so it can handle the pending user input.&lt;/p&gt;
&lt;p&gt;The Scheduler API is a more advanced approach, which allows you to schedule work based on a system of priorities that take into account whether the work being done is user-visible or backgrounded.&lt;/p&gt;
&lt;p&gt;By breaking up long tasks, you&#39;re giving the browser more opportunities to fit in critical user-visible work, such as dealing with interactions and any resulting rendering updates.&lt;/p&gt;
&lt;h3 id=&quot;avoid-unnecessary-javascript&quot;&gt;Avoid unnecessary JavaScript &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/top-cwv-2023/#avoid-unnecessary-javascript&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There&#39;s no doubt about it: &lt;a href=&quot;https://almanac.httparchive.org/en/2022/javascript#how-much-javascript-do-we-load&quot; rel=&quot;noopener&quot;&gt;websites are shipping more JavaScript than ever before&lt;/a&gt;, and the trend doesn&#39;t look like it&#39;s changing any time soon. When you ship too much JavaScript, you&#39;re creating an environment where tasks are competing for the main thread&#39;s attention. This can definitely affect your website&#39;s responsiveness, especially during that crucial startup period.&lt;/p&gt;
&lt;p&gt;This is not an unsolvable problem, however. You do have some options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use the &lt;a href=&quot;https://developer.chrome.com/docs/devtools/coverage/&quot; rel=&quot;noopener&quot;&gt;coverage tool&lt;/a&gt; in Chrome DevTools to find unused code in your website&#39;s resources. By reducing the size of the resources you need during startup, you can ensure your website spends less time parsing and compiling code, which leads to a smoother initial user experience.&lt;/li&gt;
&lt;li&gt;Sometimes the unused code you find using the coverage tool is marked &amp;quot;unused&amp;quot; because it wasn&#39;t executed during startup, but is still necessary for some functionality in the future. This is code that you can move to a separate bundle via &lt;a href=&quot;https://web.dev/reduce-javascript-payloads-with-code-splitting/&quot;&gt;code splitting&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;If you&#39;re using a tag manager, be sure to &lt;a href=&quot;https://web.dev/tag-best-practices/&quot;&gt;periodically check your tags to make sure they are optimized&lt;/a&gt;, or even if they&#39;re still being used. Older tags with unused code can be cleared out to make your tag manager&#39;s JavaScript smaller and more efficient.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;avoid-large-rendering-updates&quot;&gt;Avoid large rendering updates &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/top-cwv-2023/#avoid-large-rendering-updates&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;JavaScript isn&#39;t the only thing that can affect your website&#39;s responsiveness. Rendering can be a type of expensive work in its own right—and when large rendering updates happen, they can interfere with your website&#39;s ability to respond to user inputs.&lt;/p&gt;
&lt;p&gt;Optimizing rendering work isn&#39;t a straightforward process, and it often depends on what you&#39;re trying to achieve. Even so, there are some things you can do to ensure that your rendering updates are reasonable, and don&#39;t sprawl into long tasks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Avoid using &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;requestAnimationFrame()&lt;/code&gt;&lt;/a&gt; for doing any non-visual work. &lt;code&gt;requestAnimationFrame()&lt;/code&gt; calls are handled during the rendering phase of the event loop, and when too much work is done during this step, rendering updates can be delayed. It&#39;s essential that any work you&#39;re doing with &lt;code&gt;requestAnimationFrame()&lt;/code&gt; is reserved strictly for tasks that involve rendering updates.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/dom-size/&quot; rel=&quot;noopener&quot;&gt;Keep your DOM size small&lt;/a&gt;. DOM size and the intensity of layout work are correlated. When the renderer has to update the layout for a very large DOM, the work required to recalculate its layout can increase significantly.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/CSS_Containment&quot; rel=&quot;noopener&quot;&gt;Use CSS containment&lt;/a&gt;. CSS containment relies on the CSS &lt;code&gt;contain&lt;/code&gt; property, which gives instructions to the browser about how to do layout work for the container the &lt;code&gt;contain&lt;/code&gt; property is set on, including even isolating the scope of layout and rendering to a specific root in the DOM. It&#39;s not always an easy process, but by isolating areas containing complex layouts, you can avoid doing layout and rendering work for them that isn&#39;t necessary.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/top-cwv-2023/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Improving page performance can seem like a daunting task, especially given that there is a mountain of guidance across the web to consider. By focusing on these recommendations, however, you can approach the problem with focus and purpose, and hopefully move the needle for your website&#39;s Core Web Vitals.&lt;/p&gt;
&lt;p&gt;While the recommendations listed here are by no means exhaustive, we do believe—based on careful analysis of the state of the web—that these recommendations are the most effective ways that sites can improve their Core Web Vitals performance in 2023.&lt;/p&gt;
&lt;p&gt;If you&#39;d like to go beyond the recommendations listed here, check out these optimization guides for more information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/optimize-lcp/&quot;&gt;Optimize LCP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/optimize-cls/&quot;&gt;Optimize CLS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/optimize-fid/&quot;&gt;Optimize FID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/optimize-inp/&quot;&gt;Optimize INP&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&#39;s to a new year, and a faster web for all! May your sites be fast for your users in all the ways that matter most.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Photo by &lt;a href=&quot;https://unsplash.com/@devintavery&quot; rel=&quot;noopener&quot;&gt;Devin Avery&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>Philip Walton</name>
    </author><author>
      <name>Rick Viscomi</name>
    </author><author>
      <name>Barry Pollard</name>
    </author><author>
      <name>Brendan Kenny</name>
    </author><author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Why is CrUX data different from my RUM data?</title>
    <link href="https://web.dev/crux-and-rum-differences/"/>
    <updated>2022-08-15T00:00:00Z</updated>
    <id>https://web.dev/crux-and-rum-differences/</id>
    <content type="html" mode="escaped">&lt;p&gt;The &lt;a href=&quot;https://developer.chrome.com/docs/crux/&quot; rel=&quot;noopener&quot;&gt;Chrome User Experience Report (CrUX)&lt;/a&gt; provides user experience metrics for how real-world Chrome users experience popular destinations on the web. This data is automatically collected by Chrome from users who have opted in, and is made available based on the &lt;a href=&quot;https://developer.chrome.com/docs/crux/methodology/#eligibility&quot; rel=&quot;noopener&quot;&gt;CrUX eligibility criteria&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;CrUX data is therefore available for millions of websites. Many site owners have not had access to field data before, and CrUX has enabled many sites to see the value of this for the first time. As a public dataset, CrUX can also be used for competitive analysis and benchmarking of user experience metrics.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Real_user_monitoring&quot; rel=&quot;noopener&quot;&gt;Real User Monitoring (RUM)&lt;/a&gt; is similar to CrUX, but instead of Chrome automatically collecting user-experience metrics, code is included on the websites to do this collection and feed it back to a RUM provider or analytics solution for  further analysis.&lt;/p&gt;
&lt;p&gt;With both solutions measuring user experience metrics, it is natural to assume that they should be equivalent. It can be confusing when we see differences. This guide will explain why that can happen, and offers suggestions for what to do when the numbers do not align.&lt;/p&gt;
&lt;h2 id=&quot;benefits-of-supplementing-crux-with-a-rum-solution&quot;&gt;Benefits of supplementing CrUX with a RUM solution &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#benefits-of-supplementing-crux-with-a-rum-solution&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;CrUX is a great tool for a consistent view across sites and—as &lt;a href=&quot;https://developer.chrome.com/docs/crux/about/&quot; rel=&quot;noopener&quot;&gt;the official dataset for the Core Web Vitals program&lt;/a&gt;—sites will likely want to keep an eye on what it is showing. The aim of CrUX is to provide a statistically relevant overview of millions of websites for cross comparison.&lt;/p&gt;
&lt;p&gt;However, for a deeper dive in investigating &lt;em&gt;why&lt;/em&gt; data is showing the numbers it is, investing in a full RUM solution to supplement CrUX can give you access to more detailed information than can be made available in a publicly queryable dataset. This can help you explain and improve your metrics in a number of ways.&lt;/p&gt;
&lt;h3 id=&quot;deeper-analysis-to-investigate-issues&quot;&gt;Deeper analysis to investigate issues &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#deeper-analysis-to-investigate-issues&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;CrUX can often be used to point out if you have a problem on your site, but not necessarily exactly where on your site the issue lies nor why. RUM solutions—whether home-grown through &lt;a href=&quot;https://web.dev/debug-performance-in-the-field/&quot;&gt;the likes of the web-vitals library&lt;/a&gt; or some of the many commercial products—can help bridge that gap.&lt;/p&gt;
&lt;p&gt;Using a RUM solution gives you access to much more fine-grained data for all your pages, and on all browsers. It also allows you to slice and dice this data in ways CrUX does not, enabling you to drill down and investigate problem areas of the site. Are they affected by a particular segment of users? Or users who take certain actions? Exactly when did the problem start? These are questions that are much easier to answer with the additional data that a RUM tool can provide.&lt;/p&gt;
&lt;h3 id=&quot;correlating-with-other-business-metrics&quot;&gt;Correlating with other business metrics &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#correlating-with-other-business-metrics&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;RUM also allows you to compare your web performance metrics directly with any business metrics, showing the value in investing in performance, and what other performance work to prioritize. We have numerous &lt;a href=&quot;https://web.dev/tags/case-study/&quot;&gt;case studies&lt;/a&gt; with businesses doing this correlation, such as &lt;a href=&quot;https://web.dev/farfetch/#step-1:-defining-measuring-and-monitoring-metrics&quot;&gt;Farfetch&lt;/a&gt; or &lt;a href=&quot;https://web.dev/economic-times-cwv/&quot;&gt;The Economic Times&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;collecting-other-performance-data&quot;&gt;Collecting other performance data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#collecting-other-performance-data&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A RUM solution allows collection of other custom metrics, tied directly to your specific business. One of the more well-known examples is Twitter’s “&lt;a href=&quot;https://blog.twitter.com/engineering/en_us/a/2012/improving-performance-on-twittercom#:~:text=The%20most%20important%20metric%20we,Tweet%20on%20each%20page&#39;s%20timeline.&quot; rel=&quot;noopener&quot;&gt;Time to first Tweet&lt;/a&gt;” metric. These site-specific measures can then be correlated with Core Web Vital improvements and business metrics.&lt;/p&gt;
&lt;h2 id=&quot;differences-between-two-sets-of-field-data&quot;&gt;Differences between two sets of field data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#differences-between-two-sets-of-field-data&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
  A man with a watch knows what time it is. A man with two watches is never sure.
&lt;p&gt;&lt;cite&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Segal%27s_law&quot;&gt;Segal&#39;s Law&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Whenever you have two sources of data, it can often be confusing and frustrating as to why they differ. In much the same way as it’s important to understand the difference between &lt;a href=&quot;https://web.dev/lab-and-field-data-differences/&quot;&gt;lab and field metrics&lt;/a&gt;, there can also be differences between two sources of &lt;em&gt;field&lt;/em&gt; data. While the data would be the same in an ideal world, there are many reasons why they can differ.&lt;/p&gt;
&lt;h3 id=&quot;lab-data-versus-field-data&quot;&gt;Lab data versus field data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#lab-data-versus-field-data&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The first thing to check is whether you are looking at lab (synthetic) metrics or field (RUM) metrics. While it’s natural to assume RUM products only look at field data, many offer a lab component as well.&lt;/p&gt;
&lt;p&gt;Lab data is incredibly useful precisely because of the fixed conditions it measures under. It can be used to monitor unexpected changes or regressions in a production environment without the noise of a changing field population. However, lab data may not be representative of the real user experience, so field metrics can show quite different results.&lt;/p&gt;
&lt;h3 id=&quot;populations&quot;&gt;Populations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#populations&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The datasets used by CrUX and RUM solutions may be different due to differences in which page visits are being measured depending on which browsers, users, sites, and devices are being compared.&lt;/p&gt;
&lt;h4 id=&quot;included-browsers&quot;&gt;Included browsers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#included-browsers&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The Chrome User Experience Report, as its name suggests, is Chrome-only. While there are many Chromium-based browsers (Edge, Opera, and Brave to name a few) that also support the same metrics as Chrome given the shared core codebase, only Chrome users feed data into CrUX. This restriction also means Chrome users on iOS are not included, since it uses the underlying Webkit browser engine. Android WebViews also do not count as “Chrome”, so data from these users is not included—though &lt;a href=&quot;https://developer.chrome.com/docs/android/custom-tabs/&quot; rel=&quot;noopener&quot;&gt;Chrome Custom Tabs&lt;/a&gt; are included.&lt;/p&gt;
&lt;p&gt;While Chrome is one of the world’s most popular browsers—and therefore would likely give a broad representation of your site’s performance in most cases—measuring just that browser is by no means a measure of all your users. This may explain one main difference between RUM and CrUX. This is particularly true for performance techniques that rely on APIs or image formats only available in Chrome, for example.&lt;/p&gt;
&lt;p&gt;The lack of iOS data can also lead to bias. For example, &lt;a href=&quot;https://infrequently.org/2021/03/the-performance-inequality-gap/&quot; rel=&quot;noopener&quot;&gt;as iOS users are typically on more performant devices&lt;/a&gt; or visiting from more countries with better network infrastructure, including them can lead to high overall performance metrics. On the other hand, &lt;em&gt;excluding&lt;/em&gt; them—as CrUX does—can lead to data that is skewed to the lower end of site visitors (&lt;a href=&quot;https://www.smashingmagazine.com/2021/12/core-web-vitals-case-study-smashing-magazine/&quot; rel=&quot;noopener&quot;&gt;example case study&lt;/a&gt;). Android users typically cover a wider range of devices, device capabilities, and markets.&lt;/p&gt;
&lt;p&gt;RUM solutions can get data for non-Chrome browsers, and in particular from Chromium-based browsers that often have the same metrics (such as Core Web Vitals) built in. Non-Chromium-based browsers are also measured by RUM solutions, but may have a more limited set of metrics. For example, &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt; and &lt;a href=&quot;https://web.dev/cls/&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt; are currently only available in Chromium-based browsers and some other metrics can be measured quite differently (see later).&lt;/p&gt;
&lt;h4 id=&quot;opted-in-users&quot;&gt;Opted-in users &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#opted-in-users&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;As well as being limited to Chrome users, CrUX is further restricted by only measuring &lt;a href=&quot;https://developer.chrome.com/docs/crux/methodology/#user-eligibility&quot; rel=&quot;noopener&quot;&gt;a subset of Chrome users&lt;/a&gt; who have opted in to share CrUX data when the browser was installed.&lt;/p&gt;
&lt;p&gt;RUM providers also only look at a subset of users, usually due to cookie banner prompts—asking users to opt into RUM data collection—or tracking blockers. This can adversely affect some initial page loads if the confirmation is not given until the second or subsequent page, when some of the site assets have already been cached from previous pages. If this happens frequently, the metrics may appear more favorable in RUM than they actually are if slower initial page loads are excluded in a sufficient number of cases.&lt;/p&gt;
&lt;h4 id=&quot;included-sites&quot;&gt;Included sites &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#included-sites&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;CrUX is only intended to report on public websites, so there are other &lt;a href=&quot;https://developer.chrome.com/docs/crux/methodology/#eligibility&quot; rel=&quot;noopener&quot;&gt;eligibility criteria&lt;/a&gt; that may result in data not being logged in CrUX. Most notable of these criteria is that the website must be publicly discoverable and sufficiently popular to ensure a minimal sample size from which to draw meaningful conclusions. In most cases, this will result in no data being available in CrUX. This is less of a confusing difference compared to the data being available but different, but explains why that happens.&lt;/p&gt;
&lt;p&gt;However, if specific pages of a site are marked as indexable but others are not, then you may only see a subset of URLs in CrUX. If the origin is publicly discoverable, then all page views within that origin will be included in the origin-level data, but URL-level data may not be available.&lt;/p&gt;
&lt;h4 id=&quot;devices&quot;&gt;Devices &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#devices&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;CrUX segments data by mobile, desktop, and tablet - though many tools concentrate on the first two and may not expose tablet data, or may include it within mobile or desktop. The performance characteristics on mobile versus desktop can be quite different—both in terms of the content delivered, and in the capabilities of the devices viewing them.&lt;/p&gt;
&lt;p&gt;RUM data will allow segmenting traffic similarly, but often shows consolidated data by default. RUM may only allow segmenting by device type (for example, mobile) or browser (for example, Chrome) easily, but not both to only see mobile Chrome traffic. When comparing to CrUX data, ensure you are comparing like-for-like by filtering by device type &lt;em&gt;and&lt;/em&gt; the Chrome browser.&lt;/p&gt;
&lt;h4 id=&quot;sampling&quot;&gt;Sampling &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#sampling&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;RUM solutions typically allow adjusting the sampling rate of opted-in visitors where data is collected. This can be used to reduce the volume of data needed to be analyzed, and to reduce costs of commercial RUM services. If that sample size is too small and not representative of the wider population, then the resulting metrics will also be similarly skewed. Discuss with your RUM provider the appropriate sampling size for your site.&lt;/p&gt;
&lt;h3 id=&quot;aggregation-of-data&quot;&gt;Aggregation of data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#aggregation-of-data&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Field data by its very nature will include many, many data points of the same metrics compared to lab data, which will give a single value. If this data is aggregated differently for reporting, it can lead to another reason for differences between CrUX and RUM.&lt;/p&gt;
&lt;h4 id=&quot;timespan&quot;&gt;Timespan &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#timespan&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;CrUX data is based on a 28 day sliding window of traffic, and it is not possible to change this timeframe—though the BigQuery data is stored for each month, allowing you to see previous months.&lt;/p&gt;
&lt;p&gt;RUM data typically allows much greater granularity to allow seeing the impact of changes much sooner. When choosing smaller periods, though, RUM data can be unduly impacted by fluctuations in website traffic and visitors. When comparing RUM data to CrUX data, always ensure you are looking at performance over 28 days. Once you are satisfied the data is similar, you can look at other time frames to drill into the RUM data.&lt;/p&gt;
&lt;h4 id=&quot;aggregation-of-statistics&quot;&gt;Aggregation of statistics &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#aggregation-of-statistics&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;CrUX metrics are measured at the 75th percentile—that is, looking at the value that 75% of page views achieved. There will be extremes in field data and removing the worst 25% experiences, it is intended to give a value that the majority of visitors can reasonably be expected to achieve.&lt;/p&gt;
&lt;p&gt;RUM products often give a wider number of options of how to aggregate the metrics, including 75th percentile, median and other percentiles. If comparing RUM values to CrUX data, it is necessary to ensure you are looking at 75th percentile data to compare like-for-like.&lt;/p&gt;
&lt;p&gt;The histogram data in CrUX includes all available data—not just 75th percentile—and shows the number of page views in each rating, but the aggregate score will be based on 75th percentile. This CrUX data is surfaced in tools like &lt;a href=&quot;https://pagespeed.web.dev/&quot; rel=&quot;noopener&quot;&gt;PageSpeed Insights&lt;/a&gt;:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;PageSpeed Insights screenshot showing histograms of LCP rating page loads&quot; decoding=&quot;async&quot; height=&quot;428&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 632px) 632px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oRCstCFf6JyiVlNuphuX.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oRCstCFf6JyiVlNuphuX.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oRCstCFf6JyiVlNuphuX.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oRCstCFf6JyiVlNuphuX.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oRCstCFf6JyiVlNuphuX.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oRCstCFf6JyiVlNuphuX.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oRCstCFf6JyiVlNuphuX.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oRCstCFf6JyiVlNuphuX.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oRCstCFf6JyiVlNuphuX.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oRCstCFf6JyiVlNuphuX.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oRCstCFf6JyiVlNuphuX.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oRCstCFf6JyiVlNuphuX.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oRCstCFf6JyiVlNuphuX.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oRCstCFf6JyiVlNuphuX.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oRCstCFf6JyiVlNuphuX.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oRCstCFf6JyiVlNuphuX.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/oRCstCFf6JyiVlNuphuX.png?auto=format&amp;w=1264 1264w&quot; width=&quot;632&quot; /&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;differences-in-metrics&quot;&gt;Differences in metrics &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#differences-in-metrics&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are many metrics used to measure web performance, so when comparing two different sets of data, it’s important to understand what metrics are being measured, and how those metrics are being used.&lt;/p&gt;
&lt;h4 id=&quot;metrics-measured&quot;&gt;Metrics measured &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#metrics-measured&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;CrUX data is the official dataset of the &lt;a href=&quot;https://web.dev/vitals/&quot;&gt;Core Web Vitals&lt;/a&gt; initiative and primarily measures these three metrics (&lt;a href=&quot;https://web.dev/lcp/&quot;&gt;LCP&lt;/a&gt;, &lt;a href=&quot;https://web.dev/fid/&quot;&gt;FID&lt;/a&gt;, and &lt;a href=&quot;https://web.dev/cls/&quot;&gt;CLS&lt;/a&gt;), with &lt;a href=&quot;https://developer.chrome.com/docs/crux/methodology/#metrics&quot; rel=&quot;noopener&quot;&gt;a few additional metrics&lt;/a&gt; to complement these.&lt;/p&gt;
&lt;p&gt;RUM tools usually include these Core Web Vitals, but often include many other metrics too. Some RUM providers also measure the user experience using their own combination of all these metrics perhaps to give a happiness index or such. When comparing RUM data to CrUX, ensure you are comparing like-for-like.&lt;/p&gt;
&lt;p&gt;Tools that assess Core Web Vitals pass/fail status should consider a page passing if it meets the recommended targets at the 75th percentile for all Core Web Vitals. If FID is not present for pages with no interactions, then only LCP and CLS need to pass.&lt;/p&gt;
&lt;h4 id=&quot;metric-differences-across-browsers&quot;&gt;Metric differences across browsers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#metric-differences-across-browsers&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;CrUX only measures in Chrome browsers, and you can refer to the &lt;a href=&quot;https://chromium.googlesource.com/chromium/src/+/main/docs/speed/metrics_changelog/README.md&quot; rel=&quot;noopener&quot;&gt;Web Vitals Changelog&lt;/a&gt; to see how these change with each version of Chrome.&lt;/p&gt;
&lt;p&gt;RUM solutions, however, will measure from a wider variety of browsers. Chromium-based browsers (Edge, Opera, and so on) will likely be similar to Chrome, unless Chrome is implementing new changes as noted in the Changelog.&lt;/p&gt;
&lt;p&gt;For non-Chromium browsers, the differences can be more pronounced. &lt;a href=&quot;https://web.dev/fcp/&quot;&gt;First Contentful Paint (FCP)&lt;/a&gt;, for example, is available in Safari and Firefox, but &lt;a href=&quot;https://blog.webpagetest.org/posts/why-first-contentful-paint-doesnt-work-as-a-cross-browser-metric/&quot; rel=&quot;noopener&quot;&gt;is measured in a different way&lt;/a&gt;. This  can lead to significant variances in the times reported. As stated previously, if you are wanting to compare RUM to CrUX, it is best to filter on just Chrome users to allow for a like-for-like comparison.&lt;/p&gt;
&lt;h4 id=&quot;metrics-timing&quot;&gt;Metrics timing &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#metrics-timing&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Core Web Vitals metrics are provided by web browser APIs, but that does not mean there is not a potential for differences of values reported using them. Exactly &lt;em&gt;when&lt;/em&gt; the metric measurement is taken—at page load or throughout the full page life cycle—can lead to differences. RUM tools may not always measure metrics in the same way—even if using the same names—and the same browser APIs to get the data, which can be confusing.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt; is a page-load metric. A number of LCP elements can be reported by the Web API if larger elements are loaded later after the initial render. The final LCP element is when the page finishes loading or the user interacts with the page. Therefore differences can arise if the LCP element is reported earlier than those two events.&lt;/p&gt;
&lt;p&gt;Additionally, in field data, the LCP element can be different depending on how the page is loaded. For a default page load showing the top of the page content, the LCP element will depend primarily on the screen size. However, if the page is opened with an anchor link further down the document, or similarly opened with a deep-link into a Single Page App (SPA)—more on that later—then the LCP element can be different.&lt;/p&gt;
&lt;p&gt;Do not assume that the LCP timings provided in either CrUX nor RUM are based on the same element as lab tools. While CrUX will give you the overall LCP value per page or origin, RUM can segment this further to identify individual LCP problem sessions.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/cls/&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt; is measured &lt;a href=&quot;https://web.dev/cls/#what-is-cls&quot;&gt;throughout the life of the page&lt;/a&gt;, so the initial page-load CLS may not be representative of pages that cause greater shifts later after the page has loaded and the user has interacted with it. Taking the CLS value only after the page load—as many RUM products do—will therefore give a different result than taking the CLS value after the user finishes with the page.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/fid/&quot;&gt;First Input Delay (FID)&lt;/a&gt; requires an input to be measured, so it cannot be measured at page load. But—as its name implies—FID measures the &lt;em&gt;first input&lt;/em&gt; only. The newer &lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt; responsiveness metric measures all interactions throughout the life of the page, in a similar fashion to CLS, and so the reported value of INP may be very different if measured after the user has made a number of interactions on the page.&lt;/p&gt;
&lt;p&gt;CrUX will follow the &lt;a href=&quot;https://web.dev/vitals/&quot;&gt;Core Web Vitals documentation&lt;/a&gt; and measure these through the full lifetime of the page. Many RUM providers choose instead to measure these metrics either after page load, or at some other time (for example, when a key call-to-action is clicked) for various reasons.&lt;/p&gt;
&lt;p&gt;Getting an understanding from your RUM provider as to when Core Web Vitals are measured is important when seeing unexplained variances between the two data sources.&lt;/p&gt;
&lt;h4 id=&quot;single-page-applications&quot;&gt;Single-page applications &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#single-page-applications&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Single-page applications (SPA) work by updating the content on the current page, rather than performing traditional page navigation at the browser level. This means the browser does not see these as page navigations, despite users experiencing them as such. The &lt;a href=&quot;https://web.dev/vitals-spa-faq/&quot;&gt;Core Web Vitals APIs provided by the browser will not take these into consideration&lt;/a&gt;, and therefore CrUX does not currently support these page navigations. Work is currently underway to resolve this issue—see the &lt;a href=&quot;https://developer.chrome.com/blog/soft-navigations-experiment/&quot; rel=&quot;noopener&quot;&gt;Experimenting with measuring soft navigations&lt;/a&gt; post for more information.&lt;/p&gt;
&lt;p&gt;Some RUM providers do attempt to detect &amp;quot;soft navigations&amp;quot; in SPAs, but if they&#39;re also attributing Core Web Vitals metrics to those &amp;quot;soft navigations&amp;quot; it will lead to differences with CrUX since the underlying APIs do not support this.&lt;/p&gt;
&lt;h3 id=&quot;crux-and-web-api-differences&quot;&gt;CrUX and Web API differences &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#crux-and-web-api-differences&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As well as the differences in &lt;em&gt;which&lt;/em&gt; page views are measured, and &lt;em&gt;what&lt;/em&gt; is measured, there are a few other, more complicated, scenarios to be aware of that can lead to differences in CrUX and RUM data. Some of these are due to limitations of the Web APIs used to measure the metrics, and some are where the results returned by the API need to be treated differently for certain scenarios. The Core Web Vitals documentation lists these differences for &lt;a href=&quot;https://web.dev/lcp/#differences-between-the-metric-and-the-api&quot;&gt;LCP&lt;/a&gt;, &lt;a href=&quot;https://web.dev/cls/#differences-between-the-metric-and-the-api&quot;&gt;CLS&lt;/a&gt;, and &lt;a href=&quot;https://web.dev/fid/#differences-between-the-metric-and-the-api&quot;&gt;FID&lt;/a&gt; but the main differences are noted below.&lt;/p&gt;
&lt;h4 id=&quot;backforward-cache&quot;&gt;Back/forward cache &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#backforward-cache&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;CrUX considers &lt;a href=&quot;https://web.dev/bfcache/&quot;&gt;Back/forward cache&lt;/a&gt; (or bfcache) restores as page navigations even though they do not result in a traditional page load. As the Web APIs do not treat these as a page load, RUM solutions need to &lt;a href=&quot;https://web.dev/bfcache/#how-bfcache-affects-analytics-and-performance-measurement&quot;&gt;take extra steps&lt;/a&gt; for these pages to be counted if they wish to match CrUX. These are considerably faster page loads that can result in overall better performance being reported for a site, so not including them can result in worse overall page performance metrics. Refer to your RUM solution to understand if they handle bfcache restored pages.&lt;/p&gt;
&lt;h4 id=&quot;iframes&quot;&gt;Iframes &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#iframes&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;For security and privacy reasons, top-level pages do not have access to content within iframes (not even same-origin iframes). This means performance metrics for content in those can only be measured by the iframe itself, and not through the Web APIs on the framing page. If the iframe content includes the LCP element, or content that impacts the CLS, FID or INP experienced by the user, this will not be available to RUM solutions (&lt;a href=&quot;https://github.com/GoogleChrome/web-vitals#limitations&quot; rel=&quot;noopener&quot;&gt;including the Google web-vitals JavaScript library&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;CrUX however, being measured by the Chrome browser itself rather than the page, does not have these limitations and so does measure metrics within iframes when reporting Core Web Vitals. This more accurately reflects what the user experiences, but can be another reason for differences for sites making use of iframes.&lt;/p&gt;
&lt;p&gt;One concrete example of how this can lead to differences between LCP data in CrUX and RUM is embedded &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt;. An autoplaying &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element&#39;s first painted frame can count as an LCP candidate, but embeds for popular video streaming services—such as YouTube, for example—place these elements in an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;. As of &lt;a href=&quot;https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/speed/metrics_changelog/lcp.md&quot; rel=&quot;noopener&quot;&gt;August 2023&lt;/a&gt;, CrUX can account for this, as it can access &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; contents, but RUM solutions cannot.&lt;/p&gt;
&lt;h4 id=&quot;cross-origin-resources&quot;&gt;Cross-origin resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#cross-origin-resources&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;LCP media served from other domains do not give render time in the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/PerformanceObserver&quot; rel=&quot;noopener&quot;&gt;PerformanceObserver API&lt;/a&gt;—unless the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Timing-Allow-Origin&quot; rel=&quot;noopener&quot;&gt;Timing-Allow-Origin header&lt;/a&gt; (TAO) is provided—due to browser security restrictions to reduce timing attacks. This &lt;a href=&quot;https://web.dev/lcp/#load-time-vs-render-time&quot;&gt;falls back to the load time of the resource&lt;/a&gt; but this may be quite different from when the content was actually painted.&lt;/p&gt;
&lt;p&gt;Again, CrUX does report the render time data for Core Web Vitals. Sites are advised to limit cross-origin content that impacts the Core Web Vitals metrics and to enable TAO where possible if they wish to measure this more accurately. Other cross-origin resources may be subject to similar restrictions.&lt;/p&gt;
&lt;h4 id=&quot;background-tabs-and-prerender&quot;&gt;Background tabs and prerender &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#background-tabs-and-prerender&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When a page is not opened in the foreground—such as opening it in the background or if it uses prerender options (currently under development for Chrome)—they will still emit metrics via Web APIs. However, these are not reported by CrUX since they give timings that are inconsistent with the user experience. RUM solutions should also consider ignoring these, or at least explaining how these page views are treated.&lt;/p&gt;
&lt;h2 id=&quot;so-what-can-we-do-about-it&quot;&gt;So what can we do about it? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#so-what-can-we-do-about-it&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We have shown why there may be differences between CrUX and RUM data, either due to differences in the methodology each uses or due to which users and page views are included or excluded. Ideally, both sets of data will still be representative of your site performance to be useful, but this article should outline why it is very unlikely to get the exact same numbers in each.&lt;/p&gt;
&lt;p&gt;Where differences are slight (for example reporting a LCP of 2.0 seconds versus 2.2 seconds) both datasets will be useful and can usually be considered to be roughly in sync.&lt;/p&gt;
&lt;p&gt;When pronounced differences make you question the accuracy of the data, you should try to understand those differences. Can the RUM data be filtered to be more closely aligned with CrUX (looking only at Chrome users, for desktop or mobile, with 75th percentile values over 28 days) to reduce these differences?&lt;/p&gt;
&lt;p&gt;If so—and you can get the data to match more closely—then you should still ask why you are seeing these differences in the overall data and what this means. Are non-Chrome users skewing your metrics in a positive or negative way? Does this give you more insights into where you have performance issues that you can prioritize?&lt;/p&gt;
&lt;p&gt;If your non-Chrome users are getting different results then you can use this valuable insight that RUM has given you to optimize differently. For example, certain APIs are not available on certain browsers, but you can consider alternatives for unsupported browsers to also improve their experiences. Or you can give &lt;a href=&quot;https://www.smashingmagazine.com/2022/03/signals-customizing-website-user-experience/&quot; rel=&quot;noopener&quot;&gt;a different, but more performant, experience&lt;/a&gt; to users on constrained devices or networks. CrUX is limited to Chrome data, but you should consider all your site visitors&#39; experiences to help prioritize improvements. RUM data can fill that gap.&lt;/p&gt;
&lt;p&gt;Once you understand the reasons for any differences, both tools can be incredibly useful to understand the user experiences of your website, and to help improve this even if the numbers are not identical. Use your RUM data to complement CrUX data and allow you to dig into what CrUX is telling you at a high level by segmenting your traffic to help you identify if it’s particular areas of your site or user base that need attention.&lt;/p&gt;
&lt;p&gt;Looking at the trends to see your improvements are having the expected positive impacts is often more important than having each number match exactly between the two data sources. As mentioned above, RUM allows you to look at different time frames to get an advance look at what your 28 day CrUX scores will be—though looking at too short time frames can lead to noisy data, hence why CrUX uses 28 days.&lt;/p&gt;
&lt;p&gt;Often there is no “right” or “wrong” answer in these different metrics - they are simply having a different lens on your users and how they are experiencing your site. As long as you understand why these differences happen, and what that can do to drive your decision making, that is what is more important to better serve your site visitors.&lt;/p&gt;
&lt;h2 id=&quot;acknowledgements&quot;&gt;Acknowledgements &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/crux-and-rum-differences/#acknowledgements&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Hero image by &lt;a href=&quot;https://unsplash.com/@slelham&quot; rel=&quot;noopener&quot;&gt;Steven Lelham&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/atSaEOeE8Nk&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>Barry Pollard</name>
    </author>
  </entry>
  
  <entry>
    <title>Time to First Byte (TTFB)</title>
    <link href="https://web.dev/ttfb/"/>
    <updated>2021-10-26T00:00:00Z</updated>
    <id>https://web.dev/ttfb/</id>
    <content type="html" mode="escaped">&lt;div class=&quot;wdi-browser-compat&quot;&gt;
  &lt;span class=&quot;wdi-browser-compat__label&quot;&gt;Browser support&lt;/span&gt;
  &lt;ul class=&quot;wdi-browser-compat__items&quot;&gt;
    &lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;chrome&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Chrome 43, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      43
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox 31, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      31
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Edge 12, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      12
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Safari 11, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      11
    &lt;/span&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/PerformanceResourceTiming/responseStart#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Time to First Byte (TTFB) is a foundational metric for measuring connection setup time and web server responsiveness in both the lab and the field. It helps identify when a web server is too slow to respond to requests. In the case of navigation requests—that is, requests for an HTML document—it precedes every other meaningful loading performance metric. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;what-is-ttfb&quot;&gt;What is TTFB? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/ttfb/#what-is-ttfb&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;TTFB is a metric that measures the time between the request for a resource and when the first byte of a response begins to arrive.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A diagram of network request timings. The phases from left to right are Redirect (which overlaps with Prompt for Unload), Cache, DNS, TCP, Request, Response, Processing, and Load. The associated timings are redirectStart and redirectEnd (which overlap with the Prompt for Unload&amp;#x27;s unloadEventStart and unloadEventEnd), fetchStart, domainLookupStart, domainLookupEnd, connectStart, secureConnectionStart, connectEnd, requestStart, responseStart, responseEnd, domInteractive, domContentLoadedEventStart, domContentLoadedEventEnd, domComplete, loadEventStart, and loadEventEnd.&quot; decoding=&quot;async&quot; height=&quot;337&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ccT8ltSPrTri3tz7AA3h.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ccT8ltSPrTri3tz7AA3h.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ccT8ltSPrTri3tz7AA3h.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ccT8ltSPrTri3tz7AA3h.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ccT8ltSPrTri3tz7AA3h.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ccT8ltSPrTri3tz7AA3h.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ccT8ltSPrTri3tz7AA3h.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ccT8ltSPrTri3tz7AA3h.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ccT8ltSPrTri3tz7AA3h.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ccT8ltSPrTri3tz7AA3h.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ccT8ltSPrTri3tz7AA3h.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ccT8ltSPrTri3tz7AA3h.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ccT8ltSPrTri3tz7AA3h.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ccT8ltSPrTri3tz7AA3h.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ccT8ltSPrTri3tz7AA3h.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ccT8ltSPrTri3tz7AA3h.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ccT8ltSPrTri3tz7AA3h.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ccT8ltSPrTri3tz7AA3h.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    A diagram of network request phases and their associated timings. TTFB measures the elapsed time between &lt;code&gt;startTime&lt;/code&gt; and &lt;code&gt;responseStart&lt;/code&gt;.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;TTFB is the sum of the following request phases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Redirect time&lt;/li&gt;
&lt;li&gt;Service worker startup time (if applicable)&lt;/li&gt;
&lt;li&gt;DNS lookup&lt;/li&gt;
&lt;li&gt;Connection and TLS negotiation&lt;/li&gt;
&lt;li&gt;Request, up until the point at which the first byte of the response has arrived&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Reducing latency in connection setup time and on the backend will contribute to a lower TTFB.&lt;/p&gt;
&lt;h3 id=&quot;what-is-a-good-ttfb-score&quot;&gt;What is a good TTFB score? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/ttfb/#what-is-a-good-ttfb-score&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Because TTFB precedes &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/&quot;&gt;user-centric metrics&lt;/a&gt; such as &lt;a href=&quot;https://web.dev/fcp/&quot;&gt;First Contentful Paint (FCP)&lt;/a&gt; and &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt;, it&#39;s recommended that your server responds to navigation requests quickly enough so that the &lt;strong&gt;75th percentile&lt;/strong&gt; of users experience an &lt;a href=&quot;https://web.dev/fcp/#what-is-a-good-fcp-score&quot;&gt;FCP within the &amp;quot;good&amp;quot; threshold&lt;/a&gt;. As a rough guide, most sites should strive to have Time To First Byte of &lt;strong&gt;0.8 seconds&lt;/strong&gt; or less.&lt;/p&gt;
&lt;figure&gt;
  &lt;picture&gt;
    &lt;source srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/ILJ1xKjzVisqOPPyHYVA.svg&quot; media=&quot;(min-width: 640px)&quot; width=&quot;800&quot; height=&quot;200&quot; /&gt;
    &lt;img alt=&quot;Good TTFB values are 0.8 seconds or less, poor values are greater than 1.8 seconds, and anything in between needs improvement&quot; decoding=&quot;async&quot; height=&quot;480&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/EcKicxW5ErYYhf8RvpeO.svg&quot; width=&quot;640&quot; /&gt;
  &lt;/picture&gt;
&lt;/figure&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Important&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;p&gt;TTFB is not a &lt;a href=&quot;https://web.dev/vitals/&quot;&gt;Core Web Vitals&lt;/a&gt; metric, so it&#39;s not absolutely necessary that sites meet the &amp;quot;good&amp;quot; TTFB threshold, provided that it doesn&#39;t impede their ability to score well on the metrics that matter.&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt;Websites vary in how they deliver content. A low TTFB is crucial for getting markup out to the client as soon as possible. However, if a website delivers the initial markup quickly, but that markup then requires JavaScript to populate it with meaningful content—as is the the case with Single Page Applications (SPAs)—then achieving the lowest possible TTFB is especially important so that the client-rendering of markup can occur sooner.&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt;Conversely, a server-rendered site that does not require as much client-side work could have a higher TTFB, but better FCP and LCP values than an entirely client-rendered experience. This is why the TTFB thresholds are a “rough guide”, and will need to be weighed against how your site delivers its core content.&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;how-to-measure-ttfb&quot;&gt;How to measure TTFB &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/ttfb/#how-to-measure-ttfb&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;TTFB can be measured in &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/#in-the-lab&quot;&gt;the lab&lt;/a&gt; or in &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/#in-the-field&quot;&gt;the field&lt;/a&gt; in the following ways.&lt;/p&gt;
&lt;h3 id=&quot;field-tools&quot;&gt;Field tools &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/ttfb/#field-tools&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/crux/&quot; rel=&quot;noopener&quot;&gt;Chrome User Experience Report&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/GoogleChrome/web-vitals&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;web-vitals&lt;/code&gt; JavaScript library&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;lab-tools&quot;&gt;Lab tools &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/ttfb/#lab-tools&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;In the &lt;a href=&quot;https://developer.chrome.com/docs/devtools/network/&quot; rel=&quot;noopener&quot;&gt;network panel&lt;/a&gt; of Chrome&#39;s DevTools&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.webpagetest.org/&quot; rel=&quot;noopener&quot;&gt;WebPageTest&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;measure-ttfb-in-javascript&quot;&gt;Measure TTFB in JavaScript &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/ttfb/#measure-ttfb-in-javascript&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You can measure the TTFB of &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Request/mode&quot; rel=&quot;noopener&quot;&gt;navigation requests&lt;/a&gt; in the browser with the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Navigation_timing_API&quot; rel=&quot;noopener&quot;&gt;Navigation Timing API&lt;/a&gt;. The following example shows how to create a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/PerformanceObserver&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;PerformanceObserver&lt;/code&gt;&lt;/a&gt; that listens for a &lt;code&gt;navigation&lt;/code&gt; entry and logs it to the console:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PerformanceObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entryList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;pageNav&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entryList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEntriesByType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;navigation&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;TTFB: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;pageNav&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;responseStart&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;navigation&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;buffered&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Not all browsers support &lt;code&gt;PerformanceObserver&lt;/code&gt; or its &lt;code&gt;buffered&lt;/code&gt; flag. To get the largest possible browser support, consider adopting the &lt;code&gt;web-vitals&lt;/code&gt; package, which is discussed below. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;web-vitals&lt;/code&gt; JavaScript library&lt;/a&gt; can also measure TTFB in the browser with less complexity:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;onTTFB&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;web-vitals&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Measure and log TTFB as soon as it&#39;s available.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;onTTFB&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;measuring-resource-requests&quot;&gt;Measuring resource requests &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/ttfb/#measuring-resource-requests&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;TTFB applies to &lt;em&gt;all&lt;/em&gt; requests, not just navigation requests. In particular, resources hosted on cross-origin servers can introduce latency due to the need to set up connections to those servers. To measure TTFB for resources in the field, use the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Resource_Timing_API/Using_the_Resource_Timing_API&quot; rel=&quot;noopener&quot;&gt;Resource Timing API&lt;/a&gt; within a &lt;code&gt;PerformanceObserver&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PerformanceObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entryList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; entries &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entryList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEntries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; entry &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; entries&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Some resources may have a responseStart value of 0, due&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// to the resource being cached, or a cross-origin resource&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// being served without a Timing-Allow-Origin header set.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;responseStart &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;TTFB: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;responseStart&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;resource&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;buffered&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The above code snippet is similar to the one used to measure the TTFB for a navigation request, except instead of querying for &lt;code&gt;&#39;navigation&#39;&lt;/code&gt; entries, you query for &lt;code&gt;&#39;resource&#39;&lt;/code&gt; entries instead. It also accounts for the fact that some resources loaded from the primary origin may return a value of &lt;code&gt;0&lt;/code&gt;, since the connection is already open, or a resource is instantaneously retrieved from a cache.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Gotchas&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; TTFB for cross-origin requests will not be measurable in the field if cross-origin servers fail to set a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Timing-Allow-Origin&quot;&gt;&lt;code&gt;Timing-Allow-Origin&lt;/code&gt; header&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;how-to-improve-ttfb&quot;&gt;How to improve TTFB &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/ttfb/#how-to-improve-ttfb&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An in-depth guide on &lt;a href=&quot;https://web.dev/optimize-ttfb/&quot;&gt;optimizing TTFB&lt;/a&gt; has been published to give you more guidance on improving your website&#39;s TTFB.&lt;/p&gt;
</content>
    <author>
      <name>Jeremy Wagner</name>
    </author><author>
      <name>Barry Pollard</name>
    </author>
  </entry>
  
  <entry>
    <title>Optimizing resource loading with the Fetch Priority API</title>
    <link href="https://web.dev/fetch-priority/"/>
    <updated>2021-10-20T00:00:00Z</updated>
    <id>https://web.dev/fetch-priority/</id>
    <content type="html" mode="escaped">&lt;div class=&quot;wdi-browser-compat&quot;&gt;
  &lt;span class=&quot;wdi-browser-compat__label&quot;&gt;Browser support&lt;/span&gt;
  &lt;ul class=&quot;wdi-browser-compat__items&quot;&gt;
    &lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;chrome&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Chrome 102, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      102
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox, Not supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Edge 102, Supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
102
&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Safari preview, Preview&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;preview&quot; title=&quot;Preview&quot; aria-label=&quot;Preview&quot;&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;p&gt;&lt;/p&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/HTMLImageElement/fetchPriority#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; This feature was originally called Priority Hints but was renamed to Fetch Priority after standardization. See &lt;a href=&quot;https://web.dev/fetch-priority/#history&quot;&gt;History&lt;/a&gt; below for more details. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;When a browser parses a web page and begins to discover and download resources such as images, scripts, or CSS, it assigns them a fetch &lt;code&gt;priority&lt;/code&gt; in an attempt to download resources in an optimal order. These priorities can depend on the kind of resource and where it is in the document. For example, in-viewport images may have a &lt;code&gt;High&lt;/code&gt; priority while the priority for early loaded, render-blocking CSS via &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt;s in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; could be &lt;code&gt;Very High&lt;/code&gt;. Browsers are pretty good at assigning priorities that work well but may not be optimal in all cases.&lt;/p&gt;
&lt;p&gt;In this article, we&#39;ll discuss the Fetch Priority API and the &lt;code&gt;fetchpriority&lt;/code&gt; HTML attribute, which allow you to hint at the relative priority of a resource (&lt;code&gt;high&lt;/code&gt; or &lt;code&gt;low&lt;/code&gt;). Fetch Priority can help optimize the Core Web Vitals.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A filmstrip view comparing two tests of the Google Flights homepage. At bottom, Fetch Priority are used to boost the priority of the hero image, resulting in a 0.7 second decrease in LCP.&quot; decoding=&quot;async&quot; height=&quot;400&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UQ60oFwWrVCPbFYx3pJY.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UQ60oFwWrVCPbFYx3pJY.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UQ60oFwWrVCPbFYx3pJY.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UQ60oFwWrVCPbFYx3pJY.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UQ60oFwWrVCPbFYx3pJY.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UQ60oFwWrVCPbFYx3pJY.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UQ60oFwWrVCPbFYx3pJY.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UQ60oFwWrVCPbFYx3pJY.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UQ60oFwWrVCPbFYx3pJY.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UQ60oFwWrVCPbFYx3pJY.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UQ60oFwWrVCPbFYx3pJY.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UQ60oFwWrVCPbFYx3pJY.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UQ60oFwWrVCPbFYx3pJY.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UQ60oFwWrVCPbFYx3pJY.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UQ60oFwWrVCPbFYx3pJY.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UQ60oFwWrVCPbFYx3pJY.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UQ60oFwWrVCPbFYx3pJY.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UQ60oFwWrVCPbFYx3pJY.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Fetch Priority improving Largest Contentful Paint from 2.6&amp;nbsp;s to 1.9&amp;nbsp;s in a test of Google Flights&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/fetch-priority/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;A few key areas where Fetch Priority can help:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Boost the priority of the LCP image by specifying &lt;code&gt;fetchpriority=&amp;quot;high&amp;quot;&lt;/code&gt; on the image element, causing LCP to happen sooner.&lt;/li&gt;
&lt;li&gt;Increase the priority of &lt;code&gt;async&lt;/code&gt; scripts using better semantics than the current hack that is commonly used (inserting a &lt;code&gt;&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&lt;/code&gt; for the &lt;code&gt;async&lt;/code&gt; script).&lt;/li&gt;
&lt;li&gt;Decrease the priority of late-body scripts to allow for better sequencing with images.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Historically, developers have had some, but limited, influence over resource priority using &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/uses-rel-preload/&quot; rel=&quot;noopener&quot;&gt;preload&lt;/a&gt; and &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/uses-rel-preconnect/&quot; rel=&quot;noopener&quot;&gt;preconnect&lt;/a&gt;. Fetch Priority complements these &lt;a href=&quot;https://www.w3.org/TR/resource-hints/&quot; rel=&quot;noopener&quot;&gt;Resource Hints&lt;/a&gt;, but it&#39;s essential to understand where they all fit in. Preload lets you tell the browser about critical resources you want to load early before they are discovered naturally. This is especially useful for resources that are not easily discovered, such as fonts included in stylesheets, background images, or resources loaded from a script. Preconnect helps warm up connections to cross-origin servers and can help improve metrics like &lt;a href=&quot;https://web.dev/ttfb/&quot;&gt;Time-to-first-byte&lt;/a&gt; and is useful when you know an origin but not necessarily the exact URL of a resource that will be needed.&lt;/p&gt;
&lt;p&gt;Fetch Priority is a markup-based signal (available through the &lt;code&gt;fetchpriority&lt;/code&gt; attribute) that developers can use to indicate the relative priority of a particular resource. You can also use these hints via JavaScript and the &lt;a href=&quot;https://web.dev/introduction-to-fetch/&quot;&gt;Fetch API&lt;/a&gt; with the &lt;code&gt;priority&lt;/code&gt; property to influence the priority of resource fetches made for data. Fetch Priority can also complement preload. Take a Largest Contentful Paint image, which, when preloaded, will still get a low priority. If it is pushed back by other early low-priority resources, using Fetch Priority can help how soon the image gets loaded.&lt;/p&gt;
&lt;p&gt;Fetch Priority is &lt;a href=&quot;https://www.chromestatus.com/feature/5273474901737472&quot; rel=&quot;noopener&quot;&gt;available&lt;/a&gt; in Chrome 101 or later.&lt;/p&gt;
&lt;h2 id=&quot;resource-priority&quot;&gt;Resource priority &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/fetch-priority/#resource-priority&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The resource download sequence depends on the browser&#39;s assigned priority for every resource on the page. Different factors can affect priority computation logic. For example,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CSS, fonts, scripts, images, and third-party resources are assigned different priorities.&lt;/li&gt;
&lt;li&gt;The location or order in which you reference resources in the document also affects the priority of resources.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/uses-rel-preload/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;preload&lt;/code&gt;&lt;/a&gt; resource hint helps the browser to discover a resource faster and thus load it before the document loads it and affects priority.&lt;/li&gt;
&lt;li&gt;Priority computation changes for &lt;a href=&quot;https://web.dev/efficiently-load-third-party-javascript/#use-async-or-defer&quot;&gt;&lt;code&gt;async&lt;/code&gt; or &lt;code&gt;defer&lt;/code&gt;&lt;/a&gt; scripts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following table considers such factors to show how most resources are currently prioritized and sequenced in Chrome.&lt;/p&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;&amp;nbsp;&lt;/th&gt;
        &lt;th&gt;Layout-blocking&lt;/th&gt;
        &lt;th&gt;Load in layout-blocking phase&lt;/th&gt;
        &lt;th colspan=&quot;3&quot;&gt;Load one-at-a-time in layout-blocking phase&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;strong&gt;Blink&lt;br /&gt;Priority&lt;/strong&gt;&lt;/td&gt;
        &lt;td&gt;&lt;strong&gt;VeryHigh&lt;/strong&gt;&lt;/td&gt;
        &lt;td&gt;&lt;strong&gt;High&lt;/strong&gt;&lt;/td&gt;
        &lt;td&gt;&lt;strong&gt;Medium&lt;/strong&gt;&lt;/td&gt;
        &lt;td&gt;&lt;strong&gt;Low&lt;/strong&gt;&lt;/td&gt;
        &lt;td&gt;&lt;strong&gt;VeryLow&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;strong&gt;DevTools&lt;br /&gt;Priority&lt;/strong&gt;&lt;/td&gt;
        &lt;td&gt;&lt;strong&gt;Highest&lt;/strong&gt;&lt;/td&gt;
        &lt;td&gt;&lt;strong&gt;High&lt;/strong&gt;&lt;/td&gt;
        &lt;td&gt;&lt;strong&gt;Medium&lt;/strong&gt;&lt;/td&gt;
        &lt;td&gt;&lt;strong&gt;Low&lt;/strong&gt;&lt;/td&gt;
        &lt;td&gt;&lt;strong&gt;Lowest&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;Main resource&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;CSS*** (early**)&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;CSS*** (late**)&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;CSS (mismatch)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;Script (early** or not from preload scanner)&lt;/td&gt;
        &lt;td&gt;Script (**late)&lt;/td&gt;
        &lt;td&gt;Script (async)&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;Font&lt;/td&gt;
        &lt;td&gt;Font (preload)&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;Import&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;Image (in viewport)&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;Image&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;Media&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;SVG document&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;Prefetch&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;Preload*&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;XSL&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;XHR (sync)&lt;/td&gt;
        &lt;td&gt;XHR/fetch* (async)&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; * Preload using &lt;code&gt;&amp;quot;as&amp;quot;&lt;/code&gt; or fetch using &lt;code&gt;&amp;quot;type&amp;quot;&lt;/code&gt; use the priority of the type they are requesting (e.g., preload &lt;code&gt;as=&amp;quot;stylesheet&amp;quot;&lt;/code&gt; will use Highest priority). With no &lt;code&gt;&amp;quot;as&amp;quot;&lt;/code&gt;, they will behave like an XHR.&lt;br /&gt; ** &amp;quot;Early&amp;quot; is defined as being requested before any non-preloaded images have been requested (&amp;quot;late&amp;quot; is after).&lt;br /&gt; *** CSS where the media type doesn&#39;t match is not fetched by the preload scanner and is only processed when the main parser reaches it, which usually means it will be fetched very late and with a &amp;quot;late&amp;quot; priority. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The browser downloads resources with the same computed priority in the order they are discovered. You can check the priority assigned to different resources when loading a page under the Chrome Dev Tools &lt;strong&gt;Network&lt;/strong&gt; tab. (Ensure that you include the priority column by right-clicking on the table headings).&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of assets listed in the network tab of Chrome&amp;#x27;s DevTools. The columns read, from left to right: name, status, type, initiator, size, time, and priority.&quot; decoding=&quot;async&quot; height=&quot;263&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Lwm9jwJF5sQ3gQ7PHSs1.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Lwm9jwJF5sQ3gQ7PHSs1.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Lwm9jwJF5sQ3gQ7PHSs1.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Lwm9jwJF5sQ3gQ7PHSs1.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Lwm9jwJF5sQ3gQ7PHSs1.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Lwm9jwJF5sQ3gQ7PHSs1.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Lwm9jwJF5sQ3gQ7PHSs1.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Lwm9jwJF5sQ3gQ7PHSs1.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Lwm9jwJF5sQ3gQ7PHSs1.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Lwm9jwJF5sQ3gQ7PHSs1.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Lwm9jwJF5sQ3gQ7PHSs1.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Lwm9jwJF5sQ3gQ7PHSs1.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Lwm9jwJF5sQ3gQ7PHSs1.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Lwm9jwJF5sQ3gQ7PHSs1.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Lwm9jwJF5sQ3gQ7PHSs1.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Lwm9jwJF5sQ3gQ7PHSs1.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Lwm9jwJF5sQ3gQ7PHSs1.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Lwm9jwJF5sQ3gQ7PHSs1.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Priority for resource &lt;code&gt;type = &amp;quot;font&amp;quot;&lt;/code&gt; on BBC news detail page&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of assets listed in the network tab of Chrome&amp;#x27;s DevTools. The columns read, from left to right: name, status, type, initiator, size, time, and priority.&quot; decoding=&quot;async&quot; height=&quot;175&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/K5U3maOt2TFBbtScW7TY.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/K5U3maOt2TFBbtScW7TY.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/K5U3maOt2TFBbtScW7TY.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/K5U3maOt2TFBbtScW7TY.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/K5U3maOt2TFBbtScW7TY.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/K5U3maOt2TFBbtScW7TY.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/K5U3maOt2TFBbtScW7TY.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/K5U3maOt2TFBbtScW7TY.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/K5U3maOt2TFBbtScW7TY.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/K5U3maOt2TFBbtScW7TY.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/K5U3maOt2TFBbtScW7TY.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/K5U3maOt2TFBbtScW7TY.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/K5U3maOt2TFBbtScW7TY.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/K5U3maOt2TFBbtScW7TY.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/K5U3maOt2TFBbtScW7TY.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/K5U3maOt2TFBbtScW7TY.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/K5U3maOt2TFBbtScW7TY.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/K5U3maOt2TFBbtScW7TY.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Priority for resource type = &amp;quot;script&amp;quot; on BBC news detail page&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;when-would-you-need-fetch-priority&quot;&gt;When would you need Fetch Priority? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/fetch-priority/#when-would-you-need-fetch-priority&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Knowledge of the browser&#39;s prioritization logic provides you with a few existing knobs to tweak the order of downloads. You can&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Place resource tags such as &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; depending on the order you want to download them. Resources with the same priority are generally loaded in the order they are discovered.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/preload-critical-assets/&quot;&gt;Use the &lt;code&gt;preload&lt;/code&gt; resource hint&lt;/a&gt; to download necessary resources earlier, particularly for resources that are not easily discovered early by the browser.&lt;/li&gt;
&lt;li&gt;Use &lt;a href=&quot;https://web.dev/efficiently-load-third-party-javascript/#use-async-or-defer&quot;&gt;&lt;code&gt;async&lt;/code&gt; or &lt;code&gt;defer&lt;/code&gt;&lt;/a&gt; to download scripts without blocking other resources.&lt;/li&gt;
&lt;li&gt;Lazy-load below-the-fold content so that the browser can use the available bandwidth for more critical above-the-fold resources.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These techniques help to control the browser&#39;s priority computation, thereby improving performance and &lt;a href=&quot;https://web.dev/vitals/&quot;&gt;Core Web Vitals&lt;/a&gt;. For example, when a critical background image is preloaded, it can be discovered much earlier, improving the Largest Contentful Paint (&lt;a href=&quot;https://web.dev/lcp/&quot;&gt;LCP&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Sometimes these handles may not be enough to prioritize resources optimally for your application. Here are some of the scenarios where Fetch Priority could be helpful:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You have several above-the-fold images, but all of them need not have the same priority. For example, in an image carousel, only the first visible image needs a higher priority compared to the others.&lt;/li&gt;
&lt;li&gt;Hero images inside the viewport start at a &amp;quot;Low&amp;quot; priority. After the layout is complete, Chrome discovers they are in the viewport and boosts their priority (unfortunately, dev tools only shows the final priority—WebPageTest will show both). This usually adds a significant delay to loading the image. Providing the Fetch Priority in markup lets the image start at a &amp;quot;High&amp;quot; priority and start loading much earlier.&lt;br /&gt;&lt;br /&gt;Note that preload is still required for the early discovery of LCP images included as CSS backgrounds and can be combined with Fetch Priority by including the &lt;code&gt;fetchpriority=&#39;high&#39;&lt;/code&gt; on the preload, otherwise it will still start with the default &amp;quot;Low&amp;quot; priority for images.&lt;/li&gt;
&lt;li&gt;Declaring scripts as &lt;code&gt;async&lt;/code&gt; or &lt;code&gt;defer&lt;/code&gt; tells the browser to load them asynchronously. However, as seen in the figure above, these scripts are also assigned a &amp;quot;low&amp;quot; priority. You may want to bump up their priority while ensuring asynchronous download, particularly for any scripts that are critical for the user experience.&lt;/li&gt;
&lt;li&gt;You may use the JavaScript &lt;a href=&quot;https://web.dev/introduction-to-fetch/&quot;&gt;&lt;code&gt;fetch()&lt;/code&gt;&lt;/a&gt; API to fetch resources or data asynchronously. Fetch is assigned a &amp;quot;High&amp;quot; priority by the browser. There may be situations where you do not want all your fetches to be executed with &amp;quot;High&amp;quot; priority and prefer using different Fetch Priority instead. This can be helpful when running background API calls and mixing them with API calls that are responding to user input, like with autocomplete. The background API calls can be tagged as &amp;quot;Low&amp;quot; priority and the interactive API calls marked as &amp;quot;High&amp;quot; priority.&lt;/li&gt;
&lt;li&gt;The browser assigns CSS and fonts a &amp;quot;High&amp;quot; priority, but all such resources may not be equally important or required for LCP. You can use Fetch Priority to lower the priority of some of these resources.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;the-fetchpriority-attribute&quot;&gt;The &lt;code&gt;fetchpriority&lt;/code&gt; attribute &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/fetch-priority/#the-fetchpriority-attribute&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can provide a Fetch Priority using the &lt;code&gt;fetchpriority&lt;/code&gt; HTML attribute. You can use the attribute with &lt;code&gt;link&lt;/code&gt;, &lt;code&gt;img&lt;/code&gt;, and &lt;code&gt;script&lt;/code&gt; tags. The attribute allows you to specify the priority for resource types such as CSS, fonts, scripts, and images when downloaded using the supported tags.
The &lt;code&gt;fetchpriority&lt;/code&gt; attribute accepts one of three values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;high&lt;/code&gt;: You consider the resource a high priority and want the browser to prioritize it as long as the browser&#39;s heuristics don&#39;t prevent that from happening.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;low&lt;/code&gt;: You consider the resource a low priority and want the browser to deprioritize it if it&#39;s heuristics permit.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;auto&lt;/code&gt;: This is the default value where you don&#39;t have a preference and let the browser decide the appropriate priority.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here are a few examples of using the &lt;code&gt;fetchpriority&lt;/code&gt; attribute in markup and the script-equivalent &lt;code&gt;priority&lt;/code&gt; property.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- We don&#39;t want a high priority for this above-the-fold image --&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/images/in_viewport_but_not_important.svg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fetchpriority&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;low&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;I&lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;m an unimportant image!&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- We want to initiate an early fetch for a resource, but also deprioritize it --&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/js/script.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;script&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fetchpriority&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;low&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://example.com/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;low&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Trigger a low priority fetch&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;browser-priority-and-fetchpriority&quot;&gt;Browser priority and &lt;code&gt;fetchpriority&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/fetch-priority/#browser-priority-and-fetchpriority&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You can apply the &lt;code&gt;fetchpriority&lt;/code&gt; attribute to different resources as shown in the following figure to potentially increase or reduce their computed priority. &lt;code&gt;fetchpriority=&amp;quot;auto&amp;quot;&lt;/code&gt; (◉) in each row denotes the default priority for that type of resource.&lt;/p&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;&amp;nbsp;&lt;/th&gt;
        &lt;th&gt;Layout-blocking&lt;/th&gt;
        &lt;th&gt;Load in layout-blocking phase&lt;/th&gt;
        &lt;th colspan=&quot;3&quot;&gt;Load one-at-a-time in layout-blocking phase&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;strong&gt;Blink&lt;br /&gt;Priority&lt;/strong&gt;&lt;/td&gt;
        &lt;td&gt;&lt;strong&gt;VeryHigh&lt;/strong&gt;&lt;/td&gt;
        &lt;td&gt;&lt;strong&gt;High&lt;/strong&gt;&lt;/td&gt;
        &lt;td&gt;&lt;strong&gt;Medium&lt;/strong&gt;&lt;/td&gt;
        &lt;td&gt;&lt;strong&gt;Low&lt;/strong&gt;&lt;/td&gt;
        &lt;td&gt;&lt;strong&gt;VeryLow&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;strong&gt;DevTools&lt;br /&gt;Priority&lt;/strong&gt;&lt;/td&gt;
        &lt;td&gt;&lt;strong&gt;Highest&lt;/strong&gt;&lt;/td&gt;
        &lt;td&gt;&lt;strong&gt;High&lt;/strong&gt;&lt;/td&gt;
        &lt;td&gt;&lt;strong&gt;Medium&lt;/strong&gt;&lt;/td&gt;
        &lt;td&gt;&lt;strong&gt;Low&lt;/strong&gt;&lt;/td&gt;
        &lt;td&gt;&lt;strong&gt;Lowest&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Main Resource&lt;/td&gt;
        &lt;td&gt;◉&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;CSS*** (early**)&lt;/td&gt;
        &lt;td&gt;⬆◉&lt;/td&gt;
        &lt;td&gt;⬇&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;CSS*** (late**)&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;⬆&lt;/td&gt;
        &lt;td&gt;◉&lt;/td&gt;
        &lt;td&gt;⬇&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Script (early** or not from preload scanner)&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;⬆◉&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;⬇&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Script*** (late**)&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;⬆&lt;/td&gt;
        &lt;td&gt;◉&lt;/td&gt;
        &lt;td&gt;⬇&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Script (async/defer)&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;⬆&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;◉⬇&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Font&lt;/td&gt;
        &lt;td&gt;◉&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Font (preload)&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;⬆◉&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;⬇&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Import&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;◉&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Image (in viewport)&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;⬆◉&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;⬇&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Image&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;⬆&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;◉⬇&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Media (video/audio)&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;⬆&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;◉⬇&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;SVG document&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;⬆&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;◉⬇&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;XHR (sync) - deprecated&lt;/td&gt;
        &lt;td&gt;◉&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;XHR/fetch* (async)&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;⬆◉&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;⬇&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Preload*&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;⬆◉&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;⬇&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Prefetch&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;◉&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Favicon&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;◉&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;XSL&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;◉&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; * Preload using &lt;code&gt;&amp;quot;as&amp;quot;&lt;/code&gt; or fetch using &lt;code&gt;&amp;quot;type&amp;quot;&lt;/code&gt; use the priority of the type they are requesting (e.g., preload &lt;code&gt;as=&amp;quot;stylesheet&amp;quot;&lt;/code&gt; will use Highest priority). With no &lt;code&gt;&amp;quot;as&amp;quot;&lt;/code&gt;, they will behave like an XHR.&lt;br /&gt; ** &amp;quot;Early&amp;quot; is defined as being requested before any non-preloaded images have been requested (&amp;quot;late&amp;quot; is after).&lt;br /&gt; *** CSS where the media type doesn&#39;t match is not fetched by the preload scanner and is only processed when the main parser reaches it, which usually means it will be fetched very late and with a &amp;quot;late&amp;quot; priority.&lt;br /&gt; ◉: &lt;code&gt;fetchpriority=&amp;quot;auto&amp;quot;&lt;/code&gt;&lt;br /&gt; ⬆: &lt;code&gt;fetchpriority=&amp;quot;high&amp;quot;&lt;/code&gt;&lt;br /&gt; ⬇: &lt;code&gt;fetchpriority=&amp;quot;low&amp;quot;&lt;/code&gt;&lt;br /&gt; Images within the viewport start at &amp;quot;Low&amp;quot; priority and then at layout time are boosted to &amp;quot;High&amp;quot;. By tagging it in markup using &lt;code&gt;fetchpriority=&amp;quot;high&amp;quot;&lt;/code&gt;, it can start at &amp;quot;High&amp;quot; immediately and load much faster. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Note that &lt;code&gt;fetchpriority&lt;/code&gt; sets the &lt;em&gt;relative priority&lt;/em&gt;—that is it raises or lowers the default priority by an appropriate amount, rather than explicitly setting the priority to &amp;quot;High&amp;quot; or &amp;quot;Low&amp;quot; and the browser decides the relative priority. Often this is &amp;quot;High&amp;quot; or &amp;quot;Low&amp;quot;, but not always. For example, critical CSS with &lt;code&gt;fetchpriority=&amp;quot;high&amp;quot;&lt;/code&gt; will still retain the &amp;quot;VeryHigh&amp;quot;/&amp;quot;Highest&amp;quot; priority, and using &lt;code&gt;fetchpriority=&amp;quot;low&amp;quot;&lt;/code&gt; on these will still retain the &amp;quot;High&amp;quot; priority—in neither case is the Priority explicitly set to &amp;quot;High&amp;quot; or &amp;quot;Low&amp;quot;.&lt;/p&gt;
&lt;h3 id=&quot;use-cases&quot;&gt;Use cases &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/fetch-priority/#use-cases&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You can use the &lt;code&gt;fetchpriority&lt;/code&gt; attribute to address scenarios where you may wish to provide the browser with an extra hint as to what priority to fetch a resource with.&lt;/p&gt;
&lt;h4 id=&quot;increase-the-priority-of-the-lcp-image&quot;&gt;Increase the priority of the LCP image &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/fetch-priority/#increase-the-priority-of-the-lcp-image&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;You can specify &lt;code&gt;fetchpriority=&amp;quot;high&amp;quot;&lt;/code&gt; to boost the priority of the LCP or other critical images.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lcp-image.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fetchpriority&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;high&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The following comparison shows the Google Flights page with an LCP background image loaded with and without Fetch Priority. With the priority set to high, the &lt;a href=&quot;https://www.webpagetest.org/video/compare.php?tests=211006_AiDcG3_40871b05d6040112a05be4524565cf5d%2C211006_BiDcHR_bebed947f1b6607f2d97e8a899fdc36b&amp;amp;thumbSize=200&amp;amp;ival=100&amp;amp;end=visual&quot; rel=&quot;noopener&quot;&gt;LCP improved from 2.6s to 1.9s&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;video autoplay=&quot;&quot; controls=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; playsinline=&quot;&quot;&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/1L2RBhCLSnXjCnSlevaDjy3vba73/BCngJfoVOy0YbUz8wFrM.mp4&quot; type=&quot;video/mp4&quot; /&gt;    &lt;/video&gt;
  &lt;figcaption&gt;An experiment conducted using Cloudflare workers to rewrite the Google Flights page to use Fetch Priority.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h4 id=&quot;lower-the-priority-of-above-the-fold-images&quot;&gt;Lower the priority of above-the-fold images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/fetch-priority/#lower-the-priority-of-above-the-fold-images&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;You can use &lt;code&gt;fetchpriority=&amp;quot;low&amp;quot;&lt;/code&gt; to lower the priority of above-the-fold images that may not be important for example in an image carousel.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;carousel&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;img/carousel-1.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fetchpriority&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;high&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;img/carousel-2.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fetchpriority&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;low&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;img/carousel-3.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fetchpriority&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;low&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;img/carousel-4.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fetchpriority&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;low&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In an earlier experiment with the &lt;a href=&quot;https://github.com/google/oodle-demo&quot; rel=&quot;noopener&quot;&gt;Oodle&lt;/a&gt; app, we used this to lower the priority of images that do not appear on load. It helped to cut down the load time by 2 seconds.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A side-by-side comparison of Fetch Priority when used on the Oodle app&amp;#x27;s image carousel. On the left, the browser sets default priorities for carousel images, but downloads and paints those images around two seconds slower than the example on the right, which sets a higher priority on only the first carousel image.&quot; decoding=&quot;async&quot; height=&quot;460&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Tn4OkGpqPbrSQtd1j3GV.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Tn4OkGpqPbrSQtd1j3GV.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Tn4OkGpqPbrSQtd1j3GV.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Tn4OkGpqPbrSQtd1j3GV.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Tn4OkGpqPbrSQtd1j3GV.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Tn4OkGpqPbrSQtd1j3GV.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Tn4OkGpqPbrSQtd1j3GV.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Tn4OkGpqPbrSQtd1j3GV.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Tn4OkGpqPbrSQtd1j3GV.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Tn4OkGpqPbrSQtd1j3GV.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Tn4OkGpqPbrSQtd1j3GV.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Tn4OkGpqPbrSQtd1j3GV.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Tn4OkGpqPbrSQtd1j3GV.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Tn4OkGpqPbrSQtd1j3GV.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Tn4OkGpqPbrSQtd1j3GV.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Tn4OkGpqPbrSQtd1j3GV.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Tn4OkGpqPbrSQtd1j3GV.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Tn4OkGpqPbrSQtd1j3GV.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;h4 id=&quot;lower-the-priority-of-preloaded-resources&quot;&gt;Lower the priority of preloaded resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/fetch-priority/#lower-the-priority-of-preloaded-resources&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;To stop preloaded resources from competing with other critical resources, you could provide a hint to reduce their priority. You can use this technique with images, scripts, and CSS.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Lower priority only for non-critical preloaded scripts --&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;script&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;critical-script.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/js/script.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;script&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fetchpriority&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;low&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Preload CSS without blocking other resources --&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;theme.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fetchpriority&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;low&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;onload&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value javascript language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rel&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;stylesheet&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h4 id=&quot;reprioritize-scripts&quot;&gt;Reprioritize scripts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/fetch-priority/#reprioritize-scripts&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Scripts required to make some parts of the page interactive are essential but should not block other resources. You can mark these as async with high priority.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;async_but_important.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fetchpriority&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;high&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Scripts cannot be marked as async if they rely on specific DOM states. However if they are lower down on the page, they may be downloaded with a lower priority as shown.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;blocking_but_unimportant.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fetchpriority&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;low&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h4 id=&quot;lower-the-priority-for-non-critical-data-fetches&quot;&gt;Lower the priority for non-critical data fetches &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/fetch-priority/#lower-the-priority-for-non-critical-data-fetches&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The browser executes &lt;code&gt;fetch&lt;/code&gt; with a high priority. If you have multiple fetches that may be fired simultaneously, you can use the high default priority for the more critical data fetches and lower it for less critical data.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Important validation data (high by default)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; authenticate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/user&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Less important content data (suggested low)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; suggestedContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/content/suggested&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;low&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;fetch-priority-implementation-notes&quot;&gt;Fetch Priority implementation notes &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/fetch-priority/#fetch-priority-implementation-notes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Fetch Priority can improve performance in specific use cases, as discussed above. There are some things to be aware of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;fetchpriority&lt;/code&gt; attribute is a hint and not a directive. The browser will try to respect the developer&#39;s preference. It is also possible that the browser will apply its preferences for resource priority as deemed necessary in case of conflicts.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fetch Priority should not be confused with a preload. They are both distinct because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Preload is a mandatory fetch and not a hint.&lt;/li&gt;
&lt;li&gt;Preload allows the browser to discover the resource early, but it will still fetch it with the default priority. Conversely, Fetch Priority does not aid discoverability, but does allow you to increase or decrease the fetch priority.&lt;/li&gt;
&lt;li&gt;It is easier to observe and measure the effects of a preload.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Fetch Priority can complement preloads by increasing the granularity of prioritization. If you had already specified a preload as one of the first items in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; for an LCP image, then a &lt;code&gt;high&lt;/code&gt; Fetch Priority may not result in significant gains. However, if the preload was after other resources, then a &lt;code&gt;high&lt;/code&gt; Fetch Priority can improve the LCP. If a critical image is a CSS background image, you should preload it with &lt;code&gt;fetchpriority = &amp;quot;high&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The noticeable gains due to prioritization will be more relevant in environments where more resources contend for the available network bandwidth. This is common for HTTP/1.x connections where parallel downloads are not possible or in low bandwidth HTTP/2 connections. Prioritization can resolve bottlenecks in these conditions.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CDNs do &lt;a href=&quot;https://github.com/andydavies/http2-prioritization-issues#cdns--cloud-hosting-services&quot; rel=&quot;noopener&quot;&gt;not uniformly implement HTTP/2 prioritization&lt;/a&gt;. Even if the browser communicates the priority suggested using Fetch Priority; the CDN may not reprioritize resources in the required order. This makes testing of Fetch Priority difficult. The priorities are applied both internally within the browser and with protocols that support prioritization (HTTP/2 and HTTP/3). It is still worth using even for just the internal browser prioritization independent of CDN or origin support, as that will often change when resources are requested by the browser—for example low priority resources like images are often held back from being requested while the browser processes the critical &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; items.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It may not be possible to introduce Fetch Priority as a best practice in your initial design. It is an optimization that you can apply later in the development cycle. You can check the priorities being assigned to different resources on the page, and if they do not match your expectations, you could introduce Fetch Priority for further optimization.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;using-preload-after-chrome-95&quot;&gt;Using Preload after Chrome 95 &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/fetch-priority/#using-preload-after-chrome-95&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Fetch Priority feature was available for trial in Chrome 73 to 76 but was not released due to prioritization issues with preloads fixed in Chrome 95. Prior to Chrome 95, requests issued via &lt;code&gt;&amp;lt;link rel=preload&amp;gt;&lt;/code&gt; always start before other requests seen by the preload scanner, even if the other requests have a higher priority.&lt;/p&gt;
&lt;p&gt;With the fix in Chrome 95 and the enhancement for Fetch Priority, we hope that developers will start using preload for its &lt;a href=&quot;https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/#loading-of-late-discovered-resources&quot; rel=&quot;noopener&quot;&gt;intended purpose&lt;/a&gt;—to preload resources not detected by the parser (fonts, imports, background LCP images). The placement of the &lt;code&gt;preload&lt;/code&gt; hint will affect when the resource is preloaded. Some key points on using preload are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Including the preload in HTTP headers will cause it to jump ahead of everything else.&lt;/li&gt;
&lt;li&gt;Generally, preloads will load in the order the parser gets to them for anything above &amp;quot;Medium&amp;quot; priority—so be careful if you are including preloads at the beginning of the HTML.&lt;/li&gt;
&lt;li&gt;Font preloads will probably work best towards the end of the head or beginning of the body.&lt;/li&gt;
&lt;li&gt;Import preloads (dynamic &lt;code&gt;import()&lt;/code&gt; or &lt;code&gt;modulepreload&lt;/code&gt;) should be done after the script tag that needs the import (so the actual script gets loaded/parsed first). Basically, if the script tag loads a script that will trigger the load of dependencies, make sure the &lt;code&gt;&amp;lt;link rel=preload&amp;gt;&lt;/code&gt; for the dependencies is after the parent script tag, otherwise the dependencies may end up loading before the main script. In the proper order, the main script can be parsed/eval&#39;d while the dependencies are loading.&lt;/li&gt;
&lt;li&gt;Image preloads will have a &amp;quot;Low&amp;quot; priority (without Fetch Priority) and should be ordered relative to async scripts and other low or lowest priority tags.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;history&quot;&gt;History &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/fetch-priority/#history&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Fetch Priority was first experimented with in Chrome as an origin trial in 2018 and then again in 2021 using the &lt;code&gt;importance&lt;/code&gt; attribute. At that time it was known as &lt;a href=&quot;https://github.com/WICG/priority-hints&quot; rel=&quot;noopener&quot;&gt;Priority Hints&lt;/a&gt;. The interface has since changed to &lt;code&gt;fetchpriority&lt;/code&gt; for HTML and &lt;code&gt;priority&lt;/code&gt; for JavaScript&#39;s Fetch API as part of the web standards process. To reduce confusion we now refer to this API as Fetch Priority.&lt;/p&gt;
&lt;h2 id=&quot;browser-compatibility&quot;&gt;Browser compatibility &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/fetch-priority/#browser-compatibility&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;wdi-browser-compat&quot;&gt;
  &lt;span class=&quot;wdi-browser-compat__label&quot;&gt;Browser support&lt;/span&gt;
  &lt;ul class=&quot;wdi-browser-compat__items&quot;&gt;
    &lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;chrome&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Chrome 102, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      102
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox, Not supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Edge 102, Supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
102
&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Safari preview, Preview&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;preview&quot; title=&quot;Preview&quot; aria-label=&quot;Preview&quot;&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;p&gt;&lt;/p&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/HTMLImageElement/fetchPriority#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;As of this writing, Fetch Priority is only available in Chromium-based browsers. Other browser engines or earlier versions of Chromium browsers will ignore the attribute and use their default prioritization heuristics. Until another browser implements Fetch Priority, you may notice some references—&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/HTMLImageElement/fetchPriority&quot; rel=&quot;noopener&quot;&gt;such as MDN&lt;/a&gt;—mark this as &lt;a href=&quot;https://developer.mozilla.org/docs/MDN/Writing_guidelines/Experimental_deprecated_obsolete#experimental&quot; rel=&quot;noopener&quot;&gt;&lt;em&gt;Experimental&lt;/em&gt;&lt;/a&gt;, however Fetch Priority is now standardized and included in the &lt;a href=&quot;https://html.spec.whatwg.org/multipage/urls-and-fetching.html#fetch-priority-attributes&quot; rel=&quot;noopener&quot;&gt;HTML living standard&lt;/a&gt; and &lt;a href=&quot;https://fetch.spec.whatwg.org/#request-priority&quot; rel=&quot;noopener&quot;&gt;Fetch living standard&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/fetch-priority/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Developers are likely to be interested in Fetch Priority with the fixes in preload behavior and the recent focus on Core Web Vitals and LCP. They now have additional knobs available to achieve their desired loading sequence.&lt;/p&gt;
</content>
    <author>
      <name>Leena Sohoni</name>
    </author><author>
      <name>Addy Osmani</name>
    </author><author>
      <name>Patrick Meenan</name>
    </author><author>
      <name>Barry Pollard</name>
    </author>
  </entry>
  
  <entry>
    <title>Core Web Vitals workflows with Google tools</title>
    <link href="https://web.dev/vitals-tools/"/>
    <updated>2021-08-09T00:00:00Z</updated>
    <id>https://web.dev/vitals-tools/</id>
    <content type="html" mode="escaped">&lt;p&gt;&lt;a href=&quot;https://web.dev/vitals/&quot;&gt;Core Web Vitals&lt;/a&gt; are a set of metrics that assess the user experience on criteria such as load performance, responsiveness to user input, and layout stability.&lt;/p&gt;
&lt;p&gt;A workflow for improving Core Web Vitals for your website will be explored in this guide, but where that workflow begins depends on whether you&#39;re collecting your own field data. Where it ends may depend on which of Google&#39;s tools you&#39;ll find useful in diagnosing and fixing user experience problems.&lt;/p&gt;
&lt;h2 id=&quot;core-web-vitals-are-best-measured-in-the-field&quot;&gt;Core Web Vitals are best measured in the field &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#core-web-vitals-are-best-measured-in-the-field&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Core Web Vitals are specifically designed to measure how users experience your website—they are &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/&quot;&gt;user centric metrics&lt;/a&gt;. Lab-based tools such as Lighthouse are diagnostic tools to highlight potential performance problems and best practices. Lab-based tools are run under certain, predefined conditions and may not reflect the real-life Core Web Vitals measurements your users experience.&lt;/p&gt;
&lt;aside class=&quot;aside flow color-secondary-box-text bg-secondary-box-bg&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Highlighter pen&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M10.22 9.49l-5.91 6c-.77.8-.7 2.05.08 2.85L.77 22h5.68l.74-.75c.78.81 1.95.86 2.73.05l5.96-6.05-5.66-5.76zm12.46-4l-2.82-2.87c-.78-.8-2.07-.84-2.84-.04l-5.75 5.85 5.66 5.75 5.69-5.78c.77-.81.83-2.11.06-2.91z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Key Term&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;a href=&quot;https://web.dev/lab-and-field-data-differences/#lab-data&quot;&gt;&lt;strong&gt;Lab data&lt;/strong&gt;&lt;/a&gt; describes how &lt;em&gt;hypothetical&lt;/em&gt; users &lt;em&gt;may&lt;/em&gt; experience your website. &lt;a href=&quot;https://web.dev/lab-and-field-data-differences/#field-data&quot;&gt;&lt;strong&gt;Field data&lt;/strong&gt;&lt;/a&gt; describes how &lt;em&gt;real&lt;/em&gt; users &lt;em&gt;actually&lt;/em&gt; experienced your website. Field data is also known as Real User Monitoring (RUM), and is typically collected by monitoring real user experiences using JavaScript on the pages they load, and reporting various metrics to an analytics solution. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;For example, Lighthouse is a lab-based tool that runs tests with simulated throttling in a simulated desktop or mobile environment. While such simulations of slower network and device conditions are helpful when trying to diagnose performance problems, &lt;a href=&quot;https://web.dev/lab-and-field-data-differences/&quot;&gt;they&#39;re just a single slice&lt;/a&gt; of the large variety in network conditions and device capabilities, and so may not reflect what users on your sites are experiening.&lt;/p&gt;
&lt;p&gt;Lab-based tools like Lighthouse also typically do a &amp;quot;cold load&amp;quot; of a webpage as a totally new visitor. This is often the slowest load, but in real life, visitors may have some assets cached if they have visited before, or when they are browsing around the site. New visitors, and tools, also may experience the site differently with cookie banners or other content present to them.&lt;/p&gt;
&lt;p&gt;In short, while lab-based tools can give an indication of potential performance problems and help you debug and iterate, they may not represent how many of your visitors actually experience your website. Use field data for measuring real-world performance, and lab-based tools like Lighthouse for diagnostics of how to improve it. See also the &lt;a href=&quot;https://web.dev/vitals-tools/#when-to-use-lighthouse&quot;&gt;When to use Lighthouse&lt;/a&gt; section.&lt;/p&gt;
&lt;p&gt;Google measures Core Web Vitals through the &lt;a href=&quot;https://developer.chrome.com/docs/crux/&quot; rel=&quot;noopener&quot;&gt;Chrome User Experience Report (CrUX)&lt;/a&gt;. This is a public dataset collected from real Chrome users. It is the backbone of many Google and third-party tools which report a site&#39;s Core Web Vitals.&lt;/p&gt;
&lt;p&gt;CrUX has its limitations, though. It can often tell you &lt;em&gt;when&lt;/em&gt; there is a problem, but often has insufficient data to tell you &lt;em&gt;why&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id=&quot;collect-your-own-field-data-if-possible&quot;&gt;Collect your own field data if possible &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#collect-your-own-field-data-if-possible&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The best dataset for improving website performance in the field is the one &lt;em&gt;you&lt;/em&gt; build. That starts with collecting field data from your website&#39;s visitors. How you do this depends on the size of your organization, and whether you want to pay for &lt;a href=&quot;https://en.wikipedia.org/wiki/Real_user_monitoring#RUM_software&quot; rel=&quot;noopener&quot;&gt;a third-party solution&lt;/a&gt; or create your own.&lt;/p&gt;
&lt;p&gt;Paid solutions will almost certainly measure Core Web Vitals (and other performance metrics) and usually provide a variety of tools to dig into the resulting data. In large organizations with significant resources, this may be the preferred method.&lt;/p&gt;
&lt;p&gt;However, you may not be part of a large organization—or even one with the means to afford a third-party solution. In these cases, Google&#39;s &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;web-vitals&lt;/code&gt; library&lt;/a&gt; will help you gather &lt;em&gt;all&lt;/em&gt; Web Vitals. However, you&#39;ll be responsible for how that data is reported, stored, and analyzed.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; If you&#39;re building your own metrics collection and reporting system, &lt;a href=&quot;https://web.dev/vitals-field-measurement-best-practices/&quot;&gt;this guide of best practices&lt;/a&gt; is worth a read to avoid common pitfalls. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;If you&#39;re already using Google Analytics, but you haven&#39;t started collecting your own field data, there may be an opportunity for you to use the &lt;code&gt;web-vitals&lt;/code&gt; library to &lt;a href=&quot;https://web.dev/debug-performance-in-the-field/#usage-with-the-web-vitals-javascript-library&quot;&gt;send Web Vitals collected in the field to Google Analytics&lt;/a&gt; and use the &lt;a href=&quot;https://web.dev/vitals-ga4/&quot;&gt;BigQuery exports of GA4&lt;/a&gt; to report on the data.&lt;/p&gt;
&lt;h2 id=&quot;understanding-googles-tools&quot;&gt;Understanding Google&#39;s tools &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#understanding-googles-tools&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Regardless of whether you&#39;re collecting your own field data, there are several Google tools that may be useful in analyzing Core Web Vitals. Before establishing a workflow, a high-level overview of each tool can help you to understand which tools may—or may not—be best for you.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; As you continue, understand that it&#39;s not necessary for you to use &lt;em&gt;all&lt;/em&gt; of the tools in this guide—only those that you believe will help you in improving your website&#39;s Core Web Vitals. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;chrome-user-experience-report-crux&quot;&gt;Chrome User Experience Report (CrUX) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#chrome-user-experience-report-crux&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As mentioned previously, CrUX is a public data set of field data gathered from &lt;a href=&quot;https://developer.chrome.com/docs/crux/methodology/#user-eligibility&quot; rel=&quot;noopener&quot;&gt;a segment of real Google Chrome users&lt;/a&gt; from millions of websites. It includes Core Web Vital metrics and other metrics for websites with sufficient traffic.&lt;/p&gt;
&lt;p&gt;CrUX is available as a monthly &lt;a href=&quot;https://developer.chrome.com/docs/crux/bigquery/&quot; rel=&quot;noopener&quot;&gt;BigQuery dataset&lt;/a&gt; at the origin level, or as a &lt;a href=&quot;https://developer.chrome.com/docs/crux/api/&quot; rel=&quot;noopener&quot;&gt;daily API&lt;/a&gt; at the URL or origin-level, provided a URL or origin has enough samples in the CrUX dataset. The BigQuery data is also viewable in an easy to use &lt;a href=&quot;https://developer.chrome.com/docs/crux/dashboard/&quot; rel=&quot;noopener&quot;&gt;CrUX Dashboard&lt;/a&gt; allowing sites to review historical trends for their site.&lt;/p&gt;
&lt;h4 id=&quot;when-to-use-crux&quot;&gt;When to use CrUX &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#when-to-use-crux&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Even if you gather your own field data, CrUX is still useful. Though CrUX represents a subset of Chrome users, it&#39;s helpful to compare your website&#39;s field data to see how it aligns with its CrUX data. There are advantages and disadvantages to each, which &lt;a href=&quot;https://web.dev/crux-and-rum-differences/&quot;&gt;can result in differences&lt;/a&gt;. If you currently don&#39;t gather &lt;em&gt;any&lt;/em&gt; field data for your website, CrUX is especially valuable to provide a high-level overview—provided your website is represented in its dataset.&lt;/p&gt;
&lt;p&gt;You can use CrUX directly, or via another tool (including those mentioned below). Using the CrUX dataset directly, either via BigQuery or the API, is useful to surface data that is not currently shown in other tools—for example country-level data is often not available on other tools, or to view the &lt;a href=&quot;https://developer.chrome.com/docs/crux/methodology/#metrics&quot; rel=&quot;noopener&quot;&gt;additional metrics in CrUX&lt;/a&gt; which again are often not surfaced in other tooling.&lt;/p&gt;
&lt;h4 id=&quot;when-not-to-use-crux&quot;&gt;When &lt;em&gt;not&lt;/em&gt; to use CrUX &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#when-not-to-use-crux&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;CrUX only represents Chrome users, and even then, &lt;a href=&quot;https://developer.chrome.com/docs/crux/methodology/#user-eligibility&quot; rel=&quot;noopener&quot;&gt;only a subset of Chrome users&lt;/a&gt;. A full RUM solution can include more experiences across Chrome and other browsers where they support the Web Vital metrics.&lt;/p&gt;
&lt;p&gt;Websites that don&#39;t receive enough traffic are not represented in the CrUX dataset. If this is the case for you, you&#39;ll need to gather your own field data to understand how your website performs in the field, as CrUX won&#39;t be an option. Alternatively, you will need to depend on lab data, but with the limitations that it may not be representative above.&lt;/p&gt;
&lt;p&gt;Since the data CrUX provides is a rolling average over the previous 28 days, it&#39;s not an ideal tool during development, as it will take a fair amount of time for improvements to be reflected in the CrUX dataset.&lt;/p&gt;
&lt;p&gt;Finally, as a public dataset, CrUX is limited to how much information it can make available, and how this data can be queried. Capturing your own RUM data allows you to gather more details (for example, &lt;a href=&quot;https://web.dev/debug-performance-in-the-field/#lcp&quot;&gt;the LCP element&lt;/a&gt;), and slice and dice the data more to identify problems. Do logged in users experience better or worse Core Web Vitals than logged out users? Do users with a slow LCP have a particular LCP element? Which interactions are causing high FID and INP values?&lt;/p&gt;
&lt;h3 id=&quot;pagespeed-insights-psi&quot;&gt;PageSpeed Insights (PSI) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#pagespeed-insights-psi&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://pagespeed.web.dev/&quot; rel=&quot;noopener&quot;&gt;PSI&lt;/a&gt; is a tool that reports field data from CrUX &lt;em&gt;and&lt;/em&gt; lab from Lighthouse for a given page. See those individual sections for more details.&lt;/p&gt;
&lt;h4 id=&quot;when-to-use-psi&quot;&gt;When to use PSI &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#when-to-use-psi&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;PSI is great for assessing CrUX performance at the page level or origin-level, for both mobile and desktop users. It&#39;s a good choice for an initial overview of Core Web Vitals for a page or site. It also allows you to easily view Core Web Vitals data for other sites like competitors.&lt;/p&gt;
&lt;p&gt;PSI also provides Lighthouse data, which gives useful recommendations to improve your Core Web Vitals—if the metrics align. Where these do not align, Lighthouse recommendations may be less relevant.&lt;/p&gt;
&lt;p&gt;Since Lighthouse is run from the server, it can form a more consistent baseline than running Lighthouse from DevTools.&lt;/p&gt;
&lt;h4 id=&quot;when-not-to-use-psi&quot;&gt;When &lt;em&gt;not&lt;/em&gt; to use PSI &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#when-not-to-use-psi&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;PSI is only available for public URLs. It cannot be used on development sites that are not publicly accessible.&lt;/p&gt;
&lt;p&gt;CrUX data is only available when sites meet certain &lt;a href=&quot;https://developer.chrome.com/docs/crux/methodology/#eligibility&quot; rel=&quot;noopener&quot;&gt;eligibility criteria&lt;/a&gt;, including site popularity thresholds. PSI is less useful when CrUX data is not available for a page or origin as it can only show the Lighthouse lab data in these cases.&lt;/p&gt;
&lt;p&gt;Similarly, if you only have origin-level CrUX data rather than the specific URL being tested, then this also limits its usefulness of correlating the origin-level field data to the page-level lab diagnostics. Having the origin-level field data is still very useful information to have as a summary of the site&#39;s performance and the Lighthouse audits may help, but extra caution should be used in this case.&lt;/p&gt;
&lt;p&gt;Finally, where page-level data is available in CrUX, but differs from the Lighthouse lab data, recommendations from Lighthouse may be of limited value. This can happen particularly for &lt;a href=&quot;https://web.dev/optimize-cls/#identifying-post-load-cls-issues&quot;&gt;post-load CLS issues&lt;/a&gt;, and for interactivity Core Web Vitals (FID and INP) where lab-based audits are less useful.&lt;/p&gt;
&lt;h3 id=&quot;search-console&quot;&gt;Search Console &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#search-console&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://search.google.com/search-console/about&quot; rel=&quot;noopener&quot;&gt;Search Console&lt;/a&gt; measures your site&#39;s search traffic and performance, &lt;a href=&quot;https://support.google.com/webmasters/answer/9205520&quot; rel=&quot;noopener&quot;&gt;including Core Web Vitals&lt;/a&gt;. It is only available to site owners who have &lt;a href=&quot;https://support.google.com/webmasters/answer/34592&quot; rel=&quot;noopener&quot;&gt;confirmed their ownership of the site&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A valuable feature of Search Console is that it groups similar pages (for example, pages that use the same template) into a single group assessment. Search Console also includes a Core Web Vitals report based on field data from CrUX.&lt;/p&gt;
&lt;h4 id=&quot;when-to-use-search-console&quot;&gt;When to use Search Console &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#when-to-use-search-console&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Search Console is well-suited for both developers and those in non-developer roles to assess both search and page performance in ways other Google tools don&#39;t. Its presentation of CrUX data and grouping of pages by similarity offers novel insight into how performance improvements impact entire categories of pages.&lt;/p&gt;
&lt;h4 id=&quot;when-not-to-use-search-console&quot;&gt;When &lt;em&gt;not&lt;/em&gt; to use Search Console &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#when-not-to-use-search-console&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Search Console may not be a fit for projects that use different third-party tools which group pages by similarity, or if a website isn&#39;t represented in the CrUX dataset.&lt;/p&gt;
&lt;p&gt;Page grouping can also be somewhat confusing when the example pages in a group have different characteristics than the rest of the group—for example, if the group fails particular Core Web Vitals overall, but the example pages all seem to pass the same Core Web Vitals. This can happen when a group contains a long tail or seldomly visited pages that may be slower to load, as they&#39;re less likely to be cached. When there are sufficient volumes of these pages in the long tail, they can influence the group&#39;s overall pass rate.&lt;/p&gt;
&lt;h3 id=&quot;lighthouse&quot;&gt;Lighthouse &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#lighthouse&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt; is a lab tool that provides specific opportunities for improving page performance. &lt;a href=&quot;https://web.dev/lighthouse-user-flows/&quot;&gt;Lighthouse user flows&lt;/a&gt; also allow developers to script interaction flows for performance testing beyond page load.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Because Lighthouse is a lab tool, and &lt;a href=&quot;https://web.dev/fid/&quot;&gt;First Input Delay (FID)&lt;/a&gt; and &lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt; are field metrics, it reports &lt;a href=&quot;https://web.dev/tbt/&quot;&gt;Total Blocking Time&lt;/a&gt; instead. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/GoogleChrome/lighthouse-ci&quot; rel=&quot;noopener&quot;&gt;Lighthouse-CI&lt;/a&gt; is a related tool that runs Lighthouse during project builds and deploys to assist with performance regression testing. It presents a Lighthouse report along with pull requests, and tracks performance metrics over time.&lt;/p&gt;
&lt;h4 id=&quot;when-to-use-lighthouse&quot;&gt;When to use Lighthouse &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#when-to-use-lighthouse&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Lighthouse is excellent for finding performance improvement opportunities during development in both local and staging environments. Lighthouse CI is similarly useful in the build and deploy phases to staging and production environments, where performance regression testing is needed to preserve good user experiences.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Don&#39;t rely solely on Lighthouse CI during production deployments, as you may miss performance regressions that you&#39;d otherwise catch in local and staging environments during development with Lighthouse. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;when-not-to-use-lighthouse&quot;&gt;When &lt;em&gt;not&lt;/em&gt; to use Lighthouse &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#when-not-to-use-lighthouse&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Lighthouse (or Lighthouse CI) &lt;strong&gt;is &lt;em&gt;not&lt;/em&gt; a substitute for field data&lt;/strong&gt;. Lighthouse is primarily a diagnostic tool listing potential issues and best practices from a predefined page load. The recommendations it surfaces may not always match the performance experienced by your users.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Important&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Always concentrate on field Core Web Vitals over Lighthouse metrics and scores&lt;/strong&gt;. In particular, the Performance Score of Lighthouse is a broad measure of that lab test and &lt;a href=&quot;https://philipwalton.com/articles/my-challenge-to-the-web-performance-community/&quot;&gt;often does not correlate&lt;/a&gt; with field Core Web Vitals. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;While Lighthouse can be used to diagnose production sites through tools like PageSpeed Insights, Lighthouse would ideally be used in development and continuous integration environments to address performance issues before they reach production.&lt;/p&gt;
&lt;h3 id=&quot;web-vitals-extension&quot;&gt;Web Vitals extension &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#web-vitals-extension&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://chrome.google.com/webstore/detail/web-vitals/ahfhijdlegdabablpippeagghigmibma&quot; rel=&quot;noopener&quot;&gt;Web Vitals Chrome extension&lt;/a&gt; is a diagnostic tool that surfaces Core Web Vitals metrics as you browse the web. It also includes CrUX data for the current page if it is represented in the CrUX dataset, and provides &lt;a href=&quot;https://web.dev/debug-cwvs-with-web-vitals-extension/&quot;&gt;debugging information&lt;/a&gt; to help you identify Core Web Vitals performance problems.&lt;/p&gt;
&lt;h4 id=&quot;when-to-use-the-web-vitals-extension&quot;&gt;When to use the Web Vitals extension &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#when-to-use-the-web-vitals-extension&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The Web Vitals extension can be used by anyone in any role to assess a page&#39;s Core Web Vitals at all points of the page lifecycle. It is useful as a &amp;quot;live&amp;quot; view of performance as you interact with the page to attempt to uncover performance issues—particularly for post-load issues you might see with the CLS and INP metrics.&lt;/p&gt;
&lt;h4 id=&quot;when-not-to-use-the-web-vitals-extension&quot;&gt;When &lt;em&gt;not&lt;/em&gt; to use the Web Vitals extension &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#when-not-to-use-the-web-vitals-extension&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The Web Vitals extension isn&#39;t a holistic assessment of page performance. In addition, the metrics it reports are highly dependent the environment in which it runs, and developers often have higher powered machines or access to faster networks.&lt;/p&gt;
&lt;h3 id=&quot;the-performance-panel-in-chrome-devtools&quot;&gt;The Performance panel in Chrome DevTools &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#the-performance-panel-in-chrome-devtools&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/docs/devtools/&quot; rel=&quot;noopener&quot;&gt;Chrome DevTools&lt;/a&gt; is a collection of in-browser development tools, including the &lt;a href=&quot;https://developer.chrome.com/docs/devtools/#performance&quot; rel=&quot;noopener&quot;&gt;Performance panel&lt;/a&gt;. The Performance panel is a lab tool that profiles all page activity during page load or a recorded time period. It offers deep insight into everything it observes across dimensions such as network, rendering, painting, and scripting activity, as well as a page&#39;s Core Web Vitals.&lt;/p&gt;
&lt;h4 id=&quot;when-to-use-the-performance-panel&quot;&gt;When to use the Performance panel &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#when-to-use-the-performance-panel&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The Performance panel should be used by developers during development to gain deep insight into page performance. This is particularly useful to debug responsiveness issues affecting FID or INP. Once a poorly responding interaction is identified and repeatable, the Performance panel can provide a wealth of data as to what is going on in the browser to help understand the issue, from main thread blocking, to JavaScript call stacks, to rendering work.&lt;/p&gt;
&lt;h4 id=&quot;when-not-to-use-the-performance-panel&quot;&gt;When &lt;em&gt;not&lt;/em&gt; to use the Performance panel &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#when-not-to-use-the-performance-panel&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The Performance panel is a developer tool that provides lab data only. It&#39;s not a substitute for field data. It contains a lot of debugging information, but because of that, it may not be easy to use for novice developers or those in non-developer roles.&lt;/p&gt;
&lt;h2 id=&quot;a-three-step-workflow-for-ensuring-your-websites-core-web-vitals-stay-healthy&quot;&gt;A three step workflow for ensuring your website&#39;s Core Web Vitals stay healthy &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#a-three-step-workflow-for-ensuring-your-websites-core-web-vitals-stay-healthy&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When working to improve the user experience, it&#39;s best to think of the process as a continuous cycle. For improving Core Web Vitals and other performance metrics, one approach could be:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Evaluate website health and identify pain points.&lt;/li&gt;
&lt;li&gt;Debug and optimize.&lt;/li&gt;
&lt;li&gt;Monitor with continuous integration tools to catch and prevent regressions.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A diagram of a three step process, rendered as a continuous cycle. The first step reads &amp;#x27;Evaluate website health and identify paint points&amp;#x27;, the second &amp;#x27;Debug and optimize&amp;#x27;, and the third &amp;#x27;Monitor and continuous development&amp;#x27;.&quot; decoding=&quot;async&quot; height=&quot;465&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/9k3o679FUq63WT1kbxHe.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/9k3o679FUq63WT1kbxHe.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/9k3o679FUq63WT1kbxHe.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/9k3o679FUq63WT1kbxHe.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/9k3o679FUq63WT1kbxHe.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/9k3o679FUq63WT1kbxHe.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/9k3o679FUq63WT1kbxHe.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/9k3o679FUq63WT1kbxHe.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/9k3o679FUq63WT1kbxHe.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/9k3o679FUq63WT1kbxHe.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/9k3o679FUq63WT1kbxHe.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/9k3o679FUq63WT1kbxHe.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/9k3o679FUq63WT1kbxHe.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/9k3o679FUq63WT1kbxHe.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/9k3o679FUq63WT1kbxHe.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/9k3o679FUq63WT1kbxHe.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/9k3o679FUq63WT1kbxHe.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/9k3o679FUq63WT1kbxHe.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;step-1-evaluate-website-health-and-identify-opportunities-for-improvement&quot;&gt;Step 1: Evaluate website health and identify opportunities for improvement &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#step-1-evaluate-website-health-and-identify-opportunities-for-improvement&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It is best to start with field data to evaluate website health.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use &lt;a href=&quot;https://pagespeed.web.dev/&quot; rel=&quot;noopener&quot;&gt;PageSpeed Insights&lt;/a&gt; to view overall Core Web Vitals experience metrics on the origin, and specific information on an individual URL.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://search.google.com/search-console/about&quot; rel=&quot;noopener&quot;&gt;Search Console&lt;/a&gt; can be useful to identify pages which need improvement where its page grouping feature works well for your site.&lt;/li&gt;
&lt;li&gt;If you have RUM data, then that is often the best option to be able to identify particular pages or traffic segments with issues.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Whether you analyze field data you collect yourself or CrUX data, this first step is vital. If you&#39;re not gathering field data, CrUX data may be enough to guide you—again, provided your website is represented in the dataset.&lt;/p&gt;
&lt;h4 id=&quot;analyze-site-performance-with-pagespeed-insights&quot;&gt;Analyze site performance with PageSpeed Insights &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#analyze-site-performance-with-pagespeed-insights&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of how PageSpeed Insights portrays CrUX data for a URL&amp;#x27;s Core Web Vitals. Each of the Core Web Vitals is displayed separately, while grouping each Core Web Vital in the &amp;#x27;Good&amp;#x27;, &amp;#x27;Needs Improvement&amp;#x27;, and &amp;#x27;Poor&amp;#x27; thresholds for the last 28 days.&quot; decoding=&quot;async&quot; height=&quot;477&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/YEe3RQwgIWgQTHFV5sc0.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/YEe3RQwgIWgQTHFV5sc0.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/YEe3RQwgIWgQTHFV5sc0.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/YEe3RQwgIWgQTHFV5sc0.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/YEe3RQwgIWgQTHFV5sc0.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/YEe3RQwgIWgQTHFV5sc0.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/YEe3RQwgIWgQTHFV5sc0.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/YEe3RQwgIWgQTHFV5sc0.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/YEe3RQwgIWgQTHFV5sc0.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/YEe3RQwgIWgQTHFV5sc0.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/YEe3RQwgIWgQTHFV5sc0.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/YEe3RQwgIWgQTHFV5sc0.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/YEe3RQwgIWgQTHFV5sc0.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/YEe3RQwgIWgQTHFV5sc0.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/YEe3RQwgIWgQTHFV5sc0.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/YEe3RQwgIWgQTHFV5sc0.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/YEe3RQwgIWgQTHFV5sc0.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/YEe3RQwgIWgQTHFV5sc0.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;PageSpeed Insights displays the CrUX data covering the last 28 days of user experience data at the 75th percentile. This means that if 75% of user experiences meet the &lt;a href=&quot;https://web.dev/defining-core-web-vitals-thresholds/&quot;&gt;threshold set for a given metric&lt;/a&gt;, then the experience is considered &amp;quot;good&amp;quot;.&lt;/p&gt;
&lt;p&gt;If you have a specific page in mind to look at the performance of, then use that. For an overall view of a site when you first start optimizing, you may wish to start with the home page, as it is typically one of the most popular pages on many sites.&lt;/p&gt;
&lt;p&gt;Concentrate on the &lt;strong&gt;what your real users are experiencing&lt;/strong&gt; section of PSI initially. You will see up to four views of the data: mobile and desktop for the URL entered and the whole origin. Compare these and see how they differ. Mobile is typically less performant than desktop since it is a more resource-constrained device operating under potentially less stable network conditions. If the URL and origin data are significantly different, then try to understand why: home pages are often the first pages visited (that is, a landing page) so can be slower than the origin users take the full brunt of an unprimed browser cache. Subsequent pages will likely load faster, as any shared assets will be cached, bringing down the aggregate origin-level data.&lt;/p&gt;
&lt;p&gt;PSI also shows all three Core Web Vitals (LCP, CLS, and FID) and the pending INP metric, plus the diagnostic TTFB and FCP metrics. Are any of the Core Web Vitals failing, and by how much? This will indicate where to concentrate your efforts.&lt;/p&gt;
&lt;p&gt;Understand the relationships between these numbers—particularly for LCP. If LCP is slow, as it is in this example, then look at TTFB and FCP which are both milestones to that metric. In this example we have a 1.8 second TTFB, which is going to make it very tough to meet the 2.5 second recommended threshold for good LCP. This suggests either a slow backend (server issues or a lack of CDN), slower networks, or redirects delaying the first HTML bytes. Look at the &lt;a href=&quot;https://web.dev/optimize-ttfb/&quot;&gt;Optimize TTFB guide&lt;/a&gt; for more information. FCP takes another second on top of that, which again may be indicative of slower networks. LCP is not long after FCP in this example suggesting the LCP resource is well optimized once the page itself loads.&lt;/p&gt;
&lt;p&gt;For CLS, look at the CrUX CLS and the Lighthouse CLS scores to see if this is a load CLS issue (which Lighthouse will catch and advise on), or a post-load CLS issue that Lighthouse won&#39;t catch. More for information &lt;a href=&quot;https://web.dev/optimize-cls/#understanding-where-your-shifts-are-coming-from&quot;&gt;see the Optimize CLS guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For responsiveness, look at the FID and INP scores. Look at the TBT audits in Lighthouse to see if a lot of JavaScript processing is happening during the initial page load, which is likely to impact INP. INP can be a tricky metric to improve, so consult the &lt;a href=&quot;https://web.dev/optimize-inp/&quot;&gt;Optimize INP guide&lt;/a&gt; for more information.&lt;/p&gt;
&lt;h4 id=&quot;identify-poorly-performing-pages-in-search-console&quot;&gt;Identify poorly performing pages in Search Console &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#identify-poorly-performing-pages-in-search-console&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of a Core Web Vitals report in Search Console. The report is broken down into Desktop and Mobile categories, with line graphs detailing the distribution of pages with Core Web Vitals in the &amp;#x27;Good&amp;#x27;, &amp;#x27;Needs Improvement&amp;#x27;, and &amp;#x27;Poor&amp;#x27; categories over time.&quot; decoding=&quot;async&quot; height=&quot;639&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/DWdv878oyTdEWQViRh06.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/DWdv878oyTdEWQViRh06.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/DWdv878oyTdEWQViRh06.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/DWdv878oyTdEWQViRh06.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/DWdv878oyTdEWQViRh06.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/DWdv878oyTdEWQViRh06.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/DWdv878oyTdEWQViRh06.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/DWdv878oyTdEWQViRh06.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/DWdv878oyTdEWQViRh06.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/DWdv878oyTdEWQViRh06.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/DWdv878oyTdEWQViRh06.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/DWdv878oyTdEWQViRh06.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/DWdv878oyTdEWQViRh06.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/DWdv878oyTdEWQViRh06.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/DWdv878oyTdEWQViRh06.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/DWdv878oyTdEWQViRh06.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/DWdv878oyTdEWQViRh06.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/DWdv878oyTdEWQViRh06.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;While PSI is useful when you have a specific URL you want to test or the site as a whole, Search Console can help target your efforts to particular types of pages. This is particularly useful if many pages share common themes or technologies and Search Console can successfully identify these.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://support.google.com/webmasters/answer/9205520&quot; rel=&quot;noopener&quot;&gt;Core Web Vitals report in Search Console&lt;/a&gt; shows the big picture of your website&#39;s performance, but you can still drill down into specific pages that need attention. With Search Console, you can also:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Identify individual page groups that need improvement, and those that currently provide a good user experience.&lt;/li&gt;
&lt;li&gt;Get granular data on performance by URL grouped by status, metric, and groups of similar web pages (such as product detail pages on an e-commerce website).&lt;/li&gt;
&lt;li&gt;Get detailed reports that bucket URLs in each user experience quality category for both mobile and desktop.&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; The data in Search Console is different from what&#39;s shown in the PageSpeed Insights origin view or the CrUX dashboard. This is because Search Console organizes information by URL, whereas PageSpeed Insights origin view and CrUX Dashboard organizes data by origin. If you have few poorly performing URLs, but those URLs receive a large portion of overall traffic, Search Console will only show a few poorly performing URLs while CrUX dashboard will show a high percentage of poor user experiences. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Once you have some specific pages to look at, you can use PSI as explained previously to gather further understanding of the issues for those pages.&lt;/p&gt;
&lt;h3 id=&quot;step-2-debug-and-optimize&quot;&gt;Step 2: Debug and optimize &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#step-2-debug-and-optimize&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In step 1, you should have identified pages which require performance improvements, and also which of the Core Web Vitals metrics you want to improve. You can use the Google tooling to get further information to understand the root cause to identify the issue.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run a &lt;a href=&quot;https://web.dev/vitals-tools/#Lighthouse&quot;&gt;Lighthouse&lt;/a&gt; audit to get page-level guidance&lt;/li&gt;
&lt;li&gt;Use the &lt;a href=&quot;https://web.dev/vitals-tools/#web-vitals-extension&quot;&gt;Web Vitals extension&lt;/a&gt; to analyze Core Web Vitals in real time.&lt;/li&gt;
&lt;li&gt;Use the &lt;a href=&quot;https://web.dev/vitals-tools/#the-performance-panel-in-chrome-devtools&quot;&gt;Performance panel&lt;/a&gt; in Chrome DevTools to debug performance issues and test code changes.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For more detailed guidance, see these guides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/optimize-lcp/&quot;&gt;Optimizing LCP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/optimize-cls/&quot;&gt;Optimizing CLS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/optimize-fid/&quot;&gt;Optimizing FID&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/optimize-inp/&quot;&gt;Optimizing INP&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;uncover-opportunities-with-lighthouse&quot;&gt;Uncover opportunities with Lighthouse &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#uncover-opportunities-with-lighthouse&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;PageSpeed Insights runs Lighthouse for you, but for local development it is also possible to run Lighthouse from Chrome DevTools, which is useful to validate fixes locally.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of a Lighthouse report within Chrome DevTools. The report breaks down scores across five categories, with the report focused on the &amp;#x27;Performance&amp;#x27; category, with results at the bottom of the report window.&quot; decoding=&quot;async&quot; height=&quot;526&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/k6HffrY6tUbreyzi2mVk.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/k6HffrY6tUbreyzi2mVk.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/k6HffrY6tUbreyzi2mVk.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/k6HffrY6tUbreyzi2mVk.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/k6HffrY6tUbreyzi2mVk.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/k6HffrY6tUbreyzi2mVk.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/k6HffrY6tUbreyzi2mVk.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/k6HffrY6tUbreyzi2mVk.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/k6HffrY6tUbreyzi2mVk.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/k6HffrY6tUbreyzi2mVk.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/k6HffrY6tUbreyzi2mVk.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/k6HffrY6tUbreyzi2mVk.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/k6HffrY6tUbreyzi2mVk.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/k6HffrY6tUbreyzi2mVk.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/k6HffrY6tUbreyzi2mVk.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/k6HffrY6tUbreyzi2mVk.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/k6HffrY6tUbreyzi2mVk.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/k6HffrY6tUbreyzi2mVk.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;A key point is to validate that the Lighthouse audit replicates the issues you are trying to solve (for example, slow LCP, or CLS issues). Out of the box, Lighthouse only assesses the user experience during page load. Since it&#39;s a lab tool, it also excludes FID and INP in favor of TBT.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; By default, Lighthouse simulates a mid-tier mobile device on a throttled slow 4G connection. This may find issues that wouldn&#39;t ordinarily appear on high-speed devices and fast internet connections. This simulated throttling may not be representative of the variety of user experiences among your website&#39;s user base at the 75th percentile. However, these metrics are an indicator of where performance problems exist, and may translate into better performance overall in the field if the problems Lighthouse finds are addressed. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;When the Lighthouse metrics suggest a similar problem as the one you are trying to solve, the wealth of information in its audits can help identify issues and suggest solutions.&lt;/p&gt;
&lt;p&gt;You can filter the audits to just the Core Web Vitals you are interested in to focus on fixes for issues related to a specific metric:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Lighthouse filter options for key metrics&quot; decoding=&quot;async&quot; height=&quot;100&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 700px) 700px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wae09J8FYUe0PvFBkH4b.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wae09J8FYUe0PvFBkH4b.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wae09J8FYUe0PvFBkH4b.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wae09J8FYUe0PvFBkH4b.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wae09J8FYUe0PvFBkH4b.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wae09J8FYUe0PvFBkH4b.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wae09J8FYUe0PvFBkH4b.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wae09J8FYUe0PvFBkH4b.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wae09J8FYUe0PvFBkH4b.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wae09J8FYUe0PvFBkH4b.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wae09J8FYUe0PvFBkH4b.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wae09J8FYUe0PvFBkH4b.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wae09J8FYUe0PvFBkH4b.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wae09J8FYUe0PvFBkH4b.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wae09J8FYUe0PvFBkH4b.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wae09J8FYUe0PvFBkH4b.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wae09J8FYUe0PvFBkH4b.png?auto=format&amp;w=1400 1400w&quot; width=&quot;700&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;For FID and INP, use the TBT audits to identify issues that can potentially affect those metrics, but be aware that without interactions, Lighthouse is limited in how much it can diagnose.&lt;/p&gt;
&lt;h4 id=&quot;analyze-in-real-time-with-the-web-vitals-extension&quot;&gt;Analyze in real time with the Web Vitals extension &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#analyze-in-real-time-with-the-web-vitals-extension&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The Web Vitals Chrome extension shows Core Web Vitals in real time during page load &lt;em&gt;and&lt;/em&gt; while browsing a page. Because of this, it can capture FID and INP as well as layout shifts that occur after load. The debugging options show more detailed information on each metric:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Web Vitals Extension console logging showing INP target, event type, and breakdown&quot; decoding=&quot;async&quot; height=&quot;448&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/kxh8TRS2585OJTZJmWmt.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;It&#39;s best to think of the Web Vitals extension as a spot-checking tool to find performance issues, not a comprehensive debugging tool—that&#39;s a job for the Performance panel in Chrome DevTools.&lt;/p&gt;
&lt;h4 id=&quot;drill-down-with-the-performance-panel&quot;&gt;Drill down with the Performance panel &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#drill-down-with-the-performance-panel&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The Performance panel in Chrome DevTools profiles all page behavior during a recorded period of time.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;ALT_TEXT_HERE&quot; decoding=&quot;async&quot; height=&quot;466&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/JmVcoLckgeukluuJONDe.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/JmVcoLckgeukluuJONDe.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/JmVcoLckgeukluuJONDe.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/JmVcoLckgeukluuJONDe.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/JmVcoLckgeukluuJONDe.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/JmVcoLckgeukluuJONDe.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/JmVcoLckgeukluuJONDe.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/JmVcoLckgeukluuJONDe.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/JmVcoLckgeukluuJONDe.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/JmVcoLckgeukluuJONDe.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/JmVcoLckgeukluuJONDe.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/JmVcoLckgeukluuJONDe.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/JmVcoLckgeukluuJONDe.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/JmVcoLckgeukluuJONDe.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/JmVcoLckgeukluuJONDe.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/JmVcoLckgeukluuJONDe.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/JmVcoLckgeukluuJONDe.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/JmVcoLckgeukluuJONDe.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Key timings—such as LCP, for example—are shown in the Timings track. Click on these for more details.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;Layout Shifts&lt;/strong&gt; track highlights layout shifts and clicking on these provides more details about the elements that shifted for debugging CLS.&lt;/p&gt;
&lt;p&gt;Long Tasks (that can lead to FID and INP issues) are also highlighted with red triangles.&lt;/p&gt;
&lt;p&gt;These features—as well as information in other parts of the &lt;a href=&quot;https://developer.chrome.com/docs/devtools/#performance&quot; rel=&quot;noopener&quot;&gt;Performance panel&lt;/a&gt;—can help you determine whether fixes are having any effect on a page&#39;s Core Web Vitals.&lt;/p&gt;
&lt;h4 id=&quot;debug-core-web-vitals-in-the-field&quot;&gt;Debug Core Web Vitals in the field &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#debug-core-web-vitals-in-the-field&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Lab tools can&#39;t always identify the cause of all Core Web Vitals issues affecting your users. This is one reason why it&#39;s so important to collect your own field data, as it takes factors into account that lab data cannot.&lt;/p&gt;
&lt;p&gt;See &lt;a href=&quot;https://web.dev/debug-performance-in-the-field/&quot;&gt;debug performance in the field&lt;/a&gt; for more information.&lt;/p&gt;
&lt;h3 id=&quot;step-3-monitor-for-changes&quot;&gt;Step 3: Monitor for changes &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#step-3-monitor-for-changes&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A collection of icons for Google tools. From left to right, the icons represent &amp;#x27;CrUX on BigQuery&amp;#x27;, &amp;#x27;CrUX API&amp;#x27;, &amp;#x27;PSI API&amp;#x27;, &amp;#x27;web-vitals.js&amp;#x27;, with &amp;#x27;Lighthouse CI&amp;#x27; at the far right.&quot; decoding=&quot;async&quot; height=&quot;185&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/LqEnonUKd8j3QGK3Ax7w.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/LqEnonUKd8j3QGK3Ax7w.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/LqEnonUKd8j3QGK3Ax7w.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/LqEnonUKd8j3QGK3Ax7w.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/LqEnonUKd8j3QGK3Ax7w.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/LqEnonUKd8j3QGK3Ax7w.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/LqEnonUKd8j3QGK3Ax7w.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/LqEnonUKd8j3QGK3Ax7w.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/LqEnonUKd8j3QGK3Ax7w.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/LqEnonUKd8j3QGK3Ax7w.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/LqEnonUKd8j3QGK3Ax7w.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/LqEnonUKd8j3QGK3Ax7w.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/LqEnonUKd8j3QGK3Ax7w.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/LqEnonUKd8j3QGK3Ax7w.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/LqEnonUKd8j3QGK3Ax7w.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/LqEnonUKd8j3QGK3Ax7w.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/LqEnonUKd8j3QGK3Ax7w.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/LqEnonUKd8j3QGK3Ax7w.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Once you have fixed any issues, you want to ensure they have the desired effect and that new issues do not disrupt your Core Web Vitals. This requires monitoring for performance issues as part of developer workflow to prevent performance issues being released to production, and monitoring the field data regularly to ensure this is the case.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; &lt;a href=&quot;https://youtu.be/YJGCZCaIZkQ?t=112&quot;&gt;Research by Google&lt;/a&gt; has shown that most performance improvements tend to regress within six months. A website needs continuous monitoring in both the lab &lt;em&gt;and&lt;/em&gt; the field to identify worsening trends in Core Web Vitals and other performance metrics to avoid regressions. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;monitor-for-performance-requisitions-in-continuous-integration-ci-environments&quot;&gt;Monitor for performance requisitions in Continuous Integration (CI) environments &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#monitor-for-performance-requisitions-in-continuous-integration-ci-environments&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/GoogleChrome/lighthouse-ci&quot; rel=&quot;noopener&quot;&gt;Lighthouse-CI&lt;/a&gt; allows you to automatically run Lighthouse audits on code commits to prevent performance regressions entering code. This can check performance timings (which &lt;a href=&quot;https://developers.google.com/web/tools/lighthouse/variability&quot; rel=&quot;noopener&quot;&gt;are subject to variability&lt;/a&gt;), or for just &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/&quot; rel=&quot;noopener&quot;&gt;the performance audits&lt;/a&gt;, as a linting tool to prevent bad practices in the code.&lt;/p&gt;
&lt;h4 id=&quot;view-website-health-trends-with-field-data&quot;&gt;View website health trends with field data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#view-website-health-trends-with-field-data&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;While you should aim to catch and fix all performance issues before they make it to production, monitoring your field data via RUM is essential to find any that slip through. There are many commercial RUM products available that can help with this. The &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;web-vitals&lt;/code&gt;&lt;/a&gt; JavaScript library can automate a website&#39;s field data collection, and optionally use this data to power custom dashboards and alerting systems.&lt;/p&gt;
&lt;p&gt;For sites without a RUM solution, you can &lt;a href=&quot;https://developer.chrome.com/docs/crux/dashboard/&quot; rel=&quot;noopener&quot;&gt;use the CrUX Dashboard&lt;/a&gt; as a basic trend analysis of field data. It reports the following for sites in CrUX:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Site overview&lt;/strong&gt;, which segments Core Web Vitals into desktop and mobile device types.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Historical trend by metric type&lt;/strong&gt;, which is a distribution of metrics over time for each available monthly release of CrUX report data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User demographics&lt;/strong&gt;, which illustrates the distribution of page views across an entire origin for users in each demographic including device and &lt;a href=&quot;https://developer.mozilla.org/docs/Glossary/Effective_connection_type&quot; rel=&quot;noopener&quot;&gt;effective connection&lt;/a&gt; types.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of the CrUX dashboard. The dashboard breaks down LCP, FID, and CLS into desktop and mobile categories, with each category showing the distribution of values that lie within &amp;#x27;Good&amp;#x27;, &amp;#x27;Needs Improvement&amp;#x27; and &amp;#x27;Poor&amp;#x27; thresholds for the previous month.&quot; decoding=&quot;async&quot; height=&quot;837&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oF2PQELFtdICPN10aZge.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oF2PQELFtdICPN10aZge.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oF2PQELFtdICPN10aZge.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oF2PQELFtdICPN10aZge.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oF2PQELFtdICPN10aZge.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oF2PQELFtdICPN10aZge.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oF2PQELFtdICPN10aZge.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oF2PQELFtdICPN10aZge.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oF2PQELFtdICPN10aZge.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oF2PQELFtdICPN10aZge.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oF2PQELFtdICPN10aZge.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oF2PQELFtdICPN10aZge.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oF2PQELFtdICPN10aZge.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oF2PQELFtdICPN10aZge.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oF2PQELFtdICPN10aZge.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oF2PQELFtdICPN10aZge.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oF2PQELFtdICPN10aZge.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oF2PQELFtdICPN10aZge.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;The CrUX Dashboard is based on the &lt;a href=&quot;https://developer.chrome.com/blog/chrome-ux-report-bigquery/&quot; rel=&quot;noopener&quot;&gt;CrUX BigQuery dataset&lt;/a&gt;, which is updated once a month. This can be a good reminder to regularly check in on your Core Web Vitals.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Important&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Even if your website is represented in CrUX, you should still collect your own field data, since CrUX doesn&#39;t include &lt;em&gt;all&lt;/em&gt; users of Chrome or even other browsers—but it&#39;s certainly a good place to start in the absence of any field data. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-tools/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Ensuring fast and delightful user experiences requires a performance-first mindset and adoption of a workflow to ensure progress. With the right tools and processes to audit, debug, and monitor, building great user experiences and staying within the thresholds defined for good Core Web Vitals is within your reach.&lt;/p&gt;
</content>
    <author>
      <name>Barry Pollard</name>
    </author><author>
      <name>Antoine Bisch</name>
    </author><author>
      <name>Garima Mimani</name>
    </author><author>
      <name>Addy Osmani</name>
    </author><author>
      <name>Elizabeth Sweeny</name>
    </author>
  </entry>
  
  <entry>
    <title>Best practices for tags and tag managers</title>
    <link href="https://web.dev/tag-best-practices/"/>
    <updated>2021-07-29T00:00:00Z</updated>
    <id>https://web.dev/tag-best-practices/</id>
    <content type="html" mode="escaped">&lt;p&gt;&lt;a href=&quot;https://support.google.com/tagmanager/answer/3281060?hl=en&quot; rel=&quot;noopener&quot;&gt;Tags&lt;/a&gt; are snippets
of third-party code that are inserted into a site, typically via a tag manager.
Tags are most commonly used for marketing and analytics.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Although tags and third-party scripts are often fundamentally the exact same thing, the separate terminology reflects the different ways that these scripts are used: &amp;quot;tags&amp;quot; are typically owned by marketing departments and added via tag managers; whereas &amp;quot;third-party scripts&amp;quot; more commonly refer to resources added through code changes made by engineering departments. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The performance impact of tags and tag managers varies wildly from site to site.
Tag managers can be compared to an envelope: the tag manager provides a
vessel—but what you fill it with and how you use it is mostly up to you.&lt;/p&gt;
&lt;p&gt;This article discusses techniques for optimizing tags and tag managers for
performance and Web Vitals. Although this article references Google Tag Manager,
many of the ideas discussed are also applicable to other tag managers.&lt;/p&gt;
&lt;h2 id=&quot;impact-on-core-web-vitals&quot;&gt;Impact on Core Web Vitals &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#impact-on-core-web-vitals&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Tag Managers can often impact your Core Web Vitals indirectly by using up resources needed to load your page quickly and keep it responsive. Bandwidth can be spent downloading the tag manager JavaScript for your sites, or the subsequent calls this makes. CPU time on the main thread can be spent evaluating and executing JavaScript contained within the tag manager and the tags.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint&lt;/a&gt; (LCP) is vulnerable to bandwidth contention during the critical page load time. Additionally, blocking the main thread can &lt;a href=&quot;https://web.dev/optimize-lcp/#2-eliminate-element-render-delay&quot;&gt;delay the LCP render time&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/cls/&quot;&gt;Cumulative Layout Shift&lt;/a&gt; (CLS) can be impacted, either by delaying loading critical resources before the first render, or by tag managers injecting content into the page.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/fid/&quot;&gt;First Input Delay&lt;/a&gt; (FID) is susceptible to CPU contention on the main thread. This can also impact the newer &lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint&lt;/a&gt; (INP) metric and we have seen a correlation between the size of tag managers, and poorer INP scores.&lt;/p&gt;
&lt;h2 id=&quot;tag-types&quot;&gt;Tag types &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#tag-types&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The impact of tags on performance varies by tag type. Generally speaking, image
tags (&amp;quot;pixels&amp;quot;) are the most performant, followed by custom templates, and
lastly, custom HTML tags. Vendor tags vary depending on the functionality they
allow.&lt;/p&gt;
&lt;p&gt;However, keep in mind that how you use a tag greatly influences its performance
impact. &amp;quot;Pixels&amp;quot; are highly performant largely because the nature of this tag
type imposes tight restrictions on how they can be used; custom HTML tags aren&#39;t
necessarily always bad for performance, but due to the level of freedom they
offer users, they can be easy to misuse in a way that is bad for performance.&lt;/p&gt;
&lt;p&gt;When thinking about tags, keep scale in mind: the performance impact of any
single tag may be negligible—but can become significant when tens or hundreds of
tags are used on the same page.&lt;/p&gt;
&lt;h3 id=&quot;not-all-scripts-should-be-loaded-using-a-tag-manager&quot;&gt;Not all scripts should be loaded using a tag manager &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#not-all-scripts-should-be-loaded-using-a-tag-manager&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tag managers are typically not a good mechanism for loading resources that
implement immediate visual or functional aspects of the user experience—for
example, cookie notices, hero images, or site features. Using a tag manager to
load these resources typically delays their delivery. This is bad for the user
experience and can also increase metrics like LCP and CLS.  In addition, keep in
mind that some users block tag managers. Using a tag manager to implement UX
features may result in a broken website for some of your users.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Resources requested via tag manager will typically load later than resources requested directly. This is not always a bad thing. If a tag manager is primarily used to deliver non-essential resources, this delay can be used constructively by providing a mechanism for delaying non-essential requests. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;be-careful-with-custom-html-tags&quot;&gt;Be careful with Custom HTML tags &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#be-careful-with-custom-html-tags&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://support.google.com/tagmanager/answer/6107167/custom-tags?hl=en#CustomHTML&quot; rel=&quot;noopener&quot;&gt;Custom HTML
tags&lt;/a&gt;
have been around for many years and are heavily used on most sites. Custom HTML
tags allow you to enter your own code with few restrictions as, despite the name,
the main use of this tag is to add custom &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; elements to a page.&lt;/p&gt;
&lt;p&gt;Custom HTML tags can be used in a wide variety of ways and their performance impact
varies significantly. When measuring the performance of your site, be aware that
most tools will attribute the performance impact of a Custom HTML tag to the tag
manager that injected it—rather than the tag itself.&lt;/p&gt;
&lt;img alt=&quot;Screenshot of creating a custom tag in Google Tag Manager&quot; class=&quot;screenshot&quot; decoding=&quot;async&quot; height=&quot;470&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Custom HTML tags can insert an element into the surrounding page. The act
of inserting elements into the page can be a source of performance issues, and
in some cases, also cause layout shifts.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In most situations, if an element is inserted into the page, the browser
must recalculate the size and position of each item on the page—this process
is known as
&lt;a href=&quot;https://developer.chrome.com/blog/inside-browser-part3/#layout&quot; rel=&quot;noopener&quot;&gt;layout&lt;/a&gt;.
The performance impact of a single layout is minimal, but when it occurs
excessively it can become a source of performance issues. The impact of this
phenomenon is larger on lower-end devices and pages with a high number of
DOM elements.&lt;/li&gt;
&lt;li&gt;If a visible page element is inserted into the DOM after the surrounding
area has already been rendered, it can cause a layout shift. This phenomenon
is not unique to tag managers—however, because tags typically load later
than other parts of the page, it&#39;s common for them to be inserted into the
DOM after the surrounding page has already been rendered.&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; If you&#39;re adding or changing visible content on a page, keep in mind that search engines will need to be able to fetch and process the JavaScript used. For Google, you can &lt;a href=&quot;https://developers.google.com/search/docs/crawling-indexing/javascript/dynamic-rendering#verify&quot;&gt;verify rendering of a page&lt;/a&gt; for Search; other search engines may have similar testing tools available. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;consider-using-custom-templates&quot;&gt;Consider using Custom Templates &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#consider-using-custom-templates&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developers.google.com/tag-platform/tag-manager/templates&quot; rel=&quot;noopener&quot;&gt;Custom templates&lt;/a&gt; support
some of the same operations as Custom HTML tags but are built upon a sandboxed
version of JavaScript that provides
&lt;a href=&quot;https://developers.google.com/tag-manager/templates/api&quot; rel=&quot;noopener&quot;&gt;APIs&lt;/a&gt; for common use
cases like script injection and pixel injection. As the name implies, they allow
a template to be created, by a power user who can build this with performance in
mind. Less technical users can then use the template. This is often safer
than providing full Custom HTML access.&lt;/p&gt;
&lt;p&gt;Due to the greater restrictions imposed on custom templates, these tags are much
less likely to exhibit performance or security issues; however, for these same
reasons, custom templates will not work for all use cases.&lt;/p&gt;
&lt;img alt=&quot;Screenshot of using a custom template in Google Tag Manager&quot; class=&quot;screenshot&quot; decoding=&quot;async&quot; height=&quot;388&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h3 id=&quot;inject-scripts-correctly&quot;&gt;Inject scripts correctly &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#inject-scripts-correctly&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Using a tag manager to inject a script is a very common use case. The
recommended way to do this is to use a Custom Template and the
&lt;a href=&quot;https://developers.google.com/tag-manager/templates/api#injectscript&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;injectScript&lt;/code&gt;&lt;/a&gt;
API.&lt;/p&gt;
&lt;p&gt;For information on using the injectScript API to convert an existing Custom HTML
tag, see &lt;a href=&quot;https://developers.google.com/tag-manager/templates/convert-existing-tag&quot; rel=&quot;noopener&quot;&gt;Convert an existing
tag&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you must use a Custom HTML tag, here are some things to keep in mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Libraries and large third-party scripts should be loaded via a script tag
that downloads an external file (for example, &lt;code&gt;&amp;lt;script src=&amp;quot;external-scripts.js&amp;quot;&amp;gt;&lt;/code&gt;), rather than directly copy-pasting the script&#39;s
contents into the tag. Although forgoing use of the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag
eliminates a separate round-trip to download the script&#39;s contents, this
practice increases container size and prevents the script from being cached
separately by the browser.&lt;/li&gt;
&lt;li&gt;Many vendors recommend placing their &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag at the top of the
&lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;. However, for scripts loaded via tag manager, this recommendation
is usually unnecessary: in most situations, the browser has already finished
parsing the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; by the time that the tag manager executes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;use-pixels&quot;&gt;Use pixels &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#use-pixels&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In some situations third-party scripts can be replaced with image or iframe
&amp;quot;pixels&amp;quot;. Compared to their script-based counterparts, pixels may support
less functionality, so are often seen as a less-preferred implementation because
of that. However, when used inside tag managers, pixels can be more dynamic
as they can fire on triggers and pass different variables. They are the most
performant and secure type of tag because there is no JavaScript execution after
it is fired. Pixels have a very small resource size (less than 1 KB) and do
not cause layout shifts.&lt;/p&gt;
&lt;p&gt;Check with your third-party provider for more information on their support for
pixels. Additionally, you can try inspecting their code for a &lt;code&gt;&amp;lt;noscript&amp;gt;&lt;/code&gt; tag.
If a vendor supports pixels, they will often include them within the
&lt;code&gt;&amp;lt;noscript&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;
&lt;img alt=&quot;Screenshot of custom image tag in Google Tag Manager&quot; decoding=&quot;async&quot; height=&quot;342&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h4 id=&quot;alternatives-to-pixels&quot;&gt;Alternatives to pixels &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#alternatives-to-pixels&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Pixels became popular largely because at one time they were one of the cheapest
and most reliable ways to make a HTTP request in situations where the server
response is not relevant ( for example, when sending data to analytics
providers). The
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Navigator/sendBeacon&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;navigator.sendBeacon()&lt;/code&gt;&lt;/a&gt;
and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/WindowOrWorkerGlobalScope/fetch#parameters&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;fetch() keepalive&lt;/code&gt;&lt;/a&gt;
APIs are designed to address this same use case but are arguably more reliable
than pixels.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The &lt;code&gt;sendBeacon()&lt;/code&gt; and &lt;code&gt;fetch() keepalive&lt;/code&gt; APIs will both still work in situations where the browser is &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Window/unload_event&quot;&gt;unloading&lt;/a&gt; the page: for example, both of these APIs can be used to track outbound link clicks. By contrast, techniques like pixels and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/XMLHttpRequest&quot;&gt;&lt;code&gt;XMLHttpRequest&lt;/code&gt;&lt;/a&gt; would likely fail in those cases. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;There is nothing wrong with continuing to use pixels—they are well supported and
have minimal performance impact. However, if you are building your own beacons,
it is worth considering using one of these APIs.&lt;/p&gt;
&lt;h5 id=&quot;sendbeacon&quot;&gt;&lt;code&gt;sendBeacon()&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#sendbeacon&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;The
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Navigator/sendBeacon&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;navigator.sendBeacon()&lt;/code&gt;&lt;/a&gt;
API is designed for sending small amounts of data to web servers in situations
where the server response does not matter.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://example.com/analytics&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;checkout&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; performance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendBeacon&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;sendBeacon()&lt;/code&gt; has a limited API: it only supports making POST requests and does
not support setting custom headers. It is
&lt;a href=&quot;https://caniuse.com/beacon&quot; rel=&quot;noopener&quot;&gt;supported&lt;/a&gt; by all modern browsers.&lt;/p&gt;
&lt;h5 id=&quot;fetch-keepalive&quot;&gt;&lt;code&gt;fetch() keepalive&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#fetch-keepalive&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/WindowOrWorkerGlobalScope/fetch#parameters&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;keepalive&lt;/code&gt;&lt;/a&gt;
is a flag that allows the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Fetch_API/Using_Fetch&quot; rel=&quot;noopener&quot;&gt;Fetch
API&lt;/a&gt; to
be used to make non-blocking requests like event reporting and analytics. It is
used by including &lt;code&gt;keepalive: true&lt;/code&gt; in the parameters passed to &lt;code&gt;fetch()&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://example.com/analytics&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;checkout&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; performance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;POST&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;keepalive&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If &lt;code&gt;fetch() keepalive&lt;/code&gt; and &lt;code&gt;sendBeacon()&lt;/code&gt; seem very similar, it&#39;s because they
are. In fact, in Chromium browsers, &lt;code&gt;sendBeacon()&lt;/code&gt; is now built upon &lt;code&gt;fetch() keepalive&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When choosing between &lt;code&gt;fetch() keepalive&lt;/code&gt; and &lt;code&gt;sendBeacon()&lt;/code&gt;, it&#39;s important to
consider the features and browser support that you need. The fetch() API is
significantly more flexible; however, &lt;code&gt;keepalive&lt;/code&gt; has less browser
&lt;a href=&quot;https://caniuse.com/?search=keepalive&quot; rel=&quot;noopener&quot;&gt;support&lt;/a&gt; than &lt;code&gt;sendBeacon()&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;get-clarification&quot;&gt;Get clarification &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#get-clarification&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tags are often created by following guidance provided by a third-party vendor.
If it is unclear what a vendor&#39;s code does—consider asking someone who knows.
Getting a second opinion can help identify if a tag has the potential to create
performance or security issues.&lt;/p&gt;
&lt;p&gt;Labeling tags with an owner in the tag manager is also recommended. It is far
too easy to forget who owns a tag, and be afraid to remove it just in case!&lt;/p&gt;
&lt;h2 id=&quot;triggers&quot;&gt;Triggers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#triggers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At a high-level, optimizing &lt;a href=&quot;https://support.google.com/tagmanager/topic/7679108?hl=en&amp;amp;ref_topic=7679384&quot; rel=&quot;noopener&quot;&gt;tag
triggers&lt;/a&gt;
generally consists of making sure to not trigger tags more than necessary and
choosing a trigger that balances business needs with performance costs.&lt;/p&gt;
&lt;p&gt;Triggers themselves are JavaScript code that will increase the size—and execution
cost—of the tag manager. While most triggers are small, the cumulative effect can
add up. Having many click events for example, or timer triggers can dramatically
increase the workload of the tag manager.&lt;/p&gt;
&lt;h3 id=&quot;choose-an-appropriate-trigger-event&quot;&gt;Choose an appropriate trigger event &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#choose-an-appropriate-trigger-event&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The performance impact of a tag is not fixed: generally speaking, the earlier
that a tag fires, the greater its impact on performance. Resources are typically
constrained during the initial page load and therefore loading or executing a
particular resource (or tag) takes resources away from something else.&lt;/p&gt;
&lt;p&gt;Although it is important to choose appropriate triggers for all tags, it is
particularly important for tags that load large resources or execute long
scripts.&lt;/p&gt;
&lt;p&gt;Tags can be triggered on
&lt;a href=&quot;https://support.google.com/tagmanager/answer/7679319?hl=en&quot; rel=&quot;noopener&quot;&gt;Page Views&lt;/a&gt;
(typically &lt;code&gt;Page load&lt;/code&gt;, on &lt;code&gt;DOM Ready&lt;/code&gt;, on &lt;code&gt;Window Loaded&lt;/code&gt;), or based on a
custom event. To avoid impacting page load, it is recommended to fire
non-essential tags after &lt;code&gt;Window Loaded&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;use-custom-events&quot;&gt;Use custom events &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#use-custom-events&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://support.google.com/tagmanager/answer/7679219?hl=en&quot; rel=&quot;noopener&quot;&gt;Custom events&lt;/a&gt;
allow you to fire triggers in response to page events that aren&#39;t covered by
Google Tag Manager&#39;s built-in triggers. For example, many tags use &lt;a href=&quot;https://support.google.com/tagmanager/answer/7679319?hl=en&quot; rel=&quot;noopener&quot;&gt;page view
triggers&lt;/a&gt;; however,
the time period between &lt;code&gt;DOM Ready&lt;/code&gt; and &lt;code&gt;Window Loaded&lt;/code&gt; can be lengthy on many
pages and this can make it difficult to fine-tune when a tag fires. Custom
events provide a solution to this problem.&lt;/p&gt;
&lt;p&gt;To use custom events, first create a custom event trigger and update your tags
to use this trigger.&lt;/p&gt;
&lt;img alt=&quot;Screenshot of a Custom Event trigger in Google Tag Manager&quot; decoding=&quot;async&quot; height=&quot;348&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;To fire the trigger, push the corresponding event to the data layer.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Custom event trigger that fires after 2 seconds&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  dataLayer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token string-property property&quot;&gt;&#39;event&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;my-custom-event&#39;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;use-specific-trigger-conditions&quot;&gt;Use specific trigger conditions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#use-specific-trigger-conditions&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Using specific trigger conditions helps avoid firing a tag unnecessarily.
Although there are many ways to apply this concept, one of the simplest yet most
useful things you can do is ensure that a tag only fires on pages where it is
actually used.&lt;/p&gt;
&lt;img alt=&quot;Screenshot showing trigger conditions in Google Tag Manager&quot; decoding=&quot;async&quot; height=&quot;454&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;&lt;a href=&quot;https://support.google.com/tagmanager/answer/7182738&quot; rel=&quot;noopener&quot;&gt;Built-in variables&lt;/a&gt; can
also be incorporated into trigger conditions to limit tag firing.&lt;/p&gt;
&lt;p&gt;However, be aware that having complex trigger conditions or exceptions takes
processing time in and off itself, so do not make them too complex.&lt;/p&gt;
&lt;h3 id=&quot;load-your-tag-manager-at-an-appropriate-time&quot;&gt;Load your tag manager at an appropriate time &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#load-your-tag-manager-at-an-appropriate-time&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Adjusting when you tag manager itself loads can have a significant impact on
performance. Triggers, regardless of how they are configured, can&#39;t fire until
after a tag manager loads. Although it is important to choose good triggers for
individual tags (as explained above), experimenting with when you load your tag
manager can often have an equal or greater impact given that this single
decision will impact all tags on a page.&lt;/p&gt;
&lt;p&gt;Loading the tag manager later also adds a layer of control and can avoid future
performance issues as it will prevent a tag manager user inadvertently loading
a tag too early, without realizing the impact this can have.&lt;/p&gt;
&lt;h2 id=&quot;variables&quot;&gt;Variables &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#variables&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Variables allow data to be read from the page. They are useful in triggers, and
in tags themselves.&lt;/p&gt;
&lt;p&gt;Like triggers, variables result in JavaScript code being added to the tag manager,
and so can cause performance issues. Variables can be relatively simple built-in
types that can, for example, read parts of the URL, cookies, data layer, or DOM.
Or they can be custom JavaScript that is basically unlimited in what it can do.&lt;/p&gt;
&lt;p&gt;Keep variables simple and to a minimum, since they will need to be evaluated
continually by the tag manager. Remove old variables that are no longer used
to reduce both the size of the tag manager script, and the processing time it
uses.&lt;/p&gt;
&lt;h2 id=&quot;tag-management&quot;&gt;Tag management &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#tag-management&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Using the tags efficiently will reduce the risk of performance issues.&lt;/p&gt;
&lt;h3 id=&quot;use-the-data-layer&quot;&gt;Use the data layer &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#use-the-data-layer&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://developers.google.com/tag-manager/devguide&quot; rel=&quot;noopener&quot;&gt;data layer&lt;/a&gt; &amp;quot;contains
all of the information that you want to pass to Google Tag Manager&amp;quot;. More
concretely, it is a JavaScript array of objects that contain information about
the page. It can also be used to trigger tags.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Contents of the data layer&lt;/span&gt;&lt;br /&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataLayer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token string-property property&quot;&gt;&#39;pageCategory&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;signup&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token string-property property&quot;&gt;&#39;visitorType&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;high-value&#39;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Pushing a variable to the data layer&lt;/span&gt;&lt;br /&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataLayer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string-property property&quot;&gt;&#39;variable_name&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;variable_value&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Pushing an event to the data layer&lt;/span&gt;&lt;br /&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataLayer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string-property property&quot;&gt;&#39;event&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;event_name&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Although Google Tag Manager can be used without the data layer, its use is
strongly recommended. The data layer provides a way to consolidate the data
being accessed by third-party scripts into a single place thereby providing
better visibility into its usage. Amongst other things, this can help reduce
redundant variable calculations and script execution. Using a data layer also
controls the data being accessed by the tags, rather than giving full JavaScript
variable or DOM access.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The performance benefits of the data layer might seem non-intuitive given that updating the data layer causes Google Tag Manager to reevaluate all container variables and potentially trigger tags—all of which entails JavaScript execution. Although it is possible to misuse the data layer, generally speaking, if working with the data layer is a source of performance issues, it probably indicates that the container itself has performance issues—the data layer is merely making these issues more apparent. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;remove-duplicate-and-unused-tags&quot;&gt;Remove duplicate and unused tags &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#remove-duplicate-and-unused-tags&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Duplicate tags can occur when a tag is included in a page&#39;s HTML markup in
addition to being injected through a tag manager.&lt;/p&gt;
&lt;p&gt;Unused tags should be paused or removed rather than blocked through use of a
&lt;a href=&quot;https://support.google.com/tagmanager/answer/7679318?hl=en&quot; rel=&quot;noopener&quot;&gt;trigger exception&lt;/a&gt;.
Pausing or removing a tag removes the code from the container; blocking does
not.&lt;/p&gt;
&lt;p&gt;When unused tags are removed, the triggers and variables should also be
reviewed to see if any of them can be removed if they were only used by those
tags.&lt;/p&gt;
&lt;h3 id=&quot;use-allow-and-deny-lists&quot;&gt;Use allow and deny lists &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#use-allow-and-deny-lists&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developers.google.com/tag-manager/web/restrict&quot; rel=&quot;noopener&quot;&gt;Allow and deny lists&lt;/a&gt;
allow you to configure highly granular restrictions on the tags, triggers, and
variables allowed on a page. This can be used to help enforce performance best
practices and other policies.&lt;/p&gt;
&lt;p&gt;Allow and deny lists are configured through the data layer.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataLayer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token string-property property&quot;&gt;&#39;gtm.allowlist&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;id&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;id&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token string-property property&quot;&gt;&#39;gtm.blocklist&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;customScripts&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;For example, it is possible to not allow any Custom HTML tags, JavaScript
variables, or direct DOM access. This means only pixels and pre-defined tags
can be used, with data from the data layer. While this is certainly restrictive,
it can result in a much more performant, and secure, tag manager implementation.&lt;/p&gt;
&lt;h3 id=&quot;consider-using-server-side-tagging&quot;&gt;Consider using server-side tagging &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#consider-using-server-side-tagging&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Switching to server-side tagging is not a trivial task, but it is worth
considering - particularly for larger sites that want more control over their
data. Server-side tagging removes vendor code from the client, and with it,
offloads processing from the client to the server.&lt;/p&gt;
&lt;p&gt;For example, when using client-side tagging, sending data to multiple analytics
accounts entails that the client initiates separate requests for each endpoint.
By contrast, with server-side tagging, a single request is made by the client to
the server-side container, and from there, this data is forwarded to different
analytics accounts.&lt;/p&gt;
&lt;p&gt;Keep in mind that server-side tagging only works with some tags; tag
compatibility varies depending on vendor.&lt;/p&gt;
&lt;p&gt;For more information, see &lt;a href=&quot;https://developers.google.com/tag-manager/serverside/intro&quot; rel=&quot;noopener&quot;&gt;An introduction to server-side
tagging&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;containers&quot;&gt;Containers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#containers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Tag managers typically allow multiple instances or &amp;quot;containers&amp;quot; within their
set up. This allows multiple containers to be controlled within the one tag
manager account.&lt;/p&gt;
&lt;h3 id=&quot;use-only-one-container-per-page&quot;&gt;Use only one container per page &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#use-only-one-container-per-page&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Using multiple
&lt;a href=&quot;https://developers.google.com/tag-manager/api/v1/reference/accounts/containers&quot; rel=&quot;noopener&quot;&gt;containers&lt;/a&gt;
on a single page can create significant performance issues as it introduces
additional overhead and script execution. At the very least it duplicates the
core tag code itself which, as it is delivered as part of the container&#39;s
JavaScript, cannot be reused between the containers.&lt;/p&gt;
&lt;p&gt;It is rare for multiple containers to be used effectively. However, there can be
instances when this can work—if controlled well—including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Having a lighter &amp;quot;early load&amp;quot; container, and a heavier &amp;quot;later load&amp;quot; container,
rather than one large container.&lt;/li&gt;
&lt;li&gt;Having a restricted container used by less technical users, with a less
restricted, but more tightly controlled, container for tags that cannot be used
in the restricted container.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you must use multiple containers per page, &lt;a href=&quot;https://developers.google.com/tag-manager/devguide#multiple-containers&quot; rel=&quot;noopener&quot;&gt;follow Google Tag manager
guidance for setting up multiple
containers&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;use-separate-containers-if-needed&quot;&gt;Use separate containers if needed &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#use-separate-containers-if-needed&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you use a tag manager for multiple properties (for example, a web app and a
mobile app)—the number of containers you use can help or hurt your workflow
productivity. It can also impact performance.&lt;/p&gt;
&lt;p&gt;Generally speaking, a single container can effectively be used across multiple
sites if the sites are similar in use and structure. For example, although a
brand&#39;s mobile and web apps might serve similar functions, it&#39;s likely that the
apps will be structured differently, and therefore more effectively managed
through separate containers.&lt;/p&gt;
&lt;p&gt;Trying to reuse a single container too broadly typically unnecessarily increases
the complexity and size of the container by forcing the adoption of complex logic
to manage tags and triggers.&lt;/p&gt;
&lt;h3 id=&quot;keep-an-eye-on-container-size&quot;&gt;Keep an eye on container size &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#keep-an-eye-on-container-size&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The size of a container is determined by its tags, triggers, and variables.
Although a small container may still negatively impact page performance, a large
container almost certainly will.&lt;/p&gt;
&lt;p&gt;Container size should not be your north-star metric when optimizing your tag
usage; however, a large container size is often a warning sign that a container
is not well maintained and possibly misused.&lt;/p&gt;
&lt;p&gt;Google Tag Manager
&lt;a href=&quot;https://support.google.com/tagmanager/answer/2772488?hl=en&quot; rel=&quot;noopener&quot;&gt;limits&lt;/a&gt; container
size to 200 KB and will warn about container size starting at 140 KB. However,
most sites should aim to keep their containers far smaller than this. For
perspective, the median site container is around 50 KB.&lt;/p&gt;
&lt;p&gt;To determine the size of your container, look at the size of the response
returned by &lt;code&gt;https://www.googletagmanager.com/gtag/js?id=YOUR_ID&lt;/code&gt;. This
response contains the Google Tag Manager library plus the contents of the
container. By itself, the Google Tag Manager library is around 33 KB
compressed.&lt;/p&gt;
&lt;h3 id=&quot;name-your-container-versions&quot;&gt;Name your container versions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#name-your-container-versions&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A &lt;a href=&quot;https://developers.google.com/tag-manager/api/v1/reference/accounts/containers/versions&quot; rel=&quot;noopener&quot;&gt;container
version&lt;/a&gt;
is a snapshot of a container&#39;s content at a particular point in time. Using a
meaningful name and along with including a short description of meaningful
changes within can go a long way in making it easier to debug future performance
issues.&lt;/p&gt;
&lt;h2 id=&quot;tagging-workflows&quot;&gt;Tagging workflows &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#tagging-workflows&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Managing changes to your tags is important to ensure they do not have a
negative impact on page performance.&lt;/p&gt;
&lt;h3 id=&quot;test-tags-before-deploying&quot;&gt;Test tags before deploying &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#test-tags-before-deploying&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Testing your tags before deployment can help catch issues (performance and
otherwise) before they ship.&lt;/p&gt;
&lt;p&gt;Things to consider when testing a tag include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is the tag working correctly?&lt;/li&gt;
&lt;li&gt;Does the tag cause any layout shifts?&lt;/li&gt;
&lt;li&gt;Does the tag load any resources? How large are these resources?&lt;/li&gt;
&lt;li&gt;Does the tag trigger a long-running script?&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;preview-mode&quot;&gt;Preview mode &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#preview-mode&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://support.google.com/tagmanager/answer/6107056&quot; rel=&quot;noopener&quot;&gt;Preview mode&lt;/a&gt; allows you
to test tag changes on your actual site without having to deploy them to the
public first. Preview mode includes a debugging console that provides
information about tags.&lt;/p&gt;
&lt;p&gt;The execution time of Google Tag Manager will be different (slightly slower)
when run in Preview mode due to the additional overhead required to expose
information in the debugging console. Thus, comparing Web Vitals measurements
collected in preview mode to those collected in production is not recommended.
However, this discrepancy should not affect the execution behavior of the tags
themselves.&lt;/p&gt;
&lt;h4 id=&quot;standalone-testing&quot;&gt;Standalone testing &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#standalone-testing&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;An alternative approach to testing tags is to set up an empty page containing a
container with a single tag—the tag you are testing. This testing setup is less
realistic and won&#39;t catch some issues (for example, whether a tag causes layout
shifts)—however it can make it easier to isolate and measure the impact of the
tag on things like script execution. Check out how &lt;a href=&quot;https://medium.com/the-telegraph-engineering/improving-third-party-web-performance-at-the-telegraph-a0a1000be5&quot; rel=&quot;noopener&quot;&gt;Telegraph uses this
isolation approach to improve
performance&lt;/a&gt;
of third-party code.&lt;/p&gt;
&lt;h3 id=&quot;monitor-tag-performance&quot;&gt;Monitor tag performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#monitor-tag-performance&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Google Tag Manager &lt;a href=&quot;https://developers.google.com/tag-manager/templates/monitoring&quot; rel=&quot;noopener&quot;&gt;Monitoring
API&lt;/a&gt; can be used
to gather information about the &lt;a href=&quot;https://developers.google.com/tag-manager/templates/monitoring#execution_times&quot; rel=&quot;noopener&quot;&gt;execution
time&lt;/a&gt;
of a particular tag. This information is reported to an endpoint of your
choosing.&lt;/p&gt;
&lt;p&gt;For more information, see &lt;a href=&quot;https://www.simoahava.com/analytics/google-tag-manager-monitor/&quot; rel=&quot;noopener&quot;&gt;How to build a Google Tag Manager
Monitor&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;require-approval-for-container-changes&quot;&gt;Require approval for container changes &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#require-approval-for-container-changes&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;First-party code typically goes through review and testing before deployment -
treat your tags the same. Adding &lt;a href=&quot;https://support.google.com/tagmanager/answer/4525539/tag-manager-and-2-step-verification?hl=en&quot; rel=&quot;noopener&quot;&gt;two-step
verification&lt;/a&gt;,
which requires administrator approval for container changes, is one way to do
this. Alternatively, if you don&#39;t want to require two-step verification but
would still like to keep an eye on changes, you can set up &lt;a href=&quot;https://support.google.com/tagmanager/answer/9713667?hl=en&quot; rel=&quot;noopener&quot;&gt;container
notifications&lt;/a&gt; to
receive email alerts about container events of your choosing.&lt;/p&gt;
&lt;h3 id=&quot;periodically-audit-tag-usage&quot;&gt;Periodically audit tag usage &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#periodically-audit-tag-usage&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One of the challenges of working with tags is that they tend to accumulate over
time: tags get added but are rarely removed. Auditing tags periodically is one
way to reverse this trend. The ideal frequency for doing this will depend on how
often your site&#39;s tags are updated.&lt;/p&gt;
&lt;p&gt;Labeling each tag so the owner is obvious allows easier identification of who
is responsive for that tag and can say whether it is still needed.&lt;/p&gt;
&lt;p&gt;When auditing tags, do not forget about cleaning up triggers and variables as
well. They can easily be the cause of performance issues too.&lt;/p&gt;
&lt;p&gt;For more information, see &lt;a href=&quot;https://web.dev/controlling-third-party-scripts/&quot;&gt;Keeping third-party scripts under
control&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author><author>
      <name>Barry Pollard</name>
    </author>
  </entry>
  
  <entry>
    <title>Best practices for fonts</title>
    <link href="https://web.dev/font-best-practices/"/>
    <updated>2021-06-03T00:00:00Z</updated>
    <id>https://web.dev/font-best-practices/</id>
    <content type="html" mode="escaped">&lt;p&gt;This article discusses performance best practices for fonts. There are a variety of ways in which web fonts impact performance:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Delayed text rendering:&lt;/strong&gt; If a web font has not loaded, browsers typically delay text rendering. In many situations, this delays &lt;a href=&quot;https://web.dev/fcp&quot;&gt;First Contentful Paint (FCP)&lt;/a&gt;. In some situations, this delays &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Layout shifts:&lt;/strong&gt; The practice of font swapping has the potential to &lt;a href=&quot;https://web.dev/debug-layout-shifts/#identifying-the-cause-of-a-layout-shift&quot;&gt;cause layout shifts&lt;/a&gt; and so impact &lt;a href=&quot;https://web.dev/cls/&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt;. These layout shifts occur when a web font and its fallback font take up different amounts of space on the page.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This article is broken down into three sections: &lt;strong&gt;font loading&lt;/strong&gt;, &lt;strong&gt;font delivery&lt;/strong&gt;, and &lt;strong&gt;font rendering&lt;/strong&gt;. Each section explains how that particular aspect of the font lifecycle works and provides corresponding best practices.&lt;/p&gt;
&lt;h2 id=&quot;font-loading&quot;&gt;Font loading &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#font-loading&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Fonts are typically important resources, as without them the user might be unable to view page content. Thus, best practices for font loading generally focus on making sure that fonts get loaded as early as possible. Particular care should be given to fonts loaded from third-party sites as downloading these font files requires separate connection setups.&lt;/p&gt;
&lt;p&gt;If you&#39;re unsure if your page&#39;s fonts are being requested in time, check the &lt;strong&gt;Timing&lt;/strong&gt; tab within the &lt;strong&gt;Network&lt;/strong&gt; panel in Chrome DevTools for more information.&lt;/p&gt;
&lt;img alt=&quot;Screenshot of the Timing tab in DevTools&quot; decoding=&quot;async&quot; height=&quot;472&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h3 id=&quot;understanding-font-face&quot;&gt;Understanding &lt;code&gt;@font-face&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#understanding-font-face&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Before diving into best practices for font loading it&#39;s important to understand how &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@font-face&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;@font-face&lt;/code&gt;&lt;/a&gt; works and how this impacts font loading.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@font-face&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;@font-face&lt;/code&gt;&lt;/a&gt; declaration is an essential part of working with any web font. At a minimum, it declares the name that will be used to refer to the font and indicates the location of the corresponding font file.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Open Sans&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;/fonts/OpenSans-Regular-webfont.woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;A common misconception is that a font is requested when a &lt;code&gt;@font-face&lt;/code&gt; declaration is encountered—this is not true. By itself, &lt;code&gt;@font-face&lt;/code&gt; declaration does not trigger font download. Rather, a font is downloaded only if it is referenced by styling that is used on the page. For example, like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Open Sans&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;/fonts/OpenSans-Regular-webfont.woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;h1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Open Sans&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In other words, in the example above, &lt;code&gt;Open Sans&lt;/code&gt; would only be downloaded if the page contained a &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; element.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Other ways of loading a font are the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Preloading_content&quot;&gt;&lt;code&gt;preload&lt;/code&gt;&lt;/a&gt; resource hint and the &lt;a href=&quot;https://web.dev/optimize-webfont-loading/#the-font-loading-api&quot;&gt;Font Loading API&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Thus, when thinking about font optimization, it&#39;s important to give stylesheets just as much consideration as the font files themselves. Changing the contents or delivery of stylesheets can have a significant impact on when fonts arrive. Similarly, removing unused CSS and splitting stylesheets can reduce the number of fonts loaded by a page.&lt;/p&gt;
&lt;h3 id=&quot;inline-font-declarations&quot;&gt;Inline font declarations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#inline-font-declarations&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Most sites would strongly benefit from inlining font declarations and other critical styling in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of the main document rather than including them in an external stylesheet. This allows the browser to discover the font declarations sooner as the browser doesn&#39;t need to wait for the external stylesheet to download.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;&lt;br /&gt;    &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Open Sans&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;/fonts/OpenSans-Regular-webfont.woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Open Sans&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    ...etc.&lt;br /&gt;&lt;br /&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Note that if only some of the CSS is inlined, then the browser will still need to wait for all the CSS to be loaded, before it can discover if fonts are needed.&lt;br /&gt; &lt;br /&gt; Also note that inlining the font files themselves is not recommended. Inlining large resources like fonts is likely to delay the delivery of the main document, and with it, the discovery of other resources. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Inlining critical CSS can be a more advanced technique that not all sites will be able to achieve. The performance benefits are clear, but it requires additional processes and build tools to ensure the necessarily CSS—and ideally only the critical CSS—is inlined correctly and that any additional CSS is delivered in a non-render blocking fashion.&lt;/p&gt;
&lt;h3 id=&quot;preconnect-to-critical-third-party-origins&quot;&gt;Preconnect to critical third-party origins &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#preconnect-to-critical-third-party-origins&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If your site loads fonts from a third-party site, it is highly recommended that you use the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Link_types/preconnect&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;preconnect&lt;/code&gt;&lt;/a&gt; resource hint to establish early connection(s) with the third-party origin. Resource hints should be placed in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of the document. The resource hint below sets up a connection for loading the font stylesheet.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preconnect&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://fonts.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;To preconnect the connection that is used to download the font file, add a separate &lt;code&gt;preconnect&lt;/code&gt; resource hint that uses the &lt;code&gt;crossorigin&lt;/code&gt; attribute. Unlike stylesheets, font files must be sent over a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/CORS#what_requests_use_cors&quot; rel=&quot;noopener&quot;&gt;CORS connection&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preconnect&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://fonts.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preconnect&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://fonts.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;crossorigin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;When using the &lt;code&gt;preconnect&lt;/code&gt; resource hint, keep in mind that a font provider may serve stylesheets and fonts from separate origins. For example, this is how the &lt;code&gt;preconnect&lt;/code&gt; resource hint would be used for Google Fonts.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preconnect&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://fonts.googleapis.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preconnect&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://fonts.gstatic.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;crossorigin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; &lt;a href=&quot;https://fonts.google.com/&quot;&gt;Google Fonts&lt;/a&gt; provides the option to load fonts via &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags or an &lt;code&gt;@import&lt;/code&gt; statement. The &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; code snippet includes a &lt;code&gt;preconnect&lt;/code&gt; resource hint and therefore will likely result in faster stylesheet delivery than using &lt;code&gt;@import&lt;/code&gt; version. These &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags should be placed as early in the document as possible. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;be-cautious-when-using-preload-to-load-fonts&quot;&gt;Be cautious when using &lt;code&gt;preload&lt;/code&gt; to load fonts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#be-cautious-when-using-preload-to-load-fonts&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Although &lt;code&gt;preload&lt;/code&gt; is highly effective at making fonts discoverable early in the page load process, this comes at the cost of taking away browser resources from the loading of other resources.&lt;/p&gt;
&lt;p&gt;Inlining font declarations and adjusting stylesheets may be a more effective approach. These adjustments come closer to addressing the root cause of late-discovered fonts—rather than just providing a workaround.&lt;/p&gt;
&lt;p&gt;In addition, using &lt;code&gt;preload&lt;/code&gt; as a font-loading strategy should also be used carefully as it bypasses some of the browser&#39;s built-in content negotiation strategies. For example, &lt;code&gt;preload&lt;/code&gt; ignores &lt;code&gt;unicode-range&lt;/code&gt; declarations, and if used prudently, should only be used to load a single font format.&lt;/p&gt;
&lt;p&gt;However, when using external stylesheets, preloading the most important fonts can be very effective since the browser will not otherwise discover whether the font is needed until much later.&lt;/p&gt;
&lt;h2 id=&quot;font-delivery&quot;&gt;Font delivery &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#font-delivery&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Faster font delivery yields faster text rendering. In addition, if a font is delivered early enough, this can help eliminate layout shifts resulting from font swapping.&lt;/p&gt;
&lt;h3 id=&quot;using-self-hosted-fonts&quot;&gt;Using self-hosted fonts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#using-self-hosted-fonts&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;On paper, using a self-hosted font should deliver better performance as it eliminates a third-party connection setup. However, in practice, the performance differences between these two options is less clear cut: for example, the &lt;a href=&quot;https://almanac.httparchive.org/en/2020/fonts#fig-7&quot; rel=&quot;noopener&quot;&gt;Web Almanac&lt;/a&gt; found that sites using third-party fonts had a faster render than fonts that used first-party fonts.&lt;/p&gt;
&lt;p&gt;If you are considering using self-hosted fonts, confirm that your site is using a &lt;a href=&quot;https://web.dev/content-delivery-networks/&quot;&gt;Content Delivery Network (CDN)&lt;/a&gt; and &lt;a href=&quot;https://web.dev/content-delivery-networks/#http2-and-http3&quot;&gt;HTTP/2&lt;/a&gt;. Without use of these technologies, it is much less likely that self-hosted fonts will deliver better performance. For more information, see &lt;a href=&quot;https://web.dev/content-delivery-networks/&quot;&gt;Content Delivery Networks&lt;/a&gt;.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; If you&#39;re unsure if using self-hosted fonts will deliver better performance, try serving a font file from your own servers and compare the transfer time (including connection setup) with that of a third-party font. If you have slow servers, don&#39;t use a CDN, or don&#39;t use HTTP/2 it becomes less likely that the self-hosted font will be more performant. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;If you use a self-hosted font, it is recommended that you also apply some of the font file optimizations that third-party font providers typically provide automatically—for example, font subsetting and WOFF2 compression. The amount of effort required to apply these optimizations will depend somewhat on the languages that your site supports. In particular, be aware that optimizing fonts for &lt;a href=&quot;https://en.wikipedia.org/wiki/CJK_characters&quot; rel=&quot;noopener&quot;&gt;CJK languages&lt;/a&gt; can be particularly challenging.&lt;/p&gt;
&lt;h3 id=&quot;use-woff2&quot;&gt;Use WOFF2 &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#use-woff2&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Of the modern font fonts, &lt;a href=&quot;https://www.w3.org/TR/WOFF2/&quot; rel=&quot;noopener&quot;&gt;WOFF2&lt;/a&gt; is the newest, has the widest browser support, and offers the best compression. Because it uses Brotli, WOFF2 compresses 30% better than WOFF, leading to less data to download and therefore faster performance.&lt;/p&gt;
&lt;p&gt;Given the browser support, experts now recommend only using WOFF2:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;
    In fact, we think it is also time to proclaim: Use only WOFF2 and forget about everything else.&lt;br /&gt;
    &lt;br /&gt;
    This will simplify your CSS and workflow massively and also prevents any accidental double or incorrect font downloads. WOFF2 is now supported everywhere. So, unless you need to support really ancient browsers, just use WOFF2. If you can&#39;t, consider not serving any web fonts to those older browsers at all. This will not be a problem if you have a robust fallback strategy in place. Visitors on older browsers will simply see your fallback fonts.
  &lt;/p&gt;
  &lt;cite&gt;
    &lt;a href=&quot;https://almanac.httparchive.org/en/2022/fonts#performance&quot;&gt;Bram Stein, from the 2022 Web Almanac&lt;/a&gt;
  &lt;/cite&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;subset-fonts&quot;&gt;Subset fonts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#subset-fonts&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Font files typically include a large number of &lt;a href=&quot;https://en.wikipedia.org/wiki/Glyph&quot; rel=&quot;noopener&quot;&gt;glyphs&lt;/a&gt; for all the various characters they support. But you may not need all the characters on your page and can reduce the size of font files by subsetting fonts.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@font-face/unicode-range&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;unicode-range&lt;/code&gt;&lt;/a&gt; descriptor in the &lt;code&gt;@font-face&lt;/code&gt; declartion informs the browser which characters a font can be used for.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Open Sans&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;/fonts/OpenSans-Regular-webfont.woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;unicode-range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; U+0025-00FF&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;A font file will be downloaded if the page contains one or more characters matching the unicode range. &lt;code&gt;unicode-range&lt;/code&gt; is commonly used to serve different font files depending on the language used by page content.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;unicode-range&lt;/code&gt; is often used in conjunction with the technique of subsetting. A subset font includes a smaller portion of the that were contained in the original font file. For example, rather than serve all characters to all users, a site might generate separate subset fonts for Latin and Cyrillic characters. The number of glyphs per font varies wildly: Latin fonts are usually on the magnitude of 100 to 1000 glyphs per font; &lt;a href=&quot;https://en.wikipedia.org/wiki/CJK_characters&quot; rel=&quot;noopener&quot;&gt;CJK&lt;/a&gt; fonts may have over 10,000 characters. Removing unused glyphs can significantly reduce the filesize of a font.&lt;/p&gt;
&lt;p&gt;Some font providers may provide different versions of fonts files with different subsets automatically. For example, &lt;a href=&quot;https://fonts.google.com/&quot; rel=&quot;noopener&quot;&gt;Google Fonts&lt;/a&gt; does this by default:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* devanagari */&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Poppins&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 400&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; swap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJbecnFHGPezSQ.woff2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;woff2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;unicode-range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; U+0900-097F&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+1CD0-1CF6&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+1CF8-1CF9&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+200C-200D&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+20A8&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+20B9&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+25CC&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+A830-A839&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+A8E0-A8FB&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;/* latin-ext */&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Poppins&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 400&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; swap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJnecnFHGPezSQ.woff2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;woff2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;unicode-range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; U+0100-024F&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+0259&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+1E00-1EFF&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+2020&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+20A0-20AB&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+20AD-20CF&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+2113&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+2C60-2C7F&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+A720-A7FF&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;/* latin */&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Poppins&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 400&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; swap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJfecnFHGPc.woff2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;woff2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;unicode-range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; U+0000-00FF&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+0131&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+0152-0153&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+02BB-02BC&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+02C6&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+02DA&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+02DC&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+2000-206F&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+2074&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+20AC&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+2122&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+2191&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+2193&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+2212&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+2215&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+FEFF&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+FFFD&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;When moving to self-hosting, this is an optimization that can easily be missed and lead to larger font files locally.&lt;/p&gt;
&lt;p&gt;It is also possible to manually subset fonts if your font provider allows this, either through an API (&lt;a href=&quot;https://developers.google.com/fonts/docs/getting_started#specifying_script_subsets&quot; rel=&quot;noopener&quot;&gt;Google Fonts supports this by providing a &lt;code&gt;text&lt;/code&gt; parameter&lt;/a&gt;), or by manually editing the font files and then self-hosting. Tools for generating font subsets include &lt;a href=&quot;https://github.com/Munter/subfont&quot; rel=&quot;noopener&quot;&gt;subfont&lt;/a&gt; and &lt;a href=&quot;https://github.com/zachleat/glyphhanger&quot; rel=&quot;noopener&quot;&gt;glyphanger&lt;/a&gt;. However, do &lt;a href=&quot;https://subsetting.xyz/&quot; rel=&quot;noopener&quot;&gt;check the licence for the fonts you use allow subsetting&lt;/a&gt; and self-hosting.&lt;/p&gt;
&lt;h3 id=&quot;use-fewer-web-fonts&quot;&gt;Use fewer web fonts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#use-fewer-web-fonts&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The fastest font to deliver is a font that isn&#39;t requested in the first place. System fonts and variable fonts are two ways to potentially reduce the number of web fonts used on your site.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;system font&lt;/strong&gt; is the default font used by the user interface of a user&#39;s device. System fonts typically vary by operating system and version. Because the font is already installed, the font does not need to be downloaded. System fonts can work particularly well for body text.&lt;/p&gt;
&lt;p&gt;To use the system font in your CSS, list &lt;code&gt;system-ui&lt;/code&gt; as the font-family:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; system-ui&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The idea behind &lt;strong&gt;&lt;a href=&quot;https://web.dev/variable-fonts/&quot;&gt;variable fonts&lt;/a&gt;&lt;/strong&gt; is that a single variable font can be used as a replacement for multiple font files. Variable fonts work by defining a &amp;quot;default&amp;quot; font style and providing &lt;a href=&quot;https://web.dev/variable-fonts/#axes-definitions&quot;&gt;&amp;quot;axes&amp;quot;&lt;/a&gt; for manipulating the font. For example, a variable font with a &lt;code&gt;Weight&lt;/code&gt; axis could be used to implement lettering that would previously require separate fonts for light, regular, bold, and extra bold.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; We often refer to &amp;quot;Times New Roman&amp;quot; and &amp;quot;Helvetica&amp;quot; as fonts. However, technically speaking, these are font &lt;em&gt;families&lt;/em&gt;. A family is composed of styles, which are particular variations of the typeface (for example, light, medium, or bold italic). A font file contains a single style unless it is a variable font. A typeface is the underlying design, which can be expressed as digital fonts—and in physical type, like carved woodblocks or metal. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Not everyone will benefit from switching to variable fonts. &lt;a href=&quot;https://web.dev/variable-fonts/&quot;&gt;Variable fonts&lt;/a&gt; contain many styles, so typically have larger file sizes than individual non-variable fonts that only contain one style. Sites that will see the largest improvement from using variable fonts are those that use (and need to use) a variety of font styles and weights.&lt;/p&gt;
&lt;h2 id=&quot;font-rendering&quot;&gt;Font rendering &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#font-rendering&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When faced with a web font that has not yet loaded, the browser is faced with a dilemma: should it hold off on rendering text until the web font has arrived? Or should it render the text in a fallback font until the web font arrives?&lt;/p&gt;
&lt;p&gt;Different browsers handle this scenario differently. By default, Chromium-based and Firefox browsers will block text rendering for up to 3 seconds if the associated web font has not loaded; Safari will block text rendering indefinitely.&lt;/p&gt;
&lt;p&gt;This behavior can be configured by using the &lt;code&gt;font-display&lt;/code&gt; attribute. This choice can have significant implications: &lt;code&gt;font-display&lt;/code&gt; has the potential to impact LCP, FCP, and layout stability.&lt;/p&gt;
&lt;h3 id=&quot;choose-an-appropriate-font-display-strategy&quot;&gt;Choose an appropriate &lt;code&gt;font-display&lt;/code&gt; strategy &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#choose-an-appropriate-font-display-strategy&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@font-face/font-display&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;font-display&lt;/code&gt;&lt;/a&gt; informs the browser how it should proceed with text rendering when the associated web font has not loaded. It is defined per font-face.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Roboto&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Sans-Serif&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;/fonts/roboto.woff&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;woff&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; swap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;There are five possible values for &lt;code&gt;font-display&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;&lt;strong&gt;Value&lt;/strong&gt;&lt;/th&gt;
        &lt;th&gt;&lt;strong&gt;Block period&lt;/strong&gt;&lt;/th&gt;
        &lt;th&gt;&lt;strong&gt;Swap period&lt;/strong&gt;&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;Auto&lt;/td&gt;
        &lt;td&gt;Varies by browser&lt;/td&gt;
        &lt;td&gt;Varies by browser&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Block&lt;/td&gt;
        &lt;td&gt;2-3 seconds&lt;/td&gt;
        &lt;td&gt;Infinite&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Swap&lt;/td&gt;
        &lt;td&gt;0ms&lt;/td&gt;
        &lt;td&gt;Infinite&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Fallback&lt;/td&gt;
        &lt;td&gt;100ms&lt;/td&gt;
        &lt;td&gt;3 seconds&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Optional&lt;/td&gt;
        &lt;td&gt;100ms&lt;/td&gt;
        &lt;td&gt;None&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Block period&lt;/strong&gt;: The block period begins when the browser requests a web font. During the block period, if the web font is not available, the font is rendered in an &lt;em&gt;invisible&lt;/em&gt; fallback font and thus the text will not be visible to the user. If the font is not available at the end of the block period, it will be rendered in the fallback font.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Swap period&lt;/strong&gt;: The swap period comes after the block period. If the web font becomes available during the swap period, it will be &amp;quot;swapped&amp;quot; in.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;font-display&lt;/code&gt; strategies reflect different viewpoints about the tradeoff between performance and aesthetics. As such it&#39;s difficult to give a recommended approach as it does depend on individual preferences, how important the web font is to the page and brand, and how jarring a late-arriving font can be when swapped in.&lt;/p&gt;
&lt;p&gt;For most sites, these are the three strategies that will be most applicable:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;If performance is a top priority:&lt;/strong&gt; Use &lt;code&gt;font-display: optional&lt;/code&gt;. This is the most &amp;quot;performant&amp;quot; approach: text render is delayed for no longer than 100ms and there is assurance that there will be no font-swap related layout shifts. However, the downside here is the web font will not be used if it arrives late.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;If displaying text quickly is a top priority, but you want to still ensure the web-font is used:&lt;/strong&gt; Use &lt;code&gt;font-display: swap&lt;/code&gt; but make sure to deliver the font early enough that it does not cause a layout shift. The downside of this option is the jarring shift when the font arrives late.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;If ensuring text is displayed in a web font is a top priority:&lt;/strong&gt; Use &lt;code&gt;font-display: block&lt;/code&gt; but make sure to deliver the font early enough that it minimises the delay of the text. The downside of this is the initial text display will be delayed. Note despite this deplay, it can still cause a layout shift as the text is actually drawn invisible, and the fallback font space is therefore user to reserver the space. Once the web font loads, this may require difference space and hence a shift. This may, however, be a less jarring shift than &lt;code&gt;font-display: swap&lt;/code&gt; as the text itself will not be seen to shift.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; &lt;code&gt;font-display: auto&lt;/code&gt;, &lt;code&gt;font-display: block&lt;/code&gt;, &lt;code&gt;font-display: swap&lt;/code&gt;, and &lt;code&gt;font-display: fallback&lt;/code&gt; all have the potential to cause layout shifts when the font is swapped. However, of these approaches, &lt;code&gt;font-display: swap&lt;/code&gt; will delay text render the least. Thus, it can be the preferred approach for situations where it is important that text is displayed as quickly as possible, but ultimately gets rendered as a web font. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Also keep in mind that these two approaches can be combined: for example, use &lt;code&gt;font-display: swap&lt;/code&gt; for branding and other visually distinctive page elements; use &lt;code&gt;font-display: optional&lt;/code&gt; for fonts used in body text.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The &lt;code&gt;font-display&lt;/code&gt; strategies that work well for traditional web fonts don&#39;t work nearly as well for icon fonts. The fallback font for an icon font typically looks significantly different than the icon font and its characters may convey a completely different meaning. As a result, icon fonts are more likely to cause significant layout shifts. In addition, using a fallback font may not be practical. If possible, it&#39;s best to replace icon fonts with SVG (this is also better for accessibility). Newer versions of popular icon fonts typically support SVG. For more information on switching to SVG, see &lt;a href=&quot;https://fontawesome.com/v5.15/how-to-use/on-the-web/advanced/svg-sprites&quot;&gt;Font Awesome&lt;/a&gt; and &lt;a href=&quot;https://google.github.io/material-design-icons/#svg&quot;&gt;Material Icons&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;reduce-the-shift-between-your-fallback-font-and-your-webfont&quot;&gt;Reduce the shift between your fallback font and your webfont &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#reduce-the-shift-between-your-fallback-font-and-your-webfont&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To reduce the CLS impact, you can use the new &lt;code&gt;size-adjust&lt;/code&gt; attributes. For more information see &lt;a href=&quot;https://web.dev/css-size-adjust/&quot;&gt;this article&lt;/a&gt;. This is a very new addition to our toolset, so is more advanced and a bit manual at present. But definitely one to experiment with and watch for tooling improvements in the future!&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Web fonts still be a performance bottleneck but we have an ever-growing range of options to allow us to optimize them to reduce this bottleneck as much as possible.&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author><author>
      <name>Barry Pollard</name>
    </author>
  </entry>
  
  <entry>
    <title>Back/forward cache</title>
    <link href="https://web.dev/bfcache/"/>
    <updated>2020-11-10T00:00:00Z</updated>
    <id>https://web.dev/bfcache/</id>
    <content type="html" mode="escaped">&lt;p&gt;Back/forward cache (or bfcache) is a browser optimization that enables instant
back and forward navigation. It significantly improves the browsing experience
for users—especially those with slower networks or devices.&lt;/p&gt;
&lt;p&gt;As web developers, it&#39;s critical to understand how to &lt;a href=&quot;https://web.dev/bfcache/#optimize-your-pages-for-bfcache&quot;&gt;optimize your pages for
bfcache&lt;/a&gt; across all browsers, so your users
can reap the benefits.&lt;/p&gt;
&lt;h2 id=&quot;browser-compatibility&quot;&gt;Browser compatibility &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bfcache/#browser-compatibility&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;bfcache has been supported in both
&lt;a href=&quot;https://developer.mozilla.org/docs/Mozilla/Firefox/Releases/1.5/Using_Firefox_1.5_caching&quot; rel=&quot;noopener&quot;&gt;Firefox&lt;/a&gt;
and &lt;a href=&quot;https://webkit.org/blog/427/webkit-page-cache-i-the-basics/&quot; rel=&quot;noopener&quot;&gt;Safari&lt;/a&gt; for
many years, across desktop and mobile.&lt;/p&gt;
&lt;p&gt;Starting in version 86, Chrome enabled bfcache for
&lt;a href=&quot;https://web.dev/same-site-same-origin/&quot;&gt;cross-site&lt;/a&gt; navigations on Android for a small
percentage of users. In subsequent releases, additional support slowly rolled
out. Since version 96, bfcache is enabled for all Chrome users across desktop
and mobile.&lt;/p&gt;
&lt;h2 id=&quot;bfcache-basics&quot;&gt;bfcache basics &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bfcache/#bfcache-basics&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;bfcache is an in-memory cache that stores a complete snapshot of a page
(including the JavaScript heap) as the user is navigating away. With the entire
page in memory, the browser can quickly and easily restore it if the user
decides to return.&lt;/p&gt;
&lt;p&gt;How many times have you visited a website and clicked a link to go to another
page, only to realize it&#39;s not what you wanted and click the back button? In
that moment, bfcache can make a big difference in how fast the previous page
loads:&lt;/p&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table data-alignment=&quot;top&quot;&gt;
    &lt;tr&gt;
      &lt;td width=&quot;30%&quot;&gt;&lt;strong&gt;&lt;em&gt;Without&lt;/em&gt; bfcache enabled&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;
        A new request is initiated to load the previous page, and, depending
        on how well that page has been &lt;a href=&quot;https://web.dev/reliable/#the-options-in-your-caching-toolbox&quot;&gt;
        optimized&lt;/a&gt; for repeat visits, the browser might have to re-download,
        re-parse, and re-execute some (or all) of resources it just downloaded.
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;strong&gt;&lt;em&gt;With&lt;/em&gt; bfcache enabled&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;
        Loading the previous page is &lt;em&gt;essentially instant&lt;/em&gt;, because the
        entire page can be restored from memory, without having to go to the
        network at all
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Check out this video of bfcache in action to understand the speed up it can
bring to navigations:&lt;/p&gt;
&lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;cuPsdRckkF0&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
&lt;p&gt;In the video above, the example with bfcache is quite a bit faster than the
example without it.&lt;/p&gt;
&lt;p&gt;bfcache not only speeds up navigation, it also reduces data usage, since
resources do not have to be downloaded again.&lt;/p&gt;
&lt;p&gt;Chrome usage data shows that 1 in 10 navigations on desktop and 1 in 5 on mobile
are either back or forward. With bfcache enabled, browsers could eliminate the
data transfer and time spent loading for billions of web pages every single day!&lt;/p&gt;
&lt;h3 id=&quot;how-the-cache-works&quot;&gt;How the &amp;quot;cache&amp;quot; works &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bfcache/#how-the-cache-works&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &amp;quot;cache&amp;quot; used by bfcache is different from the &lt;a href=&quot;https://web.dev/http-cache/&quot;&gt;HTTP cache&lt;/a&gt;
(which is also useful in speeding up repeat navigations). The bfcache is a
snapshot of the entire page in memory (including the JavaScript heap), whereas
the HTTP cache contains only the responses for previously made requests. Since
it&#39;s quite rare that all requests required to load a page can be fulfilled from
the HTTP cache, repeat visits using bfcache restores are always faster than even
the most well-optimized non-bfcache navigations.&lt;/p&gt;
&lt;p&gt;Creating a snapshot of a page in memory, however, involves some complexity in
terms of how best to preserve in-progress code. For example, how do you handle
&lt;code&gt;setTimeout()&lt;/code&gt; calls where the timeout is reached while the page is in the
bfcache?&lt;/p&gt;
&lt;p&gt;The answer is that browsers pause running any pending timers or unresolved
promises—essentially all pending tasks in the &lt;a href=&quot;https://html.spec.whatwg.org/multipage/webappapis.html#task-queue&quot; rel=&quot;noopener&quot;&gt;JavaScript task
queues&lt;/a&gt;—and
resume processing tasks when (or if) the page is restored from the bfcache.&lt;/p&gt;
&lt;p&gt;In some cases this is fairly low-risk (for example, timeouts or promises), but
in other cases it might lead to very confusing or unexpected behavior. For
example, if the browser pauses a task that&#39;s required as part of an &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/IDBTransaction&quot; rel=&quot;noopener&quot;&gt;IndexedDB
transaction&lt;/a&gt;,
it can affect other open tabs in the same origin (since the same IndexedDB
databases can be accessed by multiple tabs simultaneously). As a result,
browsers will generally not attempt to cache pages in the middle of an IndexedDB
transaction or using APIs that might affect other pages.&lt;/p&gt;
&lt;p&gt;For more details on how various API usage affects a page&#39;s bfcache eligibility,
see &lt;a href=&quot;https://web.dev/bfcache/#optimize-your-pages-for-bfcache&quot;&gt;Optimize your pages for bfcache&lt;/a&gt; below.&lt;/p&gt;
&lt;h3 id=&quot;apis-to-observe-bfcache&quot;&gt;APIs to observe bfcache &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bfcache/#apis-to-observe-bfcache&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;While bfcache is an optimization that browsers do automatically, it&#39;s still
important for developers to know when it&#39;s happening so they can &lt;a href=&quot;https://web.dev/bfcache/#optimize-your-pages-for-bfcache&quot;&gt;optimize their
pages for it&lt;/a&gt; and &lt;a href=&quot;https://web.dev/bfcache/#implications-for-analytics-and-performance-measurement&quot;&gt;adjust any metrics or
performance
measurement&lt;/a&gt;
accordingly.&lt;/p&gt;
&lt;p&gt;The primary events used to observe bfcache are the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/PageTransitionEvent&quot; rel=&quot;noopener&quot;&gt;page transition
events&lt;/a&gt;—&lt;code&gt;pageshow&lt;/code&gt;
and &lt;code&gt;pagehide&lt;/code&gt;—which have been around as long as bfcache has and are supported
in pretty much &lt;a href=&quot;https://caniuse.com/page-transition-events&quot; rel=&quot;noopener&quot;&gt;all browsers in use
today&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The newer &lt;a href=&quot;https://developer.chrome.com/blog/page-lifecycle-api/&quot; rel=&quot;noopener&quot;&gt;Page
Lifecycle&lt;/a&gt;
events—&lt;code&gt;freeze&lt;/code&gt; and &lt;code&gt;resume&lt;/code&gt;—are also dispatched when pages go in or out of the
bfcache, as well as in some other situations. For example when a background tab
gets frozen to minimize CPU usage. Note, the Page Lifecycle events are currently
only supported in Chromium-based browsers.&lt;/p&gt;
&lt;h4 id=&quot;observe-when-a-page-is-restored-from-bfcache&quot;&gt;Observe when a page is restored from bfcache &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bfcache/#observe-when-a-page-is-restored-from-bfcache&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;pageshow&lt;/code&gt; event fires right after the &lt;code&gt;load&lt;/code&gt; event when the page is
initially loading and any time the page is restored from bfcache. The &lt;code&gt;pageshow&lt;/code&gt;
event has a
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/PageTransitionEvent/persisted&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;persisted&lt;/code&gt;&lt;/a&gt;
property which will be &lt;code&gt;true&lt;/code&gt; if the page was restored from bfcache
(and &lt;code&gt;false&lt;/code&gt; if not). You can use the &lt;code&gt;persisted&lt;/code&gt; property
to distinguish regular page loads from bfcache restores. For example:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;pageshow&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persisted&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;This page was restored from the bfcache.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;This page was loaded normally.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In browsers that support the Page Lifecycle API, the &lt;code&gt;resume&lt;/code&gt; event will also
fire when pages are restored from bfcache (immediately before the &lt;code&gt;pageshow&lt;/code&gt;
event), though it will also fire when a user revisits a frozen background tab.
If you want to restore a page&#39;s state after it&#39;s frozen (which includes pages in
the bfcache), you can use the &lt;code&gt;resume&lt;/code&gt; event, but if you want to measure your
site&#39;s bfcache hit rate, you&#39;d need to use the &lt;code&gt;pageshow&lt;/code&gt; event. In some cases,
you might need to use both.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; See &lt;a href=&quot;https://web.dev/bfcache/#how-bfcache-affects-analytics-and-performance-measurement&quot;&gt;Implications for performance and analytics&lt;/a&gt; for more details on bfcache measurement best practices. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;observe-when-a-page-is-entering-bfcache&quot;&gt;Observe when a page is entering bfcache &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bfcache/#observe-when-a-page-is-entering-bfcache&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;pagehide&lt;/code&gt; event is the counterpart to the &lt;code&gt;pageshow&lt;/code&gt; event. The &lt;code&gt;pageshow&lt;/code&gt;
event fires when a page is either loaded normally or restored from the bfcache.
The &lt;code&gt;pagehide&lt;/code&gt; event fires when the page is either unloaded normally or when the
browser attempts to put it into the bfcache.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;pagehide&lt;/code&gt; event also has a &lt;code&gt;persisted&lt;/code&gt; property, and if it&#39;s &lt;code&gt;false&lt;/code&gt; then
you can be confident a page is not about to enter the bfcache. However, if the
&lt;code&gt;persisted&lt;/code&gt; property is &lt;code&gt;true&lt;/code&gt;, it doesn&#39;t guarantee that a page will be cached.
It means that the browser &lt;em&gt;intends&lt;/em&gt; to cache the page, but there may be factors
that make it impossible to cache.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;pagehide&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persisted&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;This page *might* be entering the bfcache.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;This page will unload normally and be discarded.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Similarly, the &lt;code&gt;freeze&lt;/code&gt; event will fire immediately after the &lt;code&gt;pagehide&lt;/code&gt; event
(if the event&#39;s &lt;code&gt;persisted&lt;/code&gt; property is &lt;code&gt;true&lt;/code&gt;), but again that only means the
browser &lt;em&gt;intends&lt;/em&gt; to cache the page. It may still have to discard it for a
number of reasons explained below.&lt;/p&gt;
&lt;h2 id=&quot;optimize-your-pages-for-bfcache&quot;&gt;Optimize your pages for bfcache &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bfcache/#optimize-your-pages-for-bfcache&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Not all pages get stored in bfcache, and even when a page does get stored there,
it won&#39;t stay there indefinitely. It&#39;s critical that developers understand what
makes pages eligible (and ineligible) for bfcache to maximize their cache-hit
rates.&lt;/p&gt;
&lt;p&gt;The following sections outline the best practices to make it as likely as
possible that the browser can cache your pages.&lt;/p&gt;
&lt;h3 id=&quot;never-use-the-unload-event&quot;&gt;Never use the &lt;code&gt;unload&lt;/code&gt; event &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bfcache/#never-use-the-unload-event&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The most important way to optimize for bfcache in all browsers is to never use
the &lt;code&gt;unload&lt;/code&gt; event. Ever!&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;unload&lt;/code&gt; event is problematic for browsers because it predates bfcache and
many pages on the internet operate under the (reasonable) assumption that a page
will not continue to exist after the &lt;code&gt;unload&lt;/code&gt; event has fired. This presents a
challenge because many of those pages were &lt;em&gt;also&lt;/em&gt; built with the assumption that
the &lt;code&gt;unload&lt;/code&gt; event would fire any time a user is navigating away, which is no
longer true (and &lt;a href=&quot;https://developer.chrome.com/blog/page-lifecycle-api/#the-unload-event&quot; rel=&quot;noopener&quot;&gt;hasn&#39;t been true for a long
time&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;So browsers are faced with a dilemma, they have to choose between something that
can improve the user experience—but might also risk breaking the page.&lt;/p&gt;
&lt;p&gt;On desktop, Chrome and Firefox have chosen to make pages ineligible for bfcache if they add an &lt;code&gt;unload&lt;/code&gt;
listener, which is less risky but also disqualifies &lt;em&gt;a lot&lt;/em&gt; of pages. Safari
will attempt to cache some pages with an &lt;code&gt;unload&lt;/code&gt; event listener, but to reduce
potential breakage it will not run the &lt;code&gt;unload&lt;/code&gt; event when a user is navigating
away, which makes the event very unreliable.&lt;/p&gt;
&lt;p&gt;On mobile, Chrome and Safari will attempt to cache pages with an &lt;code&gt;unload&lt;/code&gt; event listener since the risk of breakage is lower due to the fact that the &lt;code&gt;unload&lt;/code&gt; event has always been extremely unreliable on mobile. Firefox treats pages that use &lt;code&gt;unload&lt;/code&gt; as ineligible for the bfcache, except on iOS, which requires all browsers to use the WebKit rendering engine, and so it behaves like Safari.&lt;/p&gt;
&lt;p&gt;Instead of using the &lt;code&gt;unload&lt;/code&gt; event, use the &lt;code&gt;pagehide&lt;/code&gt; event. The &lt;code&gt;pagehide&lt;/code&gt;
event fires in all cases where the &lt;code&gt;unload&lt;/code&gt; event currently fires, and it
&lt;em&gt;also&lt;/em&gt; fires when a page is put in the bfcache.&lt;/p&gt;
&lt;p&gt;In fact, &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt; has a &lt;a href=&quot;https://github.com/GoogleChrome/lighthouse/pull/11085&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;no-unload-listeners&lt;/code&gt; audit&lt;/a&gt;, which will warn developers if any JavaScript on their pages (including that from third-party libraries) adds an &lt;code&gt;unload&lt;/code&gt; event listener.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-warn-bg color-state-warn-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block color-state-warn-text&quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Warning sign&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M23 21L12 2 1 21h22zm-12-3v-2h2v2h-2zm0-4h2v-4h-2v4z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Warning&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Never add an &lt;code&gt;unload&lt;/code&gt; event listener! Use the &lt;code&gt;pagehide&lt;/code&gt; event instead. Adding an &lt;code&gt;unload&lt;/code&gt; event listener will make your site slower in Firefox, and the code won&#39;t even run most of the time in Chrome and Safari. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;only-add-beforeunload-listeners-conditionally&quot;&gt;Only add &lt;code&gt;beforeunload&lt;/code&gt; listeners conditionally &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bfcache/#only-add-beforeunload-listeners-conditionally&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;beforeunload&lt;/code&gt; event will not make your pages ineligible for bfcache in
Chrome or Safari, but it will make them ineligible in Firefox, so avoid using it
unless absolutely necessary.&lt;/p&gt;
&lt;p&gt;Unlike the &lt;code&gt;unload&lt;/code&gt; event, however, there are legitimate uses for
&lt;code&gt;beforeunload&lt;/code&gt;. For example, when you want to warn the user that they have
unsaved changes they&#39;ll lose if they leave the page. In this case, it&#39;s
recommended that you only add &lt;code&gt;beforeunload&lt;/code&gt; listeners when a user has unsaved
changes and then remove them immediately after the unsaved changes are saved.&lt;/p&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;worse&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Don&#39;t&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;beforeunload&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pageHasUnsavedChanges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;returnValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Are you sure you want to exit?&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;figcaption class=&quot;compare__caption&quot;&gt;
&lt;p&gt;The code above adds a &lt;code&gt;beforeunload&lt;/code&gt; listener unconditionally.&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;better&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Do&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;beforeUnloadListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;returnValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Are you sure you want to exit?&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// A function that invokes a callback when the page has unsaved changes.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;onPageHasUnsavedChanges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;beforeunload&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; beforeUnloadListener&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// A function that invokes a callback when the page&#39;s unsaved changes are resolved.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;onAllChangesSaved&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;beforeunload&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; beforeUnloadListener&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;figcaption class=&quot;compare__caption&quot;&gt;
&lt;p&gt;The code above only adds the &lt;code&gt;beforeunload&lt;/code&gt; listener when it&#39;s needed (and
removes it when it&#39;s not).&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;minimize-use-of-cache-control-no-store&quot;&gt;Minimize use of &lt;code&gt;Cache-Control: no-store&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bfcache/#minimize-use-of-cache-control-no-store&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Cache-Control: no-store&lt;/code&gt; is an HTTP header web servers can set on responses that instructs the browser not to store the response in any HTTP cache. This should be used for resources containing sensitive user information, for example pages behind a login.&lt;/p&gt;
&lt;p&gt;Though bfcache is not an HTTP cache, historically, when &lt;code&gt;Cache-Control: no-store&lt;/code&gt; is set on the page resource itself (as opposed to any subresource), browsers have chosen not to store the page in bfcache. There is &lt;a href=&quot;https://github.com/fergald/explainer-bfcache-ccns/blob/main/README.md&quot; rel=&quot;noopener&quot;&gt;work currently underway to change this behavior for Chrome&lt;/a&gt; in a privacy-preserving manner, but at present any pages using &lt;code&gt;Cache-Control: no-store&lt;/code&gt; will not be eligible for bfcache.&lt;/p&gt;
&lt;p&gt;Since &lt;code&gt;Cache-Control: no-store&lt;/code&gt; restricts a page&#39;s eligibility for bfcache, it should only be set on pages that contain sensitive information where caching of any sort is never appropriate.&lt;/p&gt;
&lt;p&gt;For pages that wish to always serve up-to-date content—and that content does not contain sensitive information—use &lt;code&gt;Cache-Control: no-cache&lt;/code&gt; or &lt;code&gt;Cache-Control: max-age=0&lt;/code&gt;. These directives instruct the browser to revalidate the content before serving it, and they do not affect a page&#39;s bfcache eligibility.&lt;/p&gt;
&lt;p&gt;Note that when a page is restored from bfcache, it is restored from memory, not from the HTTP cache. As a result, directives like &lt;code&gt;Cache-Control: no-cache&lt;/code&gt; or &lt;code&gt;Cache-Control: max-age=0&lt;/code&gt; are not taken into account, and no revalidation occurs before the content is displayed to the user.&lt;/p&gt;
&lt;p&gt;This is still likely a better user experience, however, as bfcache restores are instant and—since pages do not stay in the bfcache for very long—it&#39;s unlikely that the content is out of date. However, if your content does change minute-by-minute, you can fetch any updates using the &lt;code&gt;pageshow&lt;/code&gt; event, as outlined in the next section.&lt;/p&gt;
&lt;h3 id=&quot;update-stale-or-sensitive-data-after-bfcache-restore&quot;&gt;Update stale or sensitive data after bfcache restore &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bfcache/#update-stale-or-sensitive-data-after-bfcache-restore&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If your site keeps user state—especially any sensitive user information—that
data needs to be updated or cleared after a page is restored from bfcache.&lt;/p&gt;
&lt;p&gt;For example, if a user navigates to a checkout page and then updates their
shopping cart, a back navigation could potentially expose out-of-date
information if a stale page is restored from bfcache.&lt;/p&gt;
&lt;p&gt;Another, more critical example is if a user signs out of a site on a public
computer and the next user clicks the back button. This could potentially expose
private data that the user assumed was cleared when they logged out.&lt;/p&gt;
&lt;p&gt;To avoid situations like this, it&#39;s good to always update the page after a
&lt;code&gt;pageshow&lt;/code&gt; event if &lt;code&gt;event.persisted&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The following code checks for the presence of a site-specific cookie in the
&lt;code&gt;pageshow&lt;/code&gt; event and reloads if the cookie is not found:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;pageshow&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persisted &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cookie&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;my-cookie&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Force a reload if the user has logged out.&lt;/span&gt;&lt;br /&gt;    location&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reload&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;avoid-windowopener-references&quot;&gt;Avoid &lt;code&gt;window.opener&lt;/code&gt; references &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bfcache/#avoid-windowopener-references&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In some browsers (including Chromium-based browsers) if a page was opened using
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Window/open&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;window.open()&lt;/code&gt;&lt;/a&gt;
or (in &lt;a href=&quot;https://crbug.com/898942&quot; rel=&quot;noopener&quot;&gt;Chromium-based browsers prior to version 88&lt;/a&gt;) from a link with
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/a#target&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;target=_blank&lt;/code&gt;&lt;/a&gt;—without
specifying
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Link_types/noopener&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;rel=&amp;quot;noopener&amp;quot;&lt;/code&gt;&lt;/a&gt;—then
the opening page will have a reference to the window object of the opened page.&lt;/p&gt;
&lt;p&gt;In addition to &lt;a href=&quot;https://mathiasbynens.github.io/rel-noopener/&quot; rel=&quot;noopener&quot;&gt;being a security
risk&lt;/a&gt;, a page with a non-null
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Window/opener&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;window.opener&lt;/code&gt;&lt;/a&gt;
reference cannot safely be put into the bfcache because that could break any
pages attempting to access it.&lt;/p&gt;
&lt;p&gt;As a result, it&#39;s best to avoid creating &lt;code&gt;window.opener&lt;/code&gt; references. You can do this by using
&lt;code&gt;rel=&amp;quot;noopener&amp;quot;&lt;/code&gt; whenever possible. If your site requires opening a window and
controlling it through
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Window/postMessage&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;window.postMessage()&lt;/code&gt;&lt;/a&gt;
or directly referencing the window object, neither the opened window nor the
opener will be eligible for the bfcache.&lt;/p&gt;
&lt;h3 id=&quot;always-close-open-connections-before-the-user-navigates-away&quot;&gt;Always close open connections before the user navigates away &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bfcache/#always-close-open-connections-before-the-user-navigates-away&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As mentioned above, when a page is put into the bfcache all scheduled JavaScript
tasks are paused and then resumed when the page is taken out of the cache.&lt;/p&gt;
&lt;p&gt;If these scheduled JavaScript tasks are only accessing DOM APIs—or other APIs
isolated to just the current page—then pausing these tasks while the page is not
visible to the user is not going to cause any problems.&lt;/p&gt;
&lt;p&gt;However, if these tasks are connected to APIs that are also accessible from
other pages in the same origin (for example: IndexedDB, Web Locks, WebSockets,
etc.) this can be problematic because pausing these tasks may prevent code in
other tabs from running.&lt;/p&gt;
&lt;p&gt;As a result, some browsers will not attempt to put a page in bfcache in the
following scenarios:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pages with an open &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/IDBOpenDBRequest&quot; rel=&quot;noopener&quot;&gt;IndexedDB
connection&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Pages with in-progress
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Fetch_API&quot; rel=&quot;noopener&quot;&gt;fetch()&lt;/a&gt; or
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/XMLHttpRequest&quot; rel=&quot;noopener&quot;&gt;XMLHttpRequest&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Pages with an open
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/WebSocket&quot; rel=&quot;noopener&quot;&gt;WebSocket&lt;/a&gt; or
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/WebRTC_API&quot; rel=&quot;noopener&quot;&gt;WebRTC&lt;/a&gt;
connection&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If your page is using any of these APIs, it&#39;s best to always close connections
and remove or disconnect observers during the &lt;code&gt;pagehide&lt;/code&gt; or &lt;code&gt;freeze&lt;/code&gt; event. That
will allow the browser to safely cache the page without the risk of it affecting
other open tabs.&lt;/p&gt;
&lt;p&gt;Then, if the page is restored from the bfcache, you can re-open or re-connect to
those APIs (in the &lt;code&gt;pageshow&lt;/code&gt; or &lt;code&gt;resume&lt;/code&gt; event).&lt;/p&gt;
&lt;p&gt;The following example shows how to ensure your pages are eligible for bfcache
when using IndexedDB by closing an open connection in the &lt;code&gt;pagehide&lt;/code&gt; event
listener:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; dbPromise&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;openDB&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;dbPromise&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    dbPromise &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; req &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; indexedDB&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;my-db&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onupgradeneeded&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createObjectStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;keyval&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onerror&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onsuccess&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; dbPromise&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Close the connection to the database when the user is leaving.&lt;/span&gt;&lt;br /&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;pagehide&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dbPromise&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    dbPromise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; db&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    dbPromise &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Open the connection when the page is loaded or restored from bfcache.&lt;/span&gt;&lt;br /&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;pageshow&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;openDB&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;test-to-ensure-your-pages-are-cacheable&quot;&gt;Test to ensure your pages are cacheable &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bfcache/#test-to-ensure-your-pages-are-cacheable&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Chrome DevTools can help you test your pages to ensure they&#39;re optimized for
bfcache, and identify any issues that may be preventing them from being
eligible.&lt;/p&gt;
&lt;p&gt;To test a particular page, navigate to it in Chrome and then in DevTools go to
&lt;strong&gt;Application&lt;/strong&gt; &amp;gt; &lt;strong&gt;Back-forward Cache&lt;/strong&gt;. Next click the &lt;strong&gt;Run Test&lt;/strong&gt; button and
DevTools will attempt to navigate away and back to determine whether the page
could be restored from bfcache.&lt;/p&gt;
&lt;img alt=&quot;Back-forward cache panel in DevTools&quot; decoding=&quot;async&quot; height=&quot;313&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/QafTzULUNflaSh77zBgT.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/QafTzULUNflaSh77zBgT.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/QafTzULUNflaSh77zBgT.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/QafTzULUNflaSh77zBgT.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/QafTzULUNflaSh77zBgT.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/QafTzULUNflaSh77zBgT.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/QafTzULUNflaSh77zBgT.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/QafTzULUNflaSh77zBgT.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/QafTzULUNflaSh77zBgT.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/QafTzULUNflaSh77zBgT.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/QafTzULUNflaSh77zBgT.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/QafTzULUNflaSh77zBgT.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/QafTzULUNflaSh77zBgT.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/QafTzULUNflaSh77zBgT.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/QafTzULUNflaSh77zBgT.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/QafTzULUNflaSh77zBgT.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/QafTzULUNflaSh77zBgT.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/QafTzULUNflaSh77zBgT.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The Back/forward Cache feature in DevTools is currently in active development. We strongly encourage developers to test their pages in Chrome Canary to ensure they&#39;re running the latest version of DevTools and getting the most up-to-date bfcache recommendations. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;If successful, the panel will report &amp;quot;Restored from back-forward cache&amp;quot;:&lt;/p&gt;
&lt;img alt=&quot;DevTools reporting a page was successfully restored from bfcache&quot; decoding=&quot;async&quot; height=&quot;313&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/vPwN0z95ZBTiwZIpdZT4.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/vPwN0z95ZBTiwZIpdZT4.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/vPwN0z95ZBTiwZIpdZT4.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/vPwN0z95ZBTiwZIpdZT4.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/vPwN0z95ZBTiwZIpdZT4.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/vPwN0z95ZBTiwZIpdZT4.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/vPwN0z95ZBTiwZIpdZT4.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/vPwN0z95ZBTiwZIpdZT4.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/vPwN0z95ZBTiwZIpdZT4.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/vPwN0z95ZBTiwZIpdZT4.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/vPwN0z95ZBTiwZIpdZT4.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/vPwN0z95ZBTiwZIpdZT4.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/vPwN0z95ZBTiwZIpdZT4.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/vPwN0z95ZBTiwZIpdZT4.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/vPwN0z95ZBTiwZIpdZT4.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/vPwN0z95ZBTiwZIpdZT4.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/vPwN0z95ZBTiwZIpdZT4.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/vPwN0z95ZBTiwZIpdZT4.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;If unsuccessful, the panel will indicate the page was not restored and list the
reason why. If the reason is something you as a developer can address, that
will also be indicated:&lt;/p&gt;
&lt;img alt=&quot;DevTools reporting failure to restore a page from bfcache&quot; decoding=&quot;async&quot; height=&quot;313&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/ji3ew4DoP6joKdJvtGwa.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/ji3ew4DoP6joKdJvtGwa.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/ji3ew4DoP6joKdJvtGwa.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/ji3ew4DoP6joKdJvtGwa.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/ji3ew4DoP6joKdJvtGwa.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/ji3ew4DoP6joKdJvtGwa.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/ji3ew4DoP6joKdJvtGwa.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/ji3ew4DoP6joKdJvtGwa.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/ji3ew4DoP6joKdJvtGwa.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/ji3ew4DoP6joKdJvtGwa.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/ji3ew4DoP6joKdJvtGwa.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/ji3ew4DoP6joKdJvtGwa.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/ji3ew4DoP6joKdJvtGwa.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/ji3ew4DoP6joKdJvtGwa.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/ji3ew4DoP6joKdJvtGwa.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/ji3ew4DoP6joKdJvtGwa.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/ji3ew4DoP6joKdJvtGwa.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/ji3ew4DoP6joKdJvtGwa.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;In the screenshot above, the use of an &lt;code&gt;unload&lt;/code&gt; event listener is
&lt;a href=&quot;https://web.dev/bfcache/#never-use-the-unload-event&quot;&gt;preventing&lt;/a&gt; the page from being eligible
for bfcache. You can fix that by switching from &lt;code&gt;unload&lt;/code&gt; to using &lt;code&gt;pagehide&lt;/code&gt; instead:&lt;/p&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;worse&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Don&#39;t&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;unload&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;better&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Do&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;pagehide&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;Lighthouse 10.0 also &lt;a href=&quot;https://developer.chrome.com/blog/lighthouse-10-0/#bfcache&quot; rel=&quot;noopener&quot;&gt;added a bfcache audit&lt;/a&gt;, which performs a similar test to the one DevTools does, and also provides reasons why the page is ineligible if the audit fails. Take a look at the &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/bf-cache/&quot; rel=&quot;noopener&quot;&gt;bfcache audit&#39;s docs&lt;/a&gt; for more information.&lt;/p&gt;
&lt;h2 id=&quot;how-bfcache-affects-analytics-and-performance-measurement&quot;&gt;How bfcache affects analytics and performance measurement &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bfcache/#how-bfcache-affects-analytics-and-performance-measurement&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you track visits to your site with an analytics tool, you will likely notice
a decrease in the total number of pageviews reported as Chrome continues to
enable bfcache for more users.&lt;/p&gt;
&lt;p&gt;In fact, you&#39;re likely &lt;em&gt;already&lt;/em&gt; underreporting pageviews from other browsers
that implement bfcache since most of the popular analytics libraries do not
track bfcache restores as new pageviews.&lt;/p&gt;
&lt;p&gt;If you don&#39;t want your pageview counts to go down due to Chrome enabling
bfcache, you can report bfcache restores as pageviews (recommended) by listening
to the &lt;code&gt;pageshow&lt;/code&gt; event and checking the &lt;code&gt;persisted&lt;/code&gt; property.&lt;/p&gt;
&lt;p&gt;The following example shows how to do this with Google Analytics; the logic
should be similar for other analytics tools:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Send a pageview when the page is first loaded.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;gtag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;event&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;page_view&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;pageshow&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Send another pageview if the page is restored from bfcache.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persisted&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token function&quot;&gt;gtag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;event&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;page_view&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;measuring-your-bfcache-hit-ratio&quot;&gt;Measuring your bfcache hit ratio &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bfcache/#measuring-your-bfcache-hit-ratio&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You may also wish to track whether the bfcache was used, to help identify pages
that are not utilizing the bfcache. For example, with an event:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;pageshow&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// You can measure bfcache hit rate by tracking all bfcache restores and&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// other back/forward navigations via a separate event.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; navigationType &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; performance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEntriesByType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;navigation&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persisted &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; navigationType &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;back_forward&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token function&quot;&gt;gtag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;event&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;back_forward_navigation&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token string-property property&quot;&gt;&#39;isBFCache&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persisted&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;It is important to realize that there are a number of scenarios, outside
of the site owners control, when a Back/Forward navigation will not use
the bfcache, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;when the user quits the browser and starts it again&lt;/li&gt;
&lt;li&gt;when the user duplicates a tab&lt;/li&gt;
&lt;li&gt;when the user closes a tab and uncloses it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even without those exclusions the bfcache will be discarded after a period to conserve memory.&lt;/p&gt;
&lt;p&gt;So, website owners should not be expecting a 100% bfcache hit ratio for all
&lt;code&gt;back_forward&lt;/code&gt; navigations. However, measuring their ratio can be useful to
identify pages where the page itself is preventing bfcache usage for a high
proportion of back and forward navigations.&lt;/p&gt;
&lt;p&gt;The Chrome team is working on a
&lt;a href=&quot;https://github.com/rubberyuzu/bfcache-not-retored-reason/blob/main/NotRestoredReason.md&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;NotRestoredReasons&lt;/code&gt; API&lt;/a&gt;
to help expose the reasons why the bfcache was not used to help developers
understand the reasoning the cache was not used and if this is something they
can work on to improve for their sites.&lt;/p&gt;
&lt;h3 id=&quot;performance-measurement&quot;&gt;Performance measurement &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bfcache/#performance-measurement&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;bfcache can also negatively affect performance metrics collected &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/#in-the-field&quot;&gt;in the
field&lt;/a&gt;, specifically metrics
that measure page load times.&lt;/p&gt;
&lt;p&gt;Since bfcache navigations restore an existing page rather than initiate a new
page load, the total number of page loads collected will decrease when bfcache
is enabled. What&#39;s critical, though, is that the page loads being replaced by
bfcache restores would likely have been some of the fastest page loads in your
dataset. This is because back and forward navigations, by definition, are repeat
visits, and repeat page loads are generally faster than page loads from first
time visitors (due to &lt;a href=&quot;https://web.dev/http-cache/&quot;&gt;HTTP caching&lt;/a&gt;, as mentioned earlier).&lt;/p&gt;
&lt;p&gt;The result is fewer fast page loads in your dataset, which will likely skew the
distribution slower—despite the fact that the performance experienced by the
user has probably improved!&lt;/p&gt;
&lt;p&gt;There are a few ways to deal with this issue. One is to annotate all page load
metrics with their respective &lt;a href=&quot;https://www.w3.org/TR/navigation-timing-2/#sec-performance-navigation-types&quot; rel=&quot;noopener&quot;&gt;navigation
type&lt;/a&gt;:
&lt;code&gt;navigate&lt;/code&gt;, &lt;code&gt;reload&lt;/code&gt;, &lt;code&gt;back_forward&lt;/code&gt;, or &lt;code&gt;prerender&lt;/code&gt;. This will allow you to
continue to monitor your performance within these navigation types—even if the
overall distribution skews negative. This approach is recommended for
non-user-centric page load metrics like &lt;a href=&quot;https://web.dev/ttfb/&quot;&gt;Time to First Byte
(TTFB)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For user-centric metrics like the &lt;a href=&quot;https://web.dev/vitals/&quot;&gt;Core Web Vitals&lt;/a&gt;, a better option
is to report a value that more accurately represents what the user experiences.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; The &lt;code&gt;back_forward&lt;/code&gt; navigation type in the &lt;a href=&quot;https://www.w3.org/TR/navigation-timing-2/#sec-performance-navigation-types&quot;&gt;Navigation Timing API&lt;/a&gt; is not to be confused with bfcache restores. The Navigation Timing API only annotates page loads, whereas bfcache restores are re-using a page loaded from a previous navigation. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;impact-on-core-web-vitals&quot;&gt;Impact on Core Web Vitals &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bfcache/#impact-on-core-web-vitals&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/vitals/&quot;&gt;Core Web Vitals&lt;/a&gt; measure the user&#39;s experience of a web page across a
variety of dimensions (loading speed, interactivity, visual stability), and
since users experience bfcache restores as faster navigations than traditional
page loads, it&#39;s important that the Core Web Vitals metrics reflect this. After
all, a user doesn&#39;t care whether or not bfcache was enabled, they just care that
the navigation was fast!&lt;/p&gt;
&lt;p&gt;Tools like the &lt;a href=&quot;https://developer.chrome.com/docs/crux/&quot; rel=&quot;noopener&quot;&gt;Chrome User Experience
Report&lt;/a&gt;,
that collect and report on the Core Web Vitals metrics treat bfcache restores as
separate page visits in their dataset.&lt;/p&gt;
&lt;p&gt;And while there aren&#39;t (yet) dedicated web performance APIs for measuring these
metrics after bfcache restores, their values can be approximated using existing
web APIs.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt;, you can use the delta between
the &lt;code&gt;pageshow&lt;/code&gt; event&#39;s timestamp and the timestamp of the next painted frame
(since all elements in the frame will be painted at the same time). Note
that in the case of a bfcache restore, LCP and FCP will be the same.&lt;/li&gt;
&lt;li&gt;For &lt;a href=&quot;https://web.dev/fid/&quot;&gt;First Input Delay (FID)&lt;/a&gt;, you can re-add the event listeners
(the same ones used by the &lt;a href=&quot;https://github.com/GoogleChromeLabs/first-input-delay&quot; rel=&quot;noopener&quot;&gt;FID
polyfill&lt;/a&gt;) in the
&lt;code&gt;pageshow&lt;/code&gt; event, and report FID as the delay of the first input after the
bfcache restore.&lt;/li&gt;
&lt;li&gt;For &lt;a href=&quot;https://web.dev/cls/&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt;, you can continue to keep using
your existing Performance Observer; all you have to do is reset the current
CLS value to 0.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more details on how bfcache affects each metric, refer to the individual
Core Web Vitals &lt;a href=&quot;https://web.dev/vitals/#core-web-vitals&quot;&gt;metric guides pages&lt;/a&gt;. And for a
specific example of how to implement bfcache versions of these metrics in code,
refer to the &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals/pull/87&quot; rel=&quot;noopener&quot;&gt;PR adding them to the web-vitals JS
library&lt;/a&gt;.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; As of &lt;code&gt;v1&lt;/code&gt;, the &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals&quot;&gt;web-vitals&lt;/a&gt; JavaScript library &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals/pull/87&quot;&gt;supports bfcache restores&lt;/a&gt; in the metrics it reports. Developers using &lt;code&gt;v1&lt;/code&gt; or greater should not need to update their code. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;additional-resources&quot;&gt;Additional Resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bfcache/#additional-resources&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/Firefox/Releases/1.5/Using_Firefox_1.5_caching&quot; rel=&quot;noopener&quot;&gt;Firefox
Caching&lt;/a&gt;
&lt;em&gt;(bfcache in Firefox)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webkit.org/blog/427/webkit-page-cache-i-the-basics/&quot; rel=&quot;noopener&quot;&gt;Page Cache&lt;/a&gt;
&lt;em&gt;(bfcache in Safari)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.google.com/document/d/1JtDCN9A_1UBlDuwkjn1HWxdhQ1H2un9K4kyPLgBqJUc/edit?usp=sharing&quot; rel=&quot;noopener&quot;&gt;Back/forward cache: web exposed
behavior&lt;/a&gt;
&lt;em&gt;(bfcache differences across browsers)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://back-forward-cache-tester.glitch.me/?persistent_logs=1&quot; rel=&quot;noopener&quot;&gt;bfcache
tester&lt;/a&gt;
&lt;em&gt;(test how different APIs and events affect bfcache in browsers)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.smashingmagazine.com/2022/05/performance-game-changer-back-forward-cache/&quot; rel=&quot;noopener&quot;&gt;Performance Game Changer: Browser Back/Forward Cache&lt;/a&gt;
&lt;em&gt;(a case study from Smashing Magazine showing dramatic Core Web Vitals improvements by enabling bfcache)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Philip Walton</name>
    </author><author>
      <name>Barry Pollard</name>
    </author>
  </entry>
  
  <entry>
    <title>Optimize Largest Contentful Paint</title>
    <link href="https://web.dev/optimize-lcp/"/>
    <updated>2020-05-05T00:00:00Z</updated>
    <id>https://web.dev/optimize-lcp/</id>
    <content type="html" mode="escaped">&lt;p&gt;&lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt; is one of the three &lt;a href=&quot;https://web.dev/vitals/#core-web-vitals&quot;&gt;Core Web Vitals&lt;/a&gt; metrics, and it represents how quickly the main content of a web page is loaded. Specifically, LCP measures the time from when the user initiates loading the page until the largest image or text block is rendered within the viewport.&lt;/p&gt;
&lt;p&gt;To provide a good user experience, &lt;strong&gt;sites should strive to have an LCP of 2.5 seconds or less for at least 75% of page visits.&lt;/strong&gt;&lt;/p&gt;
&lt;figure&gt;
  &lt;picture&gt;
    &lt;source srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/elqsdYqQEefWJbUM2qMO.svg&quot; media=&quot;(min-width: 640px)&quot; width=&quot;800&quot; height=&quot;200&quot; /&gt;
    &lt;img alt=&quot;Good LCP values are 2.5 seconds or less, poor values are greater than 4.0 seconds, and anything in between needs improvement&quot; decoding=&quot;async&quot; height=&quot;480&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/8ZW8LQsagLih1ZZoOmMR.svg&quot; width=&quot;640&quot; /&gt;
  &lt;/picture&gt;
&lt;/figure&gt;
&lt;p&gt;There are a number of factors that can affect how quickly the browser is able to load and render a web page, and delays across any of them can have a significant impact on LCP.&lt;/p&gt;
&lt;p&gt;It&#39;s rare that a quick fix to a single part of a page will result in a meaningful improvement to LCP. To improve LCP you have to look at the entire loading process and make sure every step along the way is optimized.&lt;/p&gt;
&lt;h2 id=&quot;understanding-your-lcp-metric&quot;&gt;Understanding your LCP metric &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#understanding-your-lcp-metric&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before optimizing LCP, developers should seek to understand if they even have an LCP issue, and the extent of any such issue.&lt;/p&gt;
&lt;p&gt;LCP can be measured in a number of tools and not all of these measure LCP in the same way. To understand LCP of real users, we should look at what real users are experiencing, rather than what a lab-based tool like &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt; or local testing shows. These lab-based tools can give a wealth of information to explain and help you improve LCP, but be aware that lab tests alone may not be entirely representative of what your actual users experience.&lt;/p&gt;
&lt;p&gt;LCP data based on real users can be surfaced from Real User Monitoring (RUM) tools installed on a site, or via the &lt;a href=&quot;https://developer.chrome.com/docs/crux/&quot; rel=&quot;noopener&quot;&gt;Chrome User Experience Report (CrUX)&lt;/a&gt; which collect anonymous data from real Chrome users for millions of websites.&lt;/p&gt;
&lt;h3 id=&quot;using-pagespeed-insights-crux-lcp-data&quot;&gt;Using PageSpeed Insights CrUX LCP data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#using-pagespeed-insights-crux-lcp-data&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://pagespeed.web.dev/&quot; rel=&quot;noopener&quot;&gt;PageSpeed Insights&lt;/a&gt; provides access to CrUX data in the top section labeled &lt;strong&gt;Discover what your real users are experiencing&lt;/strong&gt;. More detailed lab-based data is available in the bottom section labeled &lt;strong&gt;Diagnose performance issues&lt;/strong&gt;. If CrUX data is available for your website, always concentrate on the real user data first.&lt;/p&gt;
&lt;img alt=&quot;CrUX data shown in PageSpeed Insights&quot; decoding=&quot;async&quot; height=&quot;478&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Where CrUX does not provide data (for example, a page with insufficient traffic to get page-level data), CrUX should be supplemented with RUM data collected using JavaScript APIs running on the web page. This can also provide much more data than CrUX can expose as a public dataset. Later in this guide we will explain how to collect this data using JavaScript. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;PageSpeed Insights shows up to four different CrUX data:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Mobile&lt;/strong&gt; data for &lt;strong&gt;This URL&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Desktop&lt;/strong&gt; data for &lt;strong&gt;This URL&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mobile&lt;/strong&gt; data for the whole &lt;strong&gt;Origin&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Desktop&lt;/strong&gt; data for the whole &lt;strong&gt;Origin&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These can be toggled in the controls at the top, and top right-hand side of this section. Be aware that where a URL does not have sufficient data to be shown at the URL level—but does have data for the origin—PageSpeed Insights will automatically show this.&lt;/p&gt;
&lt;img alt=&quot;PageSpeed Insight&amp;#x27;s falling back to origin-level data where url-level data is not available&quot; decoding=&quot;async&quot; height=&quot;295&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wWQxf1DbjElItJTcJsBn.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wWQxf1DbjElItJTcJsBn.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wWQxf1DbjElItJTcJsBn.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wWQxf1DbjElItJTcJsBn.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wWQxf1DbjElItJTcJsBn.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wWQxf1DbjElItJTcJsBn.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wWQxf1DbjElItJTcJsBn.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wWQxf1DbjElItJTcJsBn.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wWQxf1DbjElItJTcJsBn.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wWQxf1DbjElItJTcJsBn.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wWQxf1DbjElItJTcJsBn.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wWQxf1DbjElItJTcJsBn.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wWQxf1DbjElItJTcJsBn.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wWQxf1DbjElItJTcJsBn.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wWQxf1DbjElItJTcJsBn.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wWQxf1DbjElItJTcJsBn.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wWQxf1DbjElItJTcJsBn.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wWQxf1DbjElItJTcJsBn.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;The LCP for the whole origin may be very different to an individual page&#39;s LCP depending on how the LCP is loaded on that page compared to other pages on that origin. It can also be affected by how visitors navigate to these pages. Home pages tend to be visted by new users and so may often be loaded &amp;quot;cold&amp;quot;, without any cached content and so are often the slowest pages on a website.&lt;/p&gt;
&lt;p&gt;Looking at the four different categories of CrUX data can help you understand whether an LCP issue is specific to this page, or a more general site-wide issue. Similarly, it can show which device types have LCP issues.&lt;/p&gt;
&lt;h3 id=&quot;using-pagespeed-insights-crux-supplementary-metrics&quot;&gt;Using PageSpeed Insights CrUX supplementary metrics &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#using-pagespeed-insights-crux-supplementary-metrics&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Those looking to optimize LCP should also use the &lt;a href=&quot;https://web.dev/fcp/&quot;&gt;First Contentful Paint (FCP)&lt;/a&gt; and &lt;a href=&quot;https://web.dev/ttfb/&quot;&gt;Time to First Byte (TTFB)&lt;/a&gt; timings, which are good diagnostic metrics that can provide valuable insights into LCP.&lt;/p&gt;
&lt;p&gt;TTFB is the time when the visitor starting to navigate to a page (for example, clicking on a link), until the first bytes of the HTML document are received. A high TTFB can make achieving a 2.5 second LCP challenging, or even impossible.&lt;/p&gt;
&lt;p&gt;A high TTFB can be due to mutiple server redirects, visitors located far away from the nearest site server, visitors on poor network conditions, or an inability to use cached content due to query parameters.&lt;/p&gt;
&lt;p&gt;Once a page starts rendering, there may be an initial paint (for example, the background color), followed by some content appearing (for example, the site header). The appearance of the initial content is measured by FCP. The delta between FCP and other metrics can be very telling.&lt;/p&gt;
&lt;p&gt;A large delta between TTFB and FCP could indicate that the browser needs to download a lot of render-blocking assets. It can also be a sign it must complete a lot of work to render any meaningful content—a classic sign of a site that relies heavily on client-side rendering.&lt;/p&gt;
&lt;p&gt;A large delta between FCP and LCP indicates that the LCP resource is either not immediately available for the browser to prioritize (for example, text or images that are managed by JavaScript rather than being available in the initial HTML), or that the browser is completing other work before it can display the LCP content.&lt;/p&gt;
&lt;h3 id=&quot;using-pagespeed-insights-lighthouse-data&quot;&gt;Using PageSpeed Insights Lighthouse data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#using-pagespeed-insights-lighthouse-data&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Lighthouse section of PageSpeed Insights offers some guidance to improving LCP, but first you should check if the LCP given is broadly inline with real user data provided by CrUX.&lt;/p&gt;
&lt;p&gt;If Lighthouse is showing no LCP issue, but the CrUX data is, then any Lighthouse suggestions may not be relevant. The opposite is also true—if Lighthouse is showing a really poor LCP time, but CrUX data is showing your users mostly have a good LCP, then you may wish to consider the priority of optimizing LCP further, or if time is better spent on other performance improvements. Also be sure to check that the CrUX data is for this page and not for the full origin as detailed above.&lt;/p&gt;
&lt;p&gt;If both sources of data are showing an LCP that should be improved than the Lighthouse section can provide valuable guidance on ways to improve LCP. Use the LCP filter to only show audits relevant to LCP:&lt;/p&gt;
&lt;img alt=&quot;Lighthouse LCP Opportunities and Diagnostics&quot; decoding=&quot;async&quot; height=&quot;498&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/himAvC1GMr7K0kcbvG4F.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/himAvC1GMr7K0kcbvG4F.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/himAvC1GMr7K0kcbvG4F.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/himAvC1GMr7K0kcbvG4F.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/himAvC1GMr7K0kcbvG4F.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/himAvC1GMr7K0kcbvG4F.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/himAvC1GMr7K0kcbvG4F.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/himAvC1GMr7K0kcbvG4F.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/himAvC1GMr7K0kcbvG4F.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/himAvC1GMr7K0kcbvG4F.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/himAvC1GMr7K0kcbvG4F.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/himAvC1GMr7K0kcbvG4F.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/himAvC1GMr7K0kcbvG4F.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/himAvC1GMr7K0kcbvG4F.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/himAvC1GMr7K0kcbvG4F.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/himAvC1GMr7K0kcbvG4F.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/himAvC1GMr7K0kcbvG4F.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/himAvC1GMr7K0kcbvG4F.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;As well as the &lt;strong&gt;Opportunities&lt;/strong&gt; to improve, there is &lt;strong&gt;Diagnostic&lt;/strong&gt; information that may provide more information to help diagnose the issue. The &lt;strong&gt;Largest Contentful Paint element&lt;/strong&gt; diagnostic shows a useful breakdown of the various timings that made up the LCP:&lt;/p&gt;
&lt;img alt=&quot;Lighthouse LCP phases&quot; decoding=&quot;async&quot; height=&quot;474&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/KhIuP2CNToNeGa33w48K.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/KhIuP2CNToNeGa33w48K.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/KhIuP2CNToNeGa33w48K.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/KhIuP2CNToNeGa33w48K.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/KhIuP2CNToNeGa33w48K.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/KhIuP2CNToNeGa33w48K.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/KhIuP2CNToNeGa33w48K.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/KhIuP2CNToNeGa33w48K.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/KhIuP2CNToNeGa33w48K.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/KhIuP2CNToNeGa33w48K.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/KhIuP2CNToNeGa33w48K.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/KhIuP2CNToNeGa33w48K.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/KhIuP2CNToNeGa33w48K.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/KhIuP2CNToNeGa33w48K.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/KhIuP2CNToNeGa33w48K.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/KhIuP2CNToNeGa33w48K.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/KhIuP2CNToNeGa33w48K.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/KhIuP2CNToNeGa33w48K.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;We will delve into these sub-parts next.&lt;/p&gt;
&lt;h2 id=&quot;lcp-breakdown&quot;&gt;LCP breakdown &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#lcp-breakdown&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Optimizing for LCP can be a more complex task when PageSpeed Insights does not give you the answer on how to improve this metric. With complex tasks it&#39;s generally better to break them down into smaller, more manageable tasks and address each separately. This guide will present a methodology for how to break down LCP into its most critical sub-parts and then present specific recommendations and best practices for how to optimize each part.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; For a visual overview of the context presented in this section, see &lt;a href=&quot;https://youtu.be/fWoI9DXmpdk&quot;&gt;A Deep Dive into Optimizing LCP&lt;/a&gt; from Google I/O 2022: &lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;fWoI9DXmpdk&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt; &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Most page loads typically include a number of network requests, but for the purposes of identifying opportunities to improve LCP, you should start by looking at just two:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The initial HTML document&lt;/li&gt;
&lt;li&gt;The LCP resource (if applicable)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;While other requests on the page can affect LCP, these two requests—specifically the times when the LCP resource begins and ends—reveal whether or not your page is optimized for LCP.&lt;/p&gt;
&lt;p&gt;To identify the LCP resource, you can use developer tools (such as PageSpeed Insights discussed above, &lt;a href=&quot;https://developer.chrome.com/docs/devtools/&quot; rel=&quot;noopener&quot;&gt;Chrome DevTools&lt;/a&gt;, or &lt;a href=&quot;https://webpagetest.org/&quot; rel=&quot;noopener&quot;&gt;WebPageTest&lt;/a&gt;) to determine the &lt;a href=&quot;https://web.dev/lcp/#what-elements-are-considered&quot;&gt;LCP element&lt;/a&gt;. From there, you can match the URL (again, if applicable) loaded by the element on a &lt;a href=&quot;https://developer.chrome.com/docs/devtools/network/reference/&quot; rel=&quot;noopener&quot;&gt;network waterfall&lt;/a&gt; of all resources loaded by the page.&lt;/p&gt;
&lt;p&gt;For example, the following visualization shows these resources highlighted on a network waterfall diagram from a typical page load, where the LCP element requires an image request to render.&lt;/p&gt;
&lt;img alt=&quot;A network waterfall with the HTML and LCP resources highlighted&quot; decoding=&quot;async&quot; height=&quot;375&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/96YFhl0GQYIKFnJeOsVW.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/96YFhl0GQYIKFnJeOsVW.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/96YFhl0GQYIKFnJeOsVW.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/96YFhl0GQYIKFnJeOsVW.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/96YFhl0GQYIKFnJeOsVW.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/96YFhl0GQYIKFnJeOsVW.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/96YFhl0GQYIKFnJeOsVW.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/96YFhl0GQYIKFnJeOsVW.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/96YFhl0GQYIKFnJeOsVW.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/96YFhl0GQYIKFnJeOsVW.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/96YFhl0GQYIKFnJeOsVW.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/96YFhl0GQYIKFnJeOsVW.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/96YFhl0GQYIKFnJeOsVW.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/96YFhl0GQYIKFnJeOsVW.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/96YFhl0GQYIKFnJeOsVW.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/96YFhl0GQYIKFnJeOsVW.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/96YFhl0GQYIKFnJeOsVW.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/96YFhl0GQYIKFnJeOsVW.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;For a well-optimized page, you want your LCP resource request to start loading as early as it can, and you want the LCP element to render as quickly as possible after the LCP resource finishes loading. To help visualize whether or not a particular page is following this principle, you can break down the total LCP time into the following sub-parts:&lt;/p&gt;
&lt;img alt=&quot;A breakdown of LCP showing the four individual sub-parts&quot; decoding=&quot;async&quot; height=&quot;450&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/4AriEgko87GR1iZSgOou.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/4AriEgko87GR1iZSgOou.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/4AriEgko87GR1iZSgOou.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/4AriEgko87GR1iZSgOou.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/4AriEgko87GR1iZSgOou.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/4AriEgko87GR1iZSgOou.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/4AriEgko87GR1iZSgOou.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/4AriEgko87GR1iZSgOou.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/4AriEgko87GR1iZSgOou.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/4AriEgko87GR1iZSgOou.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/4AriEgko87GR1iZSgOou.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/4AriEgko87GR1iZSgOou.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/4AriEgko87GR1iZSgOou.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/4AriEgko87GR1iZSgOou.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/4AriEgko87GR1iZSgOou.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/4AriEgko87GR1iZSgOou.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/4AriEgko87GR1iZSgOou.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/4AriEgko87GR1iZSgOou.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;This table explains each of these LCP sub-parts in more detail:&lt;/p&gt;
&lt;div class=&quot;table-wrapper scrollbar&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;LCP sub-part&lt;/th&gt;
        &lt;th&gt;Description&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;Time to first byte (TTFB)&lt;/td&gt;
        &lt;td&gt;The time from when the user initiates loading the page until when the browser receives the first byte of the HTML document response. (See the &lt;a href=&quot;https://web.dev/ttfb/&quot;&gt;TTFB&lt;/a&gt; metric doc for more details.)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Resource load delay&lt;/td&gt;
        &lt;td&gt;The delta between TTFB and when the browser starts loading the LCP resource. &lt;em&gt;*&lt;/em&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Resource load time&lt;/td&gt;
        &lt;td&gt;The time it takes to load the LCP resource itself. &lt;em&gt;*&lt;/em&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Element render delay&lt;/td&gt;
        &lt;td&gt;The delta between when the LCP resource finishes loading until the LCP element is fully rendered.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;caption&gt;* If the LCP element does not require a resource load to render (for example, if the element is a text node rendered with a system font), this time will be 0.&lt;/caption&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Every single page can have its LCP value broken down into these four sub-parts. There is no overlap or gap between them, and collectively they add up to the full LCP time.&lt;/p&gt;
&lt;p&gt;When optimizing LCP, it&#39;s helpful to try to optimize these sub-parts individually. But it&#39;s also important to keep in mind that you need to optimize all of them. In some cases, an optimization applied to one part will not improve LCP, it will just shift the time saved to another part.&lt;/p&gt;
&lt;p&gt;For example, in the earlier network waterfall, if you reduced the file size of our image by compressing it more or switching to a more optimal format (such as AVIF or WebP), that would reduce the &lt;strong&gt;resource load time&lt;/strong&gt;, but it would not actually improve LCP because the time would just shift to the &lt;strong&gt;element render delay&lt;/strong&gt; sub-part:&lt;/p&gt;
&lt;img alt=&quot;The same breakdown of LCP shown earlier where the resource load time sub-part is shortened but the overall LCP time remains the same.&quot; decoding=&quot;async&quot; height=&quot;450&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/M3JgFnahW8pPb9o1lf2r.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/M3JgFnahW8pPb9o1lf2r.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/M3JgFnahW8pPb9o1lf2r.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/M3JgFnahW8pPb9o1lf2r.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/M3JgFnahW8pPb9o1lf2r.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/M3JgFnahW8pPb9o1lf2r.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/M3JgFnahW8pPb9o1lf2r.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/M3JgFnahW8pPb9o1lf2r.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/M3JgFnahW8pPb9o1lf2r.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/M3JgFnahW8pPb9o1lf2r.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/M3JgFnahW8pPb9o1lf2r.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/M3JgFnahW8pPb9o1lf2r.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/M3JgFnahW8pPb9o1lf2r.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/M3JgFnahW8pPb9o1lf2r.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/M3JgFnahW8pPb9o1lf2r.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/M3JgFnahW8pPb9o1lf2r.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/M3JgFnahW8pPb9o1lf2r.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/M3JgFnahW8pPb9o1lf2r.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;The reason this happens is because, on this page, the LCP element is hidden until the JavaScript code finishes loading, and then everything is revealed at once.&lt;/p&gt;
&lt;p&gt;This example helps illustrate the point that you need to optimize all of these sub-parts in order to achieve the best LCP outcomes.&lt;/p&gt;
&lt;h3 id=&quot;optimal-sub-part-times&quot;&gt;Optimal sub-part times &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#optimal-sub-part-times&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In order to optimize each sub-part of LCP, it&#39;s important to understand what the ideal breakdown of these sub-parts is on a well-optimized page.&lt;/p&gt;
&lt;p&gt;Of the four sub-parts, two have the word &amp;quot;delay&amp;quot; in their names. That is a clue that you want to get these times as close to zero as possible. The other two parts involve network requests, which by their very nature take time.&lt;/p&gt;
&lt;div class=&quot;table-wrapper scrollbar&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;LCP sub-part&lt;/th&gt;
        &lt;th&gt;% of LCP&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;Time to first byte (TTFB)&lt;/td&gt;
        &lt;td&gt;~40%&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Resource load delay&lt;/td&gt;
        &lt;td&gt;&amp;lt;10%&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Resource load time&lt;/td&gt;
        &lt;td&gt;~40%&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Element render delay&lt;/td&gt;
        &lt;td&gt;&amp;lt;10%&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;strong&gt;TOTAL&lt;/strong&gt;&lt;/td&gt;
        &lt;td&gt;&lt;strong&gt;100%&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Note that these time breakdowns are not strict rules, they&#39;re guidelines. If the LCP times on your pages are consistently within 2.5 seconds, then it doesn&#39;t really matter what the relative proportions are. But if you&#39;re spending a lot of unnecessary time in either of the &amp;quot;delay&amp;quot; portions, then it will be very difficult to constantly hit the &lt;a href=&quot;https://web.dev/lcp/#what-is-a-good-lcp-score&quot;&gt;2.5 second target&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A good way to think about the breakdown of LCP time is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;vast majority&lt;/strong&gt; of the LCP time should be spent loading the HTML document and LCP source.&lt;/li&gt;
&lt;li&gt;Any time before LCP where one of these two resources is &lt;em&gt;not&lt;/em&gt; loading is &lt;strong&gt;an opportunity to improve&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;aside flow bg-state-warn-bg color-state-warn-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block color-state-warn-text&quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Warning sign&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M23 21L12 2 1 21h22zm-12-3v-2h2v2h-2zm0-4h2v-4h-2v4z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Warning&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Given the &lt;a href=&quot;https://web.dev/lcp/#what-is-a-good-lcp-score&quot;&gt;2.5 second target&lt;/a&gt; for LCP, it may be tempting to try to convert these percentages into absolute numbers, but that is not recommended. These sub-parts are only meaningful relative to each other, so it&#39;s best to always measure them that way. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;how-to-optimize-each-part&quot;&gt;How to optimize each part &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#how-to-optimize-each-part&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now that you understand how each of the LCP sub-part times should break down on a well-optimized page, you can start optimizing your own pages.&lt;/p&gt;
&lt;p&gt;The next four sections will present recommendations and best practices for how to optimize each part. They&#39;re presented in order, starting with the optimizations that are likely to have the biggest impact.&lt;/p&gt;
&lt;h3 id=&quot;1-eliminate-resource-load-delay&quot;&gt;1. Eliminate &lt;em&gt;resource load delay&lt;/em&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#1-eliminate-resource-load-delay&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The goal in this step is to ensure the LCP resource starts loading as early as possible. While in theory the earliest a resource &lt;em&gt;could&lt;/em&gt; start loading is immediately after TTFB, in practice there is always some delay before browsers actually start loading resources.&lt;/p&gt;
&lt;p&gt;A good rule of thumb is that your LCP resource should start loading at the same time as the first resource loaded by that page. Or, to put that another way, if the LCP resource starts loading later than the first resource, then there&#39;s opportunity for improvement.&lt;/p&gt;
&lt;img alt=&quot;A network waterfall diagram showing the LCP resource starting after the first resource, showing the opportunity for improvement&quot; decoding=&quot;async&quot; height=&quot;375&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/8mqXeiEsLQwjgq2lEbjn.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/8mqXeiEsLQwjgq2lEbjn.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/8mqXeiEsLQwjgq2lEbjn.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/8mqXeiEsLQwjgq2lEbjn.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/8mqXeiEsLQwjgq2lEbjn.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/8mqXeiEsLQwjgq2lEbjn.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/8mqXeiEsLQwjgq2lEbjn.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/8mqXeiEsLQwjgq2lEbjn.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/8mqXeiEsLQwjgq2lEbjn.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/8mqXeiEsLQwjgq2lEbjn.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/8mqXeiEsLQwjgq2lEbjn.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/8mqXeiEsLQwjgq2lEbjn.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/8mqXeiEsLQwjgq2lEbjn.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/8mqXeiEsLQwjgq2lEbjn.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/8mqXeiEsLQwjgq2lEbjn.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/8mqXeiEsLQwjgq2lEbjn.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/8mqXeiEsLQwjgq2lEbjn.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/8mqXeiEsLQwjgq2lEbjn.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Generally speaking, there are two factors that affect how quickly an LCP resource can be loading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When the resource is discovered.&lt;/li&gt;
&lt;li&gt;What priority the resource is given.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;optimize-when-the-resource-is-discovered&quot;&gt;Optimize when the resource is discovered &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#optimize-when-the-resource-is-discovered&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;To ensure your LCP resource starts loading as early as possible, it&#39;s critical that the resource is discoverable in the initial HTML document response by the browser&#39;s &lt;a href=&quot;https://web.dev/preload-scanner/&quot;&gt;preload scanner&lt;/a&gt;. For example, in the following cases, the browser can discover the LCP resource by scanning the HTML document response:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The LCP element is an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element, and its &lt;code&gt;src&lt;/code&gt; or &lt;code&gt;srcset&lt;/code&gt; attributes are present in the initial HTML markup.&lt;/li&gt;
&lt;li&gt;The LCP element requires a CSS background image, but that image is preloaded via &lt;code&gt;&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&lt;/code&gt; in the HTML markup (or via a &lt;code&gt;Link&lt;/code&gt; header).&lt;/li&gt;
&lt;li&gt;The LCP element is a text node that requires a web font to render, and the font is loaded via &lt;code&gt;&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&lt;/code&gt; in the HTML markup (or via a &lt;code&gt;Link&lt;/code&gt; header).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here are some examples where the LCP resource cannot be discovered from scanning the HTML document response:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The LCP element is an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; that is dynamically added to the page via JavaScript.&lt;/li&gt;
&lt;li&gt;The LCP element is lazily loaded with a JavaScript library that hides its &lt;code&gt;src&lt;/code&gt; or &lt;code&gt;srcset&lt;/code&gt; attributes (often as &lt;code&gt;data-src&lt;/code&gt; or &lt;code&gt;data-srcset&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The LCP element requires a CSS background image.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In each of these cases, the browser needs to run the script or apply the stylesheet—which usually involves waiting for network requests to finish—before it can discover the LCP resource and could start loading it. This is never optimal.&lt;/p&gt;
&lt;p&gt;To eliminate unnecessary resource load delay, your LCP resource should &lt;em&gt;always&lt;/em&gt; be discoverable from the HTML source. In cases where the resource is only referenced from an external CSS or JavaScript file, then the LCP resource should be preloaded, with a high fetch priority (more on &lt;a href=&quot;https://web.dev/optimize-lcp/#optimize-the-priority-the-resource-is-given&quot;&gt;fetch priority in the next section&lt;/a&gt;); for example:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Load the stylesheet that will reference the LCP image. --&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/path/to/styles.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Preload the LCP image with a high fetchpriority so it starts loading with the stylesheet. --&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fetchpriority&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;high&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/path/to/hero-image.webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image/webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-warn-bg color-state-warn-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block color-state-warn-text&quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Warning sign&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M23 21L12 2 1 21h22zm-12-3v-2h2v2h-2zm0-4h2v-4h-2v4z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Warning&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; On most pages, ensuring that the LCP resource starts loading at the same time as the first resource is good enough, but be aware that it is possible to construct a page where none of the resources are discovered early and all of them start loading significantly later than TTFB. So while comparing with the first resource is a good way to identify opportunities to improve, it may not be sufficient in some cases, so it&#39;s still important to measure this time relative to TTFB and ensure it remains small. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;optimize-the-priority-the-resource-is-given&quot;&gt;Optimize the priority the resource is given &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#optimize-the-priority-the-resource-is-given&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Even if the LCP resource is discoverable from the HTML markup, it &lt;em&gt;still&lt;/em&gt; may not start loading as early as the first resource. This can happen if the browser preload scanner&#39;s priority heuristics do not recognize that the resource is important, or if it determines that other resources are more important.&lt;/p&gt;
&lt;p&gt;For example, you can delay your LCP image via HTML if you set &lt;a href=&quot;https://web.dev/browser-level-image-lazy-loading/&quot;&gt;&lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt;&lt;/a&gt; on your &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element. Using lazy loading means that the resource will not be loaded until after layout confirms the image is in the viewport and so may begin loading later than it otherwise would.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-warn-bg color-state-warn-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block color-state-warn-text&quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Warning sign&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M23 21L12 2 1 21h22zm-12-3v-2h2v2h-2zm0-4h2v-4h-2v4z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Warning&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Never lazy-load your LCP image, as that will always lead to unnecessary resource load delay, and will have a negative impact on LCP. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Even without lazy loading, images are not initially loaded with the highest priority by browsers as they are not render-blocking resources. You can hint to the browser as to which resources are most important via the &lt;a href=&quot;https://web.dev/fetch-priority/&quot;&gt;&lt;code&gt;fetchpriority&lt;/code&gt;&lt;/a&gt; attribute for resources that could benefit from a higher priority:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fetchpriority&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;high&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/path/to/hero-image.webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;It&#39;s a good idea to set &lt;code&gt;fetchpriority=&amp;quot;high&amp;quot;&lt;/code&gt; on an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element if you think it&#39;s likely to be your page&#39;s LCP element—but limit this to just one or two images (based on common desktop and mobile viewport sizes), otherwise the signal becomes meaningless. You can also lower the priority of images that may be early in the document response, but isn&#39;t visible due to styling, such as images in carousel slides that are not visible at startup:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fetchpriority&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;low&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/path/to/carousel-slide-3.webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Deprioritizing certain resources can afford more bandwidth to resources that need it more—but be careful. Always check resource priority in DevTools and test changes with lab and field tools.&lt;/p&gt;
&lt;p&gt;After you have optimized your LCP resource priority and discovery time, your network waterfall should look like this (with the LCP resource starting at the same time as the first resource):&lt;/p&gt;
&lt;img alt=&quot;A network waterfall diagram showing the LCP resource now starting at the same time as the first resource&quot; decoding=&quot;async&quot; height=&quot;375&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/f9s7SJSBNKcMm3VmcvtT.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/f9s7SJSBNKcMm3VmcvtT.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/f9s7SJSBNKcMm3VmcvtT.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/f9s7SJSBNKcMm3VmcvtT.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/f9s7SJSBNKcMm3VmcvtT.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/f9s7SJSBNKcMm3VmcvtT.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/f9s7SJSBNKcMm3VmcvtT.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/f9s7SJSBNKcMm3VmcvtT.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/f9s7SJSBNKcMm3VmcvtT.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/f9s7SJSBNKcMm3VmcvtT.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/f9s7SJSBNKcMm3VmcvtT.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/f9s7SJSBNKcMm3VmcvtT.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/f9s7SJSBNKcMm3VmcvtT.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/f9s7SJSBNKcMm3VmcvtT.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/f9s7SJSBNKcMm3VmcvtT.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/f9s7SJSBNKcMm3VmcvtT.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/f9s7SJSBNKcMm3VmcvtT.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/f9s7SJSBNKcMm3VmcvtT.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Important&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Another reason your LCP resource may not start loading as early as possible—even when it&#39;s discoverable from the HTML source—is if it&#39;s hosted on a different origin, as these requests require the browser to connect to that origin before the resource can start loading. When possible, it&#39;s a good idea to host critical resources on the same origin as your HTML document resource because then those resources can save time by reusing the existing connection (more on this point later). &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;2-eliminate-element-render-delay&quot;&gt;2. Eliminate &lt;em&gt;element render delay&lt;/em&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#2-eliminate-element-render-delay&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The goal in this step is to ensure the LCP element can render &lt;em&gt;immediately&lt;/em&gt; after its resource has finished loading, no matter when that happens.&lt;/p&gt;
&lt;p&gt;The primary reason the LCP element &lt;em&gt;wouldn&#39;t&lt;/em&gt; be able to render immediately after its resource finishes loading is if rendering is &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/render-blocking-resources/&quot; rel=&quot;noopener&quot;&gt;blocked&lt;/a&gt; for some other reason:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rendering of the entire page is blocked due to stylesheets or synchronous scripts in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; that are still loading.&lt;/li&gt;
&lt;li&gt;The LCP resource has finished loading, but the LCP element has not yet been added to the DOM (it&#39;s waiting for some JavaScript code to load).&lt;/li&gt;
&lt;li&gt;The element is being hidden by some other code, such as an A/B testing library that&#39;s still determining what experiment the user should be in.&lt;/li&gt;
&lt;li&gt;The main thread is blocked due to &lt;a href=&quot;https://web.dev/long-tasks-devtools/#what-are-long-tasks&quot;&gt;long tasks&lt;/a&gt;, and rendering work needs to wait until those long tasks complete.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following sections explain how to address the most common causes of unnecessary element render delay.&lt;/p&gt;
&lt;h4 id=&quot;reduce-or-inline-render-blocking-stylesheets&quot;&gt;Reduce or inline render-blocking stylesheets &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#reduce-or-inline-render-blocking-stylesheets&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Stylesheets loaded from the HTML markup will block rendering of all content that follows them, which is good, since you generally do not want to render unstyled HTML. However, if the stylesheet is so large that it takes significantly longer to load than the LCP resource, then it will prevent the LCP element from rendering—even after its resource has finished loading, as shown in this example:&lt;/p&gt;
&lt;img alt=&quot;A network waterfall diagram showing a large CSS file blocking rendering of the LCP element because it takes longer to load than the LCP resource&quot; decoding=&quot;async&quot; height=&quot;450&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/A5XTlQxIdF9WsLdXiBXE.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/A5XTlQxIdF9WsLdXiBXE.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/A5XTlQxIdF9WsLdXiBXE.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/A5XTlQxIdF9WsLdXiBXE.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/A5XTlQxIdF9WsLdXiBXE.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/A5XTlQxIdF9WsLdXiBXE.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/A5XTlQxIdF9WsLdXiBXE.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/A5XTlQxIdF9WsLdXiBXE.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/A5XTlQxIdF9WsLdXiBXE.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/A5XTlQxIdF9WsLdXiBXE.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/A5XTlQxIdF9WsLdXiBXE.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/A5XTlQxIdF9WsLdXiBXE.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/A5XTlQxIdF9WsLdXiBXE.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/A5XTlQxIdF9WsLdXiBXE.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/A5XTlQxIdF9WsLdXiBXE.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/A5XTlQxIdF9WsLdXiBXE.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/A5XTlQxIdF9WsLdXiBXE.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/A5XTlQxIdF9WsLdXiBXE.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;To fix this, your options are to either:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;inline the stylesheet into the HTML to avoid the additional network request; or,&lt;/li&gt;
&lt;li&gt;reduce the size of the stylesheet.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In general, inlining your stylesheet is only recommended if your stylesheet is small since inlined content in the HTML cannot benefit from caching in subsequent page loads. If a stylesheet is so large that it takes longer to load than the LCP resource, then it&#39;s unlikely to be a good candidate for inlining.&lt;/p&gt;
&lt;p&gt;In most cases, the best way to ensure the stylesheet does not block rendering of the LCP element is to reduce its size so that it&#39;s smaller than the LCP resource. This should ensure it&#39;s not a bottleneck for most visits.&lt;/p&gt;
&lt;p&gt;Some recommendations to reduce the size of the stylesheet are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/unused-css-rules/&quot; rel=&quot;noopener&quot;&gt;Remove unused CSS&lt;/a&gt;: use Chrome DevTools to find CSS rules that aren&#39;t being used and can potentially be removed (or deferred).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/defer-non-critical-css/&quot;&gt;Defer non-critical CSS&lt;/a&gt;: split your stylesheet out into styles that are required for initial page load and then styles that can be loaded lazily.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/reduce-network-payloads-using-text-compression/&quot;&gt;Minify and compress CSS&lt;/a&gt;: for styles that are critical, make sure you&#39;re reducing their &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/PerformanceResourceTiming/transferSize&quot; rel=&quot;noopener&quot;&gt;transfer size&lt;/a&gt; as much as possible.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;defer-or-inline-render-blocking-javascript&quot;&gt;Defer or inline render-blocking JavaScript &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#defer-or-inline-render-blocking-javascript&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;It is almost never necessary to add synchronous scripts (scripts without the &lt;code&gt;async&lt;/code&gt; or &lt;code&gt;defer&lt;/code&gt; attributes) to the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of your pages, and doing so will almost always have a negative impact on performance.&lt;/p&gt;
&lt;p&gt;In cases where JavaScript code needs to run as early as possible in the page load, it&#39;s best to inline it so rendering isn&#39;t delayed waiting on another network request. As with stylesheets, though, you should only inline scripts if they&#39;re very small.&lt;/p&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;worse&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Don&#39;t&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/path/to/main.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;better&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Do&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Inline script contents directly in the HTML.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// IMPORTANT: only do this for very small scripts.&lt;/span&gt;&lt;br /&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;h4 id=&quot;use-server-side-rendering&quot;&gt;Use server-side rendering &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#use-server-side-rendering&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/rendering-on-the-web/#server-rendering&quot;&gt;Server-side rendering&lt;/a&gt; (SSR) is the process of running your client-side application logic on the server and responding to HTML document requests with the full HTML markup.&lt;/p&gt;
&lt;p&gt;From the perspective of optimizing LCP, there are two primary advantage of SSR:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your image resources will be discoverable from the HTML source (as discussed in &lt;a href=&quot;https://web.dev/optimize-lcp/#1-eliminate-resource-load-delay&quot;&gt;step 1&lt;/a&gt; earlier).&lt;/li&gt;
&lt;li&gt;Your page content will not require additional JavaScript requests to finish before it can render.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The main downside of SSR is it requires additional server processing time, which can slow down your TTFB. This trade-off is usually worth it though because server processing times are within your control, whereas the network and device capabilities of your users are not.&lt;/p&gt;
&lt;p&gt;A similar option to SSR is called static site generation (SSG) or &lt;a href=&quot;https://web.dev/rendering-on-the-web/#terminology&quot;&gt;prerendering&lt;/a&gt;. This is the process of generating your HTML pages in a build step rather than on-demand. If prerendering is possible with your architecture, it&#39;s generally a better choice for performance.&lt;/p&gt;
&lt;h4 id=&quot;break-up-long-tasks&quot;&gt;Break up long tasks &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#break-up-long-tasks&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Even if you&#39;ve followed the above advice, and your JavaScript code is not render-blocking nor is it responsible for rendering your elements, it can still delay LCP.&lt;/p&gt;
&lt;p&gt;The most common reason this happens is when pages load large JavaScript files, which need to be parsed and executed on the browser&#39;s main thread. This means that, even if your image resource is fully downloaded, it may still have to wait until an unrelated script finishes executing before it can render.&lt;/p&gt;
&lt;p&gt;All browsers today render images on the main thread, which means anything that blocks the main thread can also lead to unnecessary &lt;em&gt;element render delay&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id=&quot;3-reduce-resource-load-time&quot;&gt;3. Reduce &lt;em&gt;resource load time&lt;/em&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#3-reduce-resource-load-time&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The goal of this step is to reduce the time spent transferring the bytes of the resource over the network to the user&#39;s device. In general, there are three ways to do that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reduce the size of the resource.&lt;/li&gt;
&lt;li&gt;Reduce the distance the resource has to travel.&lt;/li&gt;
&lt;li&gt;Reduce contention for network bandwidth.&lt;/li&gt;
&lt;li&gt;Eliminate the network time entirely.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;reduce-the-size-of-the-resource&quot;&gt;Reduce the size of the resource &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#reduce-the-size-of-the-resource&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The LCP resource of a page (if it has one) will either be an image or a web font. The following guides go into great detail about how to reduce the size of both:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/uses-responsive-images/&quot; rel=&quot;noopener&quot;&gt;Serve the optimal image size&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/uses-webp-images/&quot; rel=&quot;noopener&quot;&gt;Use modern image formats&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/uses-optimized-images/&quot; rel=&quot;noopener&quot;&gt;Compress images&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/reduce-webfont-size/&quot;&gt;Reduce web font size&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;reduce-the-distance-the-resource-has-to-travel&quot;&gt;Reduce the distance the resource has to travel &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#reduce-the-distance-the-resource-has-to-travel&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;In addition to reducing the size of a resource, you can also reduce the load times by getting your servers as geographically close to your users as possible. And the best way to do that is to use a &lt;a href=&quot;https://web.dev/content-delivery-networks/&quot;&gt;content delivery network&lt;/a&gt; (CDN).&lt;/p&gt;
&lt;p&gt;In fact, &lt;a href=&quot;https://web.dev/image-cdns/&quot;&gt;image CDNs&lt;/a&gt; in particular are a great choice because they not only reduce the distance the resource has to travel, but they also generally reduce the size of the resource—automatically implementing all of the size-reduction recommendations from earlier for you.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Important&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; While image CDNs are a great way to reduce resource load times, using a third-party domain to host your images comes with an additional connection cost. While preconnecting to the origin can mitigate some of this cost, the best option is to serve images from the same origin as your HTML document. Many CDNs allow you to proxy requests from your origin to theirs, which is a great option to look into if available. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;reduce-contention-for-network-bandwidth&quot;&gt;Reduce contention for network bandwidth &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#reduce-contention-for-network-bandwidth&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Even if you&#39;ve reduced the size of your resource and the distance it has to travel, a resource can still take a long time to load if you&#39;re loading many other resources at the same time. This problem is known as &lt;em&gt;network contention&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If you&#39;ve given your LCP resource a &lt;a href=&quot;https://web.dev/fetch-priority/&quot;&gt;high &lt;code&gt;fetchpriority&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://web.dev/optimize-lcp/#1-eliminate-resource-load-delay&quot;&gt;started loading it as soon as possible&lt;/a&gt; then the browser will do its best to prevent lower-priority resources from competing with it. However, if you&#39;re loading many resources with high &lt;code&gt;fetchpriority&lt;/code&gt;, or if you&#39;re just loading a lot of resources in general, then it could affect how quickly the LCP resource loads.&lt;/p&gt;
&lt;h4 id=&quot;eliminate-the-network-time-entirely&quot;&gt;Eliminate the network time entirely &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#eliminate-the-network-time-entirely&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The best way to reduce resource load times is to eliminate the network entirely from the process. If you serve your resources with an &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/uses-long-cache-ttl/&quot; rel=&quot;noopener&quot;&gt;efficient cache-control policy&lt;/a&gt;, then visitors who request those resources a second time will have them served from the cache—bringing the &lt;em&gt;resource load time&lt;/em&gt; to essentially zero!&lt;/p&gt;
&lt;p&gt;And if your LCP resource is a web font, in addition to &lt;a href=&quot;https://web.dev/reduce-webfont-size/&quot;&gt;reducing web font size&lt;/a&gt;, you should also consider whether you need to block rendering on the web font resource load. If you set a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@font-face/font-display&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;font-display&lt;/code&gt;&lt;/a&gt; value of anything other than &lt;code&gt;auto&lt;/code&gt; or &lt;code&gt;block&lt;/code&gt;, then text will &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/font-display/&quot; rel=&quot;noopener&quot;&gt;always be visible during load&lt;/a&gt;, and LCP will not be blocked on an additional network request.&lt;/p&gt;
&lt;p&gt;Finally, if your LCP resource is small, it may make sense to inline the resources as a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/Data_URIs&quot; rel=&quot;noopener&quot;&gt;data URL&lt;/a&gt;, which will also eliminate the additional network request. However, using data URLs &lt;a href=&quot;https://calendar.perfplanet.com/2018/performance-anti-patterns-base64-encoding/&quot; rel=&quot;noopener&quot;&gt;comes with caveats&lt;/a&gt; because then the resources cannot be cached and in some cases can lead to longer render delays because of the additional &lt;a href=&quot;https://www.catchpoint.com/blog/data-uri&quot; rel=&quot;noopener&quot;&gt;decode cost&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;4-reduce-time-to-first-byte&quot;&gt;4. Reduce &lt;em&gt;time to first byte&lt;/em&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#4-reduce-time-to-first-byte&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The goal of this step is to deliver the initial HTML as quickly as possible. This step is listed last because it&#39;s often the one developers have the least control over. However, it&#39;s also one of the most important steps because it directly affects every step that comes after it. Nothing can happen on the frontend until the backend delivers that first byte of content, so anything you can do to speed up your TTFB will improve every other load metric as well.&lt;/p&gt;
&lt;p&gt;A common cause of a slow TTFB for an otherwise fast site is making visitors wade through several redirects before finally arriving at the final URL. This can happen when you have visitors from advertisements, or via URL shorteners. Always try to minimize the number of redirects a visitor must wait through.&lt;/p&gt;
&lt;p&gt;Another common cause is when cached content cannot be used from a CDN edge server, and all requests must be directed all the way back to the origin server. This can happen if unique URL parameters are used by visitors for analytics—even if they do not result in different pages.&lt;/p&gt;
&lt;p&gt;For specific guidance on this topic, see &lt;a href=&quot;https://web.dev/optimize-ttfb/&quot;&gt;Optimize TTFB&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;monitor-lcp-breakdown-in-javascript&quot;&gt;Monitor LCP breakdown in JavaScript &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#monitor-lcp-breakdown-in-javascript&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The timing information for all of the LCP sub-parts discussed above is available to you in JavaScript through a combination of the following performance APIs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://w3c.github.io/largest-contentful-paint/&quot; rel=&quot;noopener&quot;&gt;Largest Contentful Paint API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/navigation-timing-2/&quot; rel=&quot;noopener&quot;&gt;Navigation Timing API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/resource-timing-2/&quot; rel=&quot;noopener&quot;&gt;Resource Timing API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The benefit to computing these timing values in JavaScript is it allows you to send them to an analytics provider or log them to your developer tools to help with debugging and optimizing.&lt;/p&gt;
&lt;p&gt;For example, the following screenshot uses the &lt;code&gt;performance.measure()&lt;/code&gt; method from the &lt;a href=&quot;https://w3c.github.io/user-timing/&quot; rel=&quot;noopener&quot;&gt;User Timing API&lt;/a&gt; to add bars to the Timings track in the Chrome DevTools Performance panel.&lt;/p&gt;
&lt;img alt=&quot;User Timing measures of the LCP sub-parts visualized in Chrome DevTools&quot; decoding=&quot;async&quot; height=&quot;471&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/swFmxNjI5nLagUOrIjZo.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/swFmxNjI5nLagUOrIjZo.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/swFmxNjI5nLagUOrIjZo.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/swFmxNjI5nLagUOrIjZo.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/swFmxNjI5nLagUOrIjZo.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/swFmxNjI5nLagUOrIjZo.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/swFmxNjI5nLagUOrIjZo.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/swFmxNjI5nLagUOrIjZo.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/swFmxNjI5nLagUOrIjZo.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/swFmxNjI5nLagUOrIjZo.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/swFmxNjI5nLagUOrIjZo.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/swFmxNjI5nLagUOrIjZo.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/swFmxNjI5nLagUOrIjZo.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/swFmxNjI5nLagUOrIjZo.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/swFmxNjI5nLagUOrIjZo.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/swFmxNjI5nLagUOrIjZo.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/swFmxNjI5nLagUOrIjZo.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/swFmxNjI5nLagUOrIjZo.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Visualizations in the timings track are particularly helpful when looked at alongside the Network and Main thread tracks, because you can see at a glance what else is happening on the page during these timespans.&lt;/p&gt;
&lt;p&gt;In addition to visualizing the LCP sub-parts in the timings track, you can also use JavaScript to compute what percentage each sub-part is of the total LCP time. With that information, you can determine whether your pages are meeting the &lt;a href=&quot;https://web.dev/optimize-lcp/#optimal-sub-part-times&quot;&gt;recommended percentage breakdowns&lt;/a&gt; described earlier.&lt;/p&gt;
&lt;p&gt;This screenshot shows an example that logs the total time of each LCP sub-part, as well as its percentage of the total LCP time to the console.&lt;/p&gt;
&lt;img alt=&quot;The LCP sub-part times, as well as their percent of LCP, printed to the console&quot; decoding=&quot;async&quot; height=&quot;267&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/zCB4PVlHUX9Iz5NSwdMu.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/zCB4PVlHUX9Iz5NSwdMu.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/zCB4PVlHUX9Iz5NSwdMu.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/zCB4PVlHUX9Iz5NSwdMu.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/zCB4PVlHUX9Iz5NSwdMu.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/zCB4PVlHUX9Iz5NSwdMu.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/zCB4PVlHUX9Iz5NSwdMu.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/zCB4PVlHUX9Iz5NSwdMu.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/zCB4PVlHUX9Iz5NSwdMu.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/zCB4PVlHUX9Iz5NSwdMu.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/zCB4PVlHUX9Iz5NSwdMu.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/zCB4PVlHUX9Iz5NSwdMu.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/zCB4PVlHUX9Iz5NSwdMu.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/zCB4PVlHUX9Iz5NSwdMu.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/zCB4PVlHUX9Iz5NSwdMu.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/zCB4PVlHUX9Iz5NSwdMu.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/zCB4PVlHUX9Iz5NSwdMu.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/zCB4PVlHUX9Iz5NSwdMu.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Both of these visualizations were created with the following code:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;LCP_SUB_PARTS&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token string&quot;&gt;&#39;Time to first byte&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token string&quot;&gt;&#39;Resource load delay&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token string&quot;&gt;&#39;Resource load time&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token string&quot;&gt;&#39;Element render delay&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PerformanceObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; lcpEntry &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEntries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; navEntry &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; performance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEntriesByType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;navigation&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; lcpResEntry &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; performance&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEntriesByType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;resource&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; lcpEntry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Ignore LCP entries that aren&#39;t images to reduce DevTools noise.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Comment this line out if you want to include text entries.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;lcpEntry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Compute the start and end times of each LCP sub-part.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// WARNING! If your LCP resource is loaded cross-origin, make sure to add&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// the `Timing-Allow-Origin` (TAO) header to get the most accurate results.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ttfb &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; navEntry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;responseStart&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; lcpRequestStart &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;    ttfb&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Prefer `requestStart` (if TOA is set), otherwise use `startTime`.&lt;/span&gt;&lt;br /&gt;    lcpResEntry &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; lcpResEntry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;requestStart &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; lcpResEntry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;startTime &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; lcpResponseEnd &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;    lcpRequestStart&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    lcpResEntry &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; lcpResEntry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;responseEnd &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; lcpRenderTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;    lcpResponseEnd&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Use LCP startTime (which is the final LCP time) as sometimes&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// slight differences between loadTime/renderTime and startTime&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// due to rounding precision.&lt;/span&gt;&lt;br /&gt;    lcpEntry &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; lcpEntry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;startTime &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Clear previous measures before making new ones.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Note: due to a bug this does not work in Chrome DevTools.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token constant&quot;&gt;LCP_SUB_PARTS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; performance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;clearMeasures&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;part&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Create measures for each LCP sub-part for easier&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// visualization in the Chrome DevTools Performance panel.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; lcpSubPartMeasures &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;    performance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;measure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;LCP_SUB_PARTS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ttfb&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    performance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;measure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;LCP_SUB_PARTS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ttfb&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; lcpRequestStart&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    performance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;measure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;LCP_SUB_PARTS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; lcpRequestStart&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; lcpResponseEnd&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    performance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;measure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;LCP_SUB_PARTS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; lcpResponseEnd&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; lcpRenderTime&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Log helpful debug information to the console.&lt;/span&gt;&lt;br /&gt;  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;LCP value: &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lcpRenderTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;LCP element: &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lcpEntry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;element&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lcpEntry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;    lcpSubPartMeasures&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;measure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token string-property property&quot;&gt;&#39;LCP sub-part&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; measure&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token string-property property&quot;&gt;&#39;Time (ms)&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; measure&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;duration&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token string-property property&quot;&gt;&#39;% of LCP&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;br /&gt;        Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;round&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; measure&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;duration&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; lcpRenderTime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;largest-contentful-paint&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;buffered&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You can use this code as-is for local debugging, or modify it to send this data to an analytics provider so you can get a better understanding of what the breakdown of LCP is on your pages for real users.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; &lt;p&gt; The above code works for standard navigations, but extra consideration must be taken for &lt;a href=&quot;https://developer.chrome.com/blog/prerender-pages/&quot;&gt;prerendered pages&lt;/a&gt;, which should be measured from the activation start time but which has been kept out of this code for simplicity. &lt;/p&gt; &lt;p&gt; The &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals&quot;&gt;web-vitals library&lt;/a&gt; includes this breakdown in the attribution build, and includes these considerations. &lt;/p&gt; &lt;p&gt; For those looking to implement their own solution, the &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals/blob/main/src/attribution/onLCP.ts&quot;&gt;code for this is open source&lt;/a&gt; and is similar to above but with extra lofic for activation start. &lt;/p&gt; &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;monitor-lcp-breakdown-via-the-web-vitals-extension&quot;&gt;Monitor LCP breakdown via the Web Vitals extension &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#monitor-lcp-breakdown-via-the-web-vitals-extension&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://chrome.google.com/webstore/detail/web-vitals/ahfhijdlegdabablpippeagghigmibma&quot; rel=&quot;noopener&quot;&gt;Web Vitals extension&lt;/a&gt; will &lt;a href=&quot;https://web.dev/debug-cwvs-with-web-vitals-extension/#lcp-debug-information&quot;&gt;log the LCP time, LCP element, and these four sub-parts in the console logging&lt;/a&gt;, to easily allow you to see this breakdown.&lt;/p&gt;
&lt;img alt=&quot;Screenshot of the console logging of the Web Vitals extension showing the LCP sub-part timings&quot; decoding=&quot;async&quot; height=&quot;239&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FEFEkgKuC6RxVh7MHmkn.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FEFEkgKuC6RxVh7MHmkn.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FEFEkgKuC6RxVh7MHmkn.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FEFEkgKuC6RxVh7MHmkn.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FEFEkgKuC6RxVh7MHmkn.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FEFEkgKuC6RxVh7MHmkn.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FEFEkgKuC6RxVh7MHmkn.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FEFEkgKuC6RxVh7MHmkn.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FEFEkgKuC6RxVh7MHmkn.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FEFEkgKuC6RxVh7MHmkn.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FEFEkgKuC6RxVh7MHmkn.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FEFEkgKuC6RxVh7MHmkn.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FEFEkgKuC6RxVh7MHmkn.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FEFEkgKuC6RxVh7MHmkn.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FEFEkgKuC6RxVh7MHmkn.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FEFEkgKuC6RxVh7MHmkn.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FEFEkgKuC6RxVh7MHmkn.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FEFEkgKuC6RxVh7MHmkn.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-lcp/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;LCP is complex, and its timing can be affected by a number of factors. But if you consider that optimizing LCP is primarily about optimizing the load of the LCP resource, it can significantly simplify things.&lt;/p&gt;
&lt;p&gt;At a high level, optimizing LCP can be summarized in four steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Ensure the LCP resource starts loading as early as possible.&lt;/li&gt;
&lt;li&gt;Ensure the LCP element can render as soon as its resource finishes loading.&lt;/li&gt;
&lt;li&gt;Reduce the load time of the LCP resource as much as you can without sacrificing quality.&lt;/li&gt;
&lt;li&gt;Deliver the initial HTML document as fast as possible.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you’re able to follow these steps on your pages, then you should feel confident that you’re delivering an optimal loading experience to your users, and you should see that reflected in your real-world LCP scores.&lt;/p&gt;
</content>
    <author>
      <name>Philip Walton</name>
    </author><author>
      <name>Barry Pollard</name>
    </author>
  </entry>
  
  <entry>
    <title>Optimize Cumulative Layout Shift</title>
    <link href="https://web.dev/optimize-cls/"/>
    <updated>2020-05-05T00:00:00Z</updated>
    <id>https://web.dev/optimize-cls/</id>
    <content type="html" mode="escaped">&lt;p&gt;&amp;quot;I was about to click that! Why did it move? 😭&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/cls/&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt; is one of the three &lt;a href=&quot;https://web.dev/vitals/#core-web-vitals&quot;&gt;Core Web Vitals&lt;/a&gt; metrics, and it measures the instability of content by summing shift scores across layout shifts that don&#39;t occur within 500 milliseconds of user input. It looks at how much visible content shifted in the viewport as well as the distance the elements impacted were shifted.&lt;/p&gt;
&lt;p&gt;Layout shifts can be distracting to users. Imagine you&#39;ve started reading an article when all of a sudden elements shift around the page, throwing you off and requiring you to find your place again. This is very common on the web, including when reading the news, or trying to click those &#39;Search&#39; or &#39;Add to Cart&#39; buttons. Such experiences are visually jarring and frustrating. They&#39;re often caused when visible elements are forced to move because another element was suddenly added to the page or resized.&lt;/p&gt;
&lt;p&gt;To provide a good user experience, &lt;strong&gt;sites should strive to have a CLS of 0.1 or less for at least 75% of page visits.&lt;/strong&gt;&lt;/p&gt;
&lt;figure&gt;
  &lt;picture&gt;
    &lt;source srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9mWVASbWDLzdBUpVcjE1.svg&quot; media=&quot;(min-width: 640px)&quot; width=&quot;800&quot; height=&quot;200&quot; /&gt;
    &lt;img alt=&quot;Good CLS values are under 0.1, poor values are greater than 0.25 and anything in between needs improvement&quot; decoding=&quot;async&quot; height=&quot;480&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uqclEgIlTHhwIgNTXN3Y.svg&quot; width=&quot;640&quot; /&gt;
  &lt;/picture&gt;
&lt;/figure&gt;
&lt;p&gt;Unlike the other Core Web Vitals, which are time-based values measured in seconds or milliseconds, the CLS score is a unitless value based on a calculation of how much content is shifting and by how far.&lt;/p&gt;
&lt;p&gt;In this guide, we&#39;ll cover optimizing common causes of layout shifts.&lt;/p&gt;
&lt;p&gt;The most common causes of a poor CLS are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Images without dimensions&lt;/li&gt;
&lt;li&gt;Ads, embeds, and iframes without dimensions&lt;/li&gt;
&lt;li&gt;Dynamically injected content such as ads, embeds, and iframes without dimensions&lt;/li&gt;
&lt;li&gt;Web fonts&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; For a visual overview of some of the content presented in this guide, see the Optimize for Core Web Vitals video from Google I/O 2020:  &lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;AQqFZ5t8uNc&quot; videoStartAt=&quot;88&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt; &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;understanding-where-your-shifts-are-coming-from&quot;&gt;Understanding where your shifts are coming from &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-cls/#understanding-where-your-shifts-are-coming-from&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before we start looking at solutions to common CLS issues, it&#39;s always important to understand your CLS score and where the shifts are coming from. A big part of the problem is understanding your CLS score—the fix afterwards is often the easier part!&lt;/p&gt;
&lt;h3 id=&quot;cls-in-lab-tools-versus-field&quot;&gt;CLS in lab tools versus field &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-cls/#cls-in-lab-tools-versus-field&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It is quite common to hear developers think the CLS measured by the &lt;a href=&quot;https://developer.chrome.com/docs/crux/&quot; rel=&quot;noopener&quot;&gt;Chrome UX Report (CrUX)&lt;/a&gt; is incorrect as it does not match the CLS they measure using Chrome DevTools or other lab tools. Web performance lab tools like Lighthouse may not show the full CLS of a page as they typically do a simple load of the page to measure some web performance metrics and provide some guidance (though &lt;a href=&quot;https://web.dev/lighthouse-user-flows/&quot;&gt;Lighthouse user flows&lt;/a&gt; do allow you measure beyond the default page load audit).&lt;/p&gt;
&lt;p&gt;CrUX is the official dataset of the Web Vitals program, and for that, CLS is measured throughout the full life of the page and not just during the initial page load that lab tools typically measure.&lt;/p&gt;
&lt;p&gt;Layout shifts are very common during page load, as all the necessary resources are fetched to initially render the page, but layout shifts can also happen after the initial load. Many post-load shifts may occur &lt;a href=&quot;https://web.dev/cls/#user-initiated-layout-shifts&quot;&gt;as the result of a user interaction&lt;/a&gt; and therefore will be excluded from the CLS score as they are &lt;em&gt;expected&lt;/em&gt; shifts—as long as they occur within 500 milliseconds of that interaction.&lt;/p&gt;
&lt;p&gt;However, other post-load shifts that are unexpected by the user may be included where there was no qualifying interaction—for example, if you scroll down the page and lazy-loaded content is loaded and that causes shifts. Other common causes of post-load CLS are on interactions of transitions, for example on Single Page Apps, which take longer than the 500 millisecond grace period.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://pagespeed.web.dev/&quot; rel=&quot;noopener&quot;&gt;PageSpeed Insights&lt;/a&gt; will show both the user-perceived CLS from a URL where it exists in the &amp;quot;Discover what your real users are experiencing&amp;quot; section, and also the lab-based load CLS in the &amp;quot;Diagnose performance issues&amp;quot; section beneath. If you see a difference between these, this is likely caused by post-load CLS.&lt;/p&gt;
&lt;img alt=&quot;Screenshot of PageSpeed Insights showing URL-level data highlighting the real user CLS which is considerably larger than the Lighthouse CLS&quot; decoding=&quot;async&quot; height=&quot;450&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2eO9BsIb8Gx6kU4Zvbqa.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2eO9BsIb8Gx6kU4Zvbqa.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2eO9BsIb8Gx6kU4Zvbqa.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2eO9BsIb8Gx6kU4Zvbqa.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2eO9BsIb8Gx6kU4Zvbqa.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2eO9BsIb8Gx6kU4Zvbqa.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2eO9BsIb8Gx6kU4Zvbqa.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2eO9BsIb8Gx6kU4Zvbqa.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2eO9BsIb8Gx6kU4Zvbqa.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2eO9BsIb8Gx6kU4Zvbqa.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2eO9BsIb8Gx6kU4Zvbqa.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2eO9BsIb8Gx6kU4Zvbqa.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2eO9BsIb8Gx6kU4Zvbqa.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2eO9BsIb8Gx6kU4Zvbqa.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2eO9BsIb8Gx6kU4Zvbqa.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2eO9BsIb8Gx6kU4Zvbqa.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2eO9BsIb8Gx6kU4Zvbqa.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2eO9BsIb8Gx6kU4Zvbqa.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; PageSpeed Inights will show URL-level data where it exists, and attempt to fall back to origin-level data where this does not exist. Always check what data is showing to ensure you do not waste time trying to track down a CLS issue that actually exists on other pages on your origin! In the above example you can see this is URL-level data as shown in the top right of the image. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;identifying-load-cls-issues&quot;&gt;Identifying load CLS issues &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-cls/#identifying-load-cls-issues&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When the CrUX and Lighthouse CLS scores of PageSpeed Insights are broadly in line, this usually indicates there is a load CLS issue that was detected by Lighthouse. In this case Lighthouse will help with two audits to provide more information on images causing CLS due to missing width and height, and also list all the elements that shifted for the page load along with their CLS contribution. You can see these audits by filtering on the CLS audits:&lt;/p&gt;
&lt;img alt=&quot;Lighthouse Screenshot showing the CLS audits providing more information to help you identify and address CLS issues&quot; decoding=&quot;async&quot; height=&quot;544&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2C3v6dGwPx2yFyYpVdue.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2C3v6dGwPx2yFyYpVdue.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2C3v6dGwPx2yFyYpVdue.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2C3v6dGwPx2yFyYpVdue.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2C3v6dGwPx2yFyYpVdue.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2C3v6dGwPx2yFyYpVdue.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2C3v6dGwPx2yFyYpVdue.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2C3v6dGwPx2yFyYpVdue.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2C3v6dGwPx2yFyYpVdue.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2C3v6dGwPx2yFyYpVdue.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2C3v6dGwPx2yFyYpVdue.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2C3v6dGwPx2yFyYpVdue.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2C3v6dGwPx2yFyYpVdue.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2C3v6dGwPx2yFyYpVdue.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2C3v6dGwPx2yFyYpVdue.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2C3v6dGwPx2yFyYpVdue.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2C3v6dGwPx2yFyYpVdue.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/2C3v6dGwPx2yFyYpVdue.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Lighthouse will identify the elements that were shifted, but often these are the ones &lt;em&gt;impacted&lt;/em&gt; rather than the elements &lt;em&gt;causing&lt;/em&gt; the CLS. For example, if a new element is inserted into the DOM, the elements that are beneath it will show in this audit, but the root cause is the addition of the new element above. However, the shifted element should be sufficient to help you identify and resolve the root cause. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.chrome.com/docs/devtools/evaluate-performance/&quot; rel=&quot;noopener&quot;&gt;Performance panel&lt;/a&gt; in DevTools also highlights layout shifts in the &lt;strong&gt;Experience&lt;/strong&gt; section. The &lt;strong&gt;Summary&lt;/strong&gt; view for a &lt;code&gt;Layout Shift&lt;/code&gt; record includes the cumulative layout shift score as well as a rectangle overlay showing the affected regions. This is particularly helpful to get more detail on load CLS issues since this is easily replicated with a reload performance profile.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Layout Shift records being displayed in the Chrome DevTools performance panel when expanding the Experience section&quot; decoding=&quot;async&quot; height=&quot;438&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/ApDKifKCRNGWI2SXSR1g.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/ApDKifKCRNGWI2SXSR1g.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/ApDKifKCRNGWI2SXSR1g.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/ApDKifKCRNGWI2SXSR1g.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/ApDKifKCRNGWI2SXSR1g.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/ApDKifKCRNGWI2SXSR1g.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/ApDKifKCRNGWI2SXSR1g.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/ApDKifKCRNGWI2SXSR1g.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/ApDKifKCRNGWI2SXSR1g.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/ApDKifKCRNGWI2SXSR1g.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/ApDKifKCRNGWI2SXSR1g.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/ApDKifKCRNGWI2SXSR1g.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/ApDKifKCRNGWI2SXSR1g.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/ApDKifKCRNGWI2SXSR1g.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/ApDKifKCRNGWI2SXSR1g.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/ApDKifKCRNGWI2SXSR1g.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/ApDKifKCRNGWI2SXSR1g.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/ApDKifKCRNGWI2SXSR1g.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;After recording a new trace in the Performance panel, the &lt;b&gt;Experience&lt;/b&gt; section of the results is populated with a red-tinted bar displaying a &lt;code&gt;Layout Shift&lt;/code&gt; record. Clicking the record allows you to drill down into impacted elements (e.g. note the moved from/to entries).&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;identifying-post-load-cls-issues&quot;&gt;Identifying post-load CLS issues &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-cls/#identifying-post-load-cls-issues&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When the CrUX and Lighthouse CLS scores of PageSpeed Insights are not in line, then this likely indicates post-load CLS. Without field data helping to identify the reason (that we will cover next), these can be more tricky to track down.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://chrome.google.com/webstore/detail/web-vitals/ahfhijdlegdabablpippeagghigmibma&quot; rel=&quot;noopener&quot;&gt;Web Vitals Chrome extension&lt;/a&gt; can be used to monitor CLS as you interact with a page, either in a heads up display, or in the console—where you can &lt;a href=&quot;https://web.dev/debug-cwvs-with-web-vitals-extension/#cls-debug-information&quot;&gt;get more details above the elements shifted&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As an alternative to using the extension, you can browse your web page while &lt;a href=&quot;https://web.dev/cls/#measure-layout-shifts-in-javascript&quot;&gt;recording layout shifts using a Performance Observer&lt;/a&gt; pasted into the console.&lt;/p&gt;
&lt;p&gt;Once you are monitoring shifts you can try to replicate any post-load CLS issues. Scolling down a page is a common place for CLS to occur if content is lazy loaded and does not have space reserved for it. Content shifting on hover is another common post-load CLS cause. Both of these &amp;quot;interactions&amp;quot; are ineligible for the 500 milliseconds grace period as CLS during these periods are seen as being &amp;quot;unexpected shifts&amp;quot;, despite the user interaction, as they should not cause content to shift. Other interactions—such as clicks or taps—do have that grace period, but a common reason for CLS in these cases is taking longer than that 500 milliseconds to move or add content.&lt;/p&gt;
&lt;p&gt;We have a more detailed posted on &lt;a href=&quot;https://web.dev/debug-layout-shifts/&quot;&gt;debugging layout shifts&lt;/a&gt; for more information.&lt;/p&gt;
&lt;p&gt;Once you have identified any common causes of CLS, the &lt;a href=&quot;https://web.dev/optimize-cls/lighthouse-user-flows/#timespans&quot;&gt;timespans user flow mode of Lighthouse&lt;/a&gt; can also be used to ensure typical user flows do not regress by introducing layout shifts.&lt;/p&gt;
&lt;h3 id=&quot;measuring-cls-elements-in-the-field&quot;&gt;Measuring CLS elements in the field &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-cls/#measuring-cls-elements-in-the-field&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It is also recommended to monitor CLS in the field. This can be used to measure both the CLS and—perhaps more importantly—the elements impacting your CLS score in the field and feed them back to your analytics service.&lt;/p&gt;
&lt;p&gt;This can be invaluable in pointing you in the right direction of where the issue is as it can remove much of the guess work discribed above when you are trying to understand under what circumstances CLS is occuring. Again, be aware that this will measure the elements that shifted, rather than the root causes of those shifts, but this is often sufficient to identify the cause or at least to narrow down the problem.&lt;/p&gt;
&lt;p&gt;Measuring CLS in the field can also be used to rank the issues in order of importance based on most frequently experienced issues.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals#send-attribution-data&quot; rel=&quot;noopener&quot;&gt;attribution functionality of the &lt;code&gt;web-vitals&lt;/code&gt; library&lt;/a&gt; allows this additional information to be collected. Read our &lt;a href=&quot;https://web.dev/debug-performance-in-the-field/&quot;&gt;Debug performance in the field&lt;/a&gt; post for more information on how to do this. Other RUM providers have also started collecting and presenting this data similarly.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; RUM solutions that measure CLS in the field, including the &lt;code&gt;web-vitals&lt;/code&gt; library, may show differences that CrUX data as explained in the &lt;a href=&quot;https://web.dev/optimize-cls/crux-and-rum-differences/&quot;&gt;Why is CrUX data different from my RUM data?&lt;/a&gt; post. In particular, CLS that happens in iframes is not measurable from Web APIs but is visible to the user, and is therefore included in CrUX. So while field data can be invaluable for identifying CLS issues, be aware that it may be incomplete in certain scenarios. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;common-causes-of-cls&quot;&gt;Common causes of CLS &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-cls/#common-causes-of-cls&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once you have identified the causes of CLS, you can start working on fixing the issues. In this section we will show some of the more common reasons for CLS, and what you can do to avoid them.&lt;/p&gt;
&lt;h3 id=&quot;images-without-dimensions&quot;&gt;Images without dimensions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-cls/#images-without-dimensions&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Always include &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; size attributes on your images and video elements. Alternatively, reserve the required space with &lt;a href=&quot;https://web.dev/aspect-ratio/&quot;&gt;CSS &lt;code&gt;aspect-ratio&lt;/code&gt;&lt;/a&gt; or similar. This approach ensures that the browser can allocate the correct amount of space in the document while the image is loading.&lt;/p&gt;
  &lt;figure&gt;
    &lt;video controls=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; poster=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/8wKRITUkK3Zrp5jvQ1Xw.jpg?auto=format&quot;&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/tcFciHGuF3MxnTr1y5ue01OGLBn2/10TEOBGBqZm1SEXE7KiC.webm&quot; type=&quot;video/webm&quot; /&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/tcFciHGuF3MxnTr1y5ue01OGLBn2/WOQn6K6OQcoElRw0NCkZ.mp4&quot; type=&quot;video/mp4&quot; /&gt;    &lt;/video&gt;
   &lt;figcaption&gt;
      Images without width and height specified.
    &lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figure&gt;
    &lt;video controls=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; poster=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/wm4VqJtKvove6qjiIjic.jpg?auto=format&quot;&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/tcFciHGuF3MxnTr1y5ue01OGLBn2/38UiHViz44OWqlKFe1VC.webm&quot; type=&quot;video/webm&quot; /&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/tcFciHGuF3MxnTr1y5ue01OGLBn2/sFxDb36aEMvTPIyZHz1O.mp4&quot; type=&quot;video/mp4&quot; /&gt;    &lt;/video&gt;
   &lt;figcaption&gt;
      Images with width and height specified.
    &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Lighthouse report showing the before/after impact to Cumulative Layout Shift after setting dimensions on images&quot; decoding=&quot;async&quot; height=&quot;148&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/A2OyrzSXuW1qYGWAarGx.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/A2OyrzSXuW1qYGWAarGx.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/A2OyrzSXuW1qYGWAarGx.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/A2OyrzSXuW1qYGWAarGx.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/A2OyrzSXuW1qYGWAarGx.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/A2OyrzSXuW1qYGWAarGx.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/A2OyrzSXuW1qYGWAarGx.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/A2OyrzSXuW1qYGWAarGx.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/A2OyrzSXuW1qYGWAarGx.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/A2OyrzSXuW1qYGWAarGx.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/A2OyrzSXuW1qYGWAarGx.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/A2OyrzSXuW1qYGWAarGx.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/A2OyrzSXuW1qYGWAarGx.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/A2OyrzSXuW1qYGWAarGx.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/A2OyrzSXuW1qYGWAarGx.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/A2OyrzSXuW1qYGWAarGx.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/A2OyrzSXuW1qYGWAarGx.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/A2OyrzSXuW1qYGWAarGx.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Lighthouse 6.0 impact of setting image dimensions on CLS.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h4 id=&quot;history-of-width-and-height-attributes-on-images&quot;&gt;History of &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes on images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-cls/#history-of-width-and-height-attributes-on-images&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;In the early days of the web, developers would add &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes to their &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags to ensure sufficient space was allocated on the page before the browser started fetching images. This would minimize reflow and re-layout.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;puppy.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;640&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;360&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Puppy with balloons&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You may notice &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; above do not include units. These &amp;quot;pixel&amp;quot; dimensions would ensure a 640x360 area would be reserved. The image would stretch to fit this space, regardless of whether the true dimensions matched or not.&lt;/p&gt;
&lt;p&gt;When &lt;a href=&quot;https://www.smashingmagazine.com/2011/01/guidelines-for-responsive-web-design/&quot; rel=&quot;noopener&quot;&gt;Responsive Web Design&lt;/a&gt; was introduced, developers began to omit &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; and started using CSS to resize images instead:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* or max-width: 100%; */&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;A downside to this approach is space could only be allocated for an image once it began to download and the browser could determine its dimensions. As images loaded in, the page would reflow as each image appeared on screen. It became common for text to suddenly pop down the screen. This wasn&#39;t a great user experience at all.&lt;/p&gt;
&lt;p&gt;This is where aspect ratio comes in. The aspect ratio of an image is the ratio of its width to its height. It&#39;s common to see this expressed as two numbers separated by a colon (for example 16:9 or 4:3). For an x:y aspect ratio, the image is x units wide and y units high.&lt;/p&gt;
&lt;p&gt;This means if we know one of the dimensions, the other can be determined. For a 16:9 aspect ratio:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If puppy.jpg has a 360px height, width is 360 x (16 / 9) = 640px&lt;/li&gt;
&lt;li&gt;If puppy.jpg has a 640px width, height is 640 x (9 / 16) = 360px&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Knowing the aspect ratio allows the browser to calculate and reserve sufficient space for the height and associated area.&lt;/p&gt;
&lt;h4 id=&quot;modern-best-practice-for-setting-image-dimensions&quot;&gt;Modern best practice for setting image dimensions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-cls/#modern-best-practice-for-setting-image-dimensions&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Modern browsers now set the default aspect ratio of images based on an image&#39;s &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes so developers just need to set these, and include the above CSS, to prevent layout shifts:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- set a 640:360 i.e a 16:9 aspect ratio --&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;puppy.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;640&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;360&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Puppy with balloons&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;All browsers will then add a &lt;a href=&quot;https://html.spec.whatwg.org/multipage/rendering.html#attributes-for-embedded-content-and-images&quot; rel=&quot;noopener&quot;&gt;default aspect ratio&lt;/a&gt; based on the element&#39;s existing &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes.&lt;/p&gt;
&lt;p&gt;This calculates an aspect ratio based on the &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes before the image has loaded. It provides this information at the very start of layout calculation. As soon as an image is told to be a certain width (for example &lt;code&gt;width: 100%&lt;/code&gt;), the aspect ratio is used to calculate the height.&lt;/p&gt;
&lt;p&gt;This &lt;code&gt;aspect-ratio&lt;/code&gt; value is calculated by major browsers as the HTML is processed, rather than with a default User Agent stylesheet (see &lt;a href=&quot;https://jakearchibald.com/2022/img-aspect-ratio/#width--height-presentational-hints&quot; rel=&quot;noopener&quot;&gt;this post for a deep dive into why&lt;/a&gt;), so the value is displayed a little differently. For example, Chrome displays it like this in the Styles section of the Element panel:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;img[Attributes Style]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;aspect-ratio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto 640 / 360&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Safari behaves similarly by using a &lt;code&gt;HTML Attributes&lt;/code&gt; style source. Firefox does not currently display this calculated &lt;code&gt;aspect-ratio&lt;/code&gt; at all in it&#39;s Inspector panel, but does use it for layout.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;auto&lt;/code&gt; part of the above code is important as it causes the &lt;code&gt;640 / 360&lt;/code&gt; to be overriden with the image dimensions once the image is downloaded. If the image dimensions are different this will still cause some layout shift after the image loads, but this ensures the image aspect ratio is still used ultimately when it becomes available—as it was in the past—in case the HTML is incorrect. Plus, the shift is likely to be a lot smaller than the 0x0 default image size when dimensions are not provided!&lt;/p&gt;
&lt;p&gt;Tip: If you&#39;re having a hard time understanding aspect ratio, a handy &lt;a href=&quot;https://aspectratiocalculator.com/16-9.html&quot; rel=&quot;noopener&quot;&gt;calculator&lt;/a&gt; is available to help.&lt;/p&gt;
&lt;p&gt;For a fantastic deep-dive into aspect ratio with further thinking around responsive images, see &lt;a href=&quot;https://blog.logrocket.com/jank-free-page-loading-with-media-aspect-ratios/&quot; rel=&quot;noopener&quot;&gt;jank-free page loading with media aspect ratios&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If your image is in a container, you can use CSS to resize the image to the width of this container. We set &lt;code&gt;height: auto;&lt;/code&gt; to avoid the image height being a fixed value (for example &lt;code&gt;360px&lt;/code&gt;).&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h4 id=&quot;what-about-responsive-images&quot;&gt;What about responsive images? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-cls/#what-about-responsive-images&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When working with &lt;a href=&quot;https://web.dev/serve-responsive-images&quot;&gt;responsive images&lt;/a&gt;, &lt;code&gt;srcset&lt;/code&gt; defines the images you allow the browser to select between and what size each image is. To ensure &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; width and height attributes can be set, each image should use the same aspect ratio.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1000&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1000&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;puppy-1000.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;puppy-1000.jpg 1000w, puppy-2000.jpg 2000w, puppy-3000.jpg 3000w&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Puppy with balloons&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;What about &lt;a href=&quot;https://developer.mozilla.org/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images#Art_direction&quot; rel=&quot;noopener&quot;&gt;art direction&lt;/a&gt;?&lt;/p&gt;
&lt;p&gt;Pages may wish to include a cropped shot of an image on narrow viewports with the full image displayed on desktop.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(max-width: 799px)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;puppy-480w-cropped.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(min-width: 800px)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;puppy-800w.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;puppy-800w.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Puppy with balloons&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;It&#39;s very possible these images could have different aspect ratios. Chrome, Firefox, and Safari now support setting &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; on the &lt;code&gt;source&lt;/code&gt; children of the &lt;code&gt;picture&lt;/code&gt; element:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(max-width: 799px)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;puppy-480w-cropped.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;480&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;400/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(min-width: 800px)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;puppy-800w.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;800&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;400/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;puppy-800w.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Puppy with balloons&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;800&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;400/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;ads,-embeds,-and-other-late-loaded-content&quot;&gt;Ads, embeds, and other late-loaded content &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-cls/#ads,-embeds,-and-other-late-loaded-content&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As we have seen, images have special considerations. However, images are not the only type of content that can cause layout shifts. Ads, embeds, iframes, and other dynamically injected content can all cause content after these to shift down, increasing your CLS.&lt;/p&gt;
&lt;p&gt;Ads are one of the largest contributors to layout shifts on the web. Ad networks and publishers often support dynamic ad sizes. Ad sizes increase performance/revenue due to higher click rates and more ads competing in the auction. Unfortunately, this can lead to a suboptimal user experience due to ads pushing visible content you&#39;re viewing down the page.&lt;/p&gt;
&lt;p&gt;Embeddable widgets allow you to include portable web content in your page, such as videos from YouTube, maps from Google Maps, and social media posts. These widgets often aren&#39;t aware in advance just how large their contents will be. For example, in the case of a social media post, it might have an embedded image, video, multiple rows of text, or a number of other unpredictable factors. As a result, platforms offering embeds do not always reserve space for their widgets and so cause layout shifts when they finally load.&lt;/p&gt;
&lt;p&gt;The techniques for dealing with these are all similar. The major differences are how much control you have over the content that will be inserted. If this is inserted by a third-party like an ad partner, you may not know the exact size of content that will be inserted, nor be able to control any layout shifts happening within those embeds.&lt;/p&gt;
&lt;h4 id=&quot;statically-reserve-space-for-late-loading-content&quot;&gt;Statically reserve space for late-loading content &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-cls/#statically-reserve-space-for-late-loading-content&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When placing late-loading content in the content flow, layout shifts can be avoided by reserving the space for them in the initial layout.&lt;/p&gt;
&lt;p&gt;This can be as simple as adding a &lt;code&gt;min-height&lt;/code&gt; styling to reserve space or, for responsive content such as ads, using the new &lt;a href=&quot;https://web.dev/aspect-ratio/&quot;&gt;&lt;code&gt;aspect-ratio&lt;/code&gt;&lt;/a&gt; CSS property in a similar manner to the way browsers automatically use this for images with dimensions provided.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Three mobile devices with just text content in the first device, this is shifted down in the second device, and reserving space with a placeholder as shown in the third device prevents the shift&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/ThcGvVp0RiiABpmnWz7u.svg&quot; width=&quot;1180&quot; /&gt;
  &lt;figcaption&gt;
    Reserving space for ads can prevent layout shifts
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You may need to account for subtle differences in ad or placeholder sizes across form factors using media queries.&lt;/p&gt;
&lt;p&gt;For content that may not have a fixed height—like ads—you may not be able to reserve the exact amount of space needed to eliminate the layout shift entirely. If a smaller ad is served, a publisher can style the (larger) container to avoid layout shifts, or choose the most likely size for the ad slot based on historical data. The downside to this approach is that it will increase the amount of blank space, so keep in mind the trade-off here.&lt;/p&gt;
&lt;p&gt;Alternatively, set the initial size to the smallest size that will be used, and accept some level of shift for larger content. Using &lt;code&gt;min-height&lt;/code&gt;, as suggested above, will allow the parent element to grow as necessary. This will not fully eliminate CLS, but will hopefully reduce the impact of it to a more managable level. The default size of an empty element is 0px which gives maximum CLS, so any size is better than that!&lt;/p&gt;
&lt;p&gt;Try to avoid collapsing the reserved space if, for example, there is no ad returned, by showing a placeholder. Removing the space set aside for elements can cause just as much CLS as inserting content!&lt;/p&gt;
&lt;h4 id=&quot;avoid-placing-late-loading-content-near-the-top-of-the-viewport&quot;&gt;Avoid placing late-loading content near the top of the viewport &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-cls/#avoid-placing-late-loading-content-near-the-top-of-the-viewport&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Dynamically injected content near the top of the viewport may cause a greater layout shift than those at the middle. This is because elements inserted at the top generally have more content lower down, meaning more elements move when the late-loading content causes a shift.&lt;/p&gt;
&lt;p&gt;Conversely, dynamically injected content near the middle of the viewport may not shift as many elements as the content above it is less likely to move, but will still cause some CLS. Even content injected at the bottom of the screen will cause CLS as the content it replace is moved off-screen.&lt;/p&gt;
&lt;p&gt;The ideal scenario is not to shift any other content so reserving the appropriate space is preferred. Where this is not possible, minimizing the shifts can at least reduce the impact—both to your users and your CLS scores.&lt;/p&gt;
&lt;h4 id=&quot;avoid-inserting-new-content-without-a-user-interaction&quot;&gt;Avoid inserting new content without a user interaction &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-cls/#avoid-inserting-new-content-without-a-user-interaction&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;You&#39;ve probably experienced layout shifts due to UI that pops-in at the top or bottom of the viewport when you&#39;re trying to load a site. Similar to ads, this often happens with banners and forms that shift the rest of the page&#39;s content:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&amp;quot;Sign-up to our newsletter!&amp;quot; (whoa, slow down! we just met!)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&amp;quot;Related content&amp;quot;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&amp;quot;Install our [iOS/Android] app&amp;quot;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&amp;quot;We&#39;re still taking orders&amp;quot;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&amp;quot;GDPR notice&amp;quot;&lt;/p&gt;
&lt;figure&gt;
  &lt;video controls=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; poster=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PF9ulVHDQOvoWendb6ea.jpg?auto=format&quot;&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/tcFciHGuF3MxnTr1y5ue01OGLBn2/LEicZ7zHqGFrXl67Olve.webm&quot; type=&quot;video/webm&quot; /&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/tcFciHGuF3MxnTr1y5ue01OGLBn2/XFvOHc2OB8vUD9GbpL2w.mp4&quot; type=&quot;video/mp4&quot; /&gt;    &lt;/video&gt;
 &lt;figcaption&gt;
    Dynamic content without space reserved.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you need to display these types of UI affordances, reserve sufficient space in the viewport for it in advance (for example, using a placeholder or skeleton UI) so that when it loads, it does not cause content in the page to surprisingly shift around. Alternatively, ensure the element is not part of the document flow by overlaying the content where this makes sense. See the &lt;a href=&quot;https://web.dev/cookie-notice-best-practices/&quot;&gt;Best practices for cookie notices&lt;/a&gt; post for more recommendations on these types of components.&lt;/p&gt;
&lt;p&gt;In some cases adding content dynamically is an important part of user experience. For example, when loading more products to a list of items or when updating live feed content. There are several ways to avoid unexpected layout shifts in those cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Replace the old content with the new content within a fixed size container or use a carousel and remove the old content after the transition. Remember to disable any links and controls until the transition has completed to prevent accidental clicks or taps while the new content is coming in.&lt;/li&gt;
&lt;li&gt;Have the user initiate the load of new content, so they are not surprised by the shift (for example with a &amp;quot;Load more&amp;quot; or &amp;quot;Refresh&amp;quot; button). It&#39;s recommended to prefetch the content before the user interaction so that it shows up immediately. As a reminder, &lt;a href=&quot;https://web.dev/optimize-cls/cls/#user-initiated-layout-shifts&quot;&gt;layout shifts that occur within 500 milliseconds&lt;/a&gt; of user input are not counted towards CLS.&lt;/li&gt;
&lt;li&gt;Seamlessly load the content offscreen and overlay a notice to the user that it&#39;s available (for example, with a &amp;quot;Scroll up&amp;quot; button).&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Examples of dynamic content loading without causing unexpected layout shifts from Twitter and the Chloé website&quot; decoding=&quot;async&quot; height=&quot;458&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/OcYv93SYnIg1kfTihK6xqRDebvB2/TjsYVkcDf03ZOVCcsizv.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/OcYv93SYnIg1kfTihK6xqRDebvB2/TjsYVkcDf03ZOVCcsizv.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/OcYv93SYnIg1kfTihK6xqRDebvB2/TjsYVkcDf03ZOVCcsizv.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/OcYv93SYnIg1kfTihK6xqRDebvB2/TjsYVkcDf03ZOVCcsizv.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/OcYv93SYnIg1kfTihK6xqRDebvB2/TjsYVkcDf03ZOVCcsizv.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/OcYv93SYnIg1kfTihK6xqRDebvB2/TjsYVkcDf03ZOVCcsizv.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/OcYv93SYnIg1kfTihK6xqRDebvB2/TjsYVkcDf03ZOVCcsizv.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/OcYv93SYnIg1kfTihK6xqRDebvB2/TjsYVkcDf03ZOVCcsizv.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/OcYv93SYnIg1kfTihK6xqRDebvB2/TjsYVkcDf03ZOVCcsizv.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/OcYv93SYnIg1kfTihK6xqRDebvB2/TjsYVkcDf03ZOVCcsizv.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/OcYv93SYnIg1kfTihK6xqRDebvB2/TjsYVkcDf03ZOVCcsizv.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/OcYv93SYnIg1kfTihK6xqRDebvB2/TjsYVkcDf03ZOVCcsizv.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/OcYv93SYnIg1kfTihK6xqRDebvB2/TjsYVkcDf03ZOVCcsizv.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/OcYv93SYnIg1kfTihK6xqRDebvB2/TjsYVkcDf03ZOVCcsizv.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/OcYv93SYnIg1kfTihK6xqRDebvB2/TjsYVkcDf03ZOVCcsizv.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/OcYv93SYnIg1kfTihK6xqRDebvB2/TjsYVkcDf03ZOVCcsizv.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/OcYv93SYnIg1kfTihK6xqRDebvB2/TjsYVkcDf03ZOVCcsizv.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/OcYv93SYnIg1kfTihK6xqRDebvB2/TjsYVkcDf03ZOVCcsizv.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Examples of dynamic content loading without causing unexpected layout shifts. Left: Live feed content loading on Twitter. Right: &quot;Load More&quot; example on Chloé website. Check out how the YNAP team &lt;a href=&quot;https://medium.com/ynap-tech/how-to-optimize-for-cls-when-having-to-load-more-content-3f60f0cf561c&quot;&gt;optimized for CLS when loading more content&lt;/a&gt;.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; If content is likely to take more than 500 milliseconds—for example it requires a network fetch—then reserving the expected space within that 500 millisecond timeframe and taking the impact of any future shift up front allows you to ensure any shifts will not be included in the CLS score. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;animations&quot;&gt;Animations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-cls/#animations&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Changes to CSS property values can require the browser to react to these changes. A number of values trigger re-layout, paint, and composite such as &lt;code&gt;box-shadow&lt;/code&gt; and &lt;code&gt;box-sizing&lt;/code&gt;. Try to avoid animating these.&lt;/p&gt;
&lt;p&gt;A number of CSS properties can be changed in a much more performant manner. For example, &lt;code&gt;transform&lt;/code&gt; animations can be used to translate, scale, rotate, or skew without triggering a re-layout and so completely avoiding layout shifts.&lt;/p&gt;
&lt;p&gt;When animations are instead done by changing &lt;code&gt;top&lt;/code&gt; and &lt;code&gt;left&lt;/code&gt; CSS properties instead of using &lt;code&gt;translate&lt;/code&gt;, layout shifts occur. This happens &lt;strong&gt;even when the element being moved is in it&#39;s own layer and so does not cause shifts to other elements&lt;/strong&gt;. Composited animations via &lt;code&gt;translate&lt;/code&gt; are exempt from CLS as they cannot impact other elements. There are also other considerable performance benefits of using non-composited animations since they do no cause re-layout and therefore are much less work for the browser.&lt;/p&gt;
&lt;p&gt;To learn more about what CSS properties trigger layout see &lt;a href=&quot;https://web.dev/animations-guide/&quot;&gt;High-performance animations&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;web-fonts&quot;&gt;Web fonts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-cls/#web-fonts&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Downloading and rendering web fonts is typically handled in one of two ways before the web font is downloaded:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The fallback font is swapped with the web font (FOUT—flash of unstyled text)&lt;/li&gt;
&lt;li&gt;&amp;quot;Invisible&amp;quot; text is displayed using the fallback font until a web font is available and the text is made visible (FOIT—flash of invisible text)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is important to understand that &lt;strong&gt;both of these can cause layout shifts&lt;/strong&gt;. Even though the text is invisible, it is laid out using the fallback font. This means the text block using the font, and the surrounding content, shifts when the web font loads—in the exact same way as for the visible font for FOUT.&lt;/p&gt;
&lt;p&gt;The following tools can help you minimize this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;font-display: optional&lt;/code&gt; can avoid a re-layout as the web font is only used if it is available by the time of initial layout.&lt;/li&gt;
&lt;li&gt;Ensure the appropriate fallback font is used. For example, using &lt;code&gt;font-family: &amp;quot;Google Sans&amp;quot;, sans-serif;&lt;/code&gt; will ensure the browser&#39;s &lt;code&gt;sans-serif&lt;/code&gt; fallback font is used while &lt;code&gt;&amp;quot;Google Sans&amp;quot;&lt;/code&gt; is loaded. Not specifying a fallback font using just &lt;code&gt;font-family: &amp;quot;Google Sans&amp;quot;&lt;/code&gt; will mean the default font is used, which on Chrome is &amp;quot;Times&amp;quot;—a serif font which is a worse match than the default &lt;code&gt;sans-serif&lt;/code&gt; font.&lt;/li&gt;
&lt;li&gt;Minimize the size differences between the fallback font and the web font using the new &lt;code&gt;size-adjust&lt;/code&gt;, &lt;code&gt;ascent-override&lt;/code&gt;, &lt;code&gt;descent-override&lt;/code&gt;, and &lt;code&gt;line-gap-override&lt;/code&gt; APIs as detailed in the &lt;a href=&quot;https://developer.chrome.com/blog/font-fallbacks/&quot; rel=&quot;noopener&quot;&gt;Improved font fallbacks&lt;/a&gt; post.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://web.dev/optimize-webfont-loading/#the-font-loading-api&quot;&gt;Font Loading API&lt;/a&gt; can reduce the time it takes to get necessary fonts.&lt;/li&gt;
&lt;li&gt;Load critical web fonts as early as possible using &lt;code&gt;&amp;lt;link rel=preload&amp;gt;&lt;/code&gt;. A preloaded font will have a higher chance to meet the first paint, in which case there&#39;s no layout shifting.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Read &lt;a href=&quot;https://web.dev/font-best-practices/&quot;&gt;Best practices for fonts&lt;/a&gt; for other font best practices.&lt;/p&gt;
&lt;h2 id=&quot;reduce-cls-by-ensuring-pages-are-eligible-for-the-bfcache&quot;&gt;Reduce CLS by ensuring pages are eligible for the bfcache &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-cls/#reduce-cls-by-ensuring-pages-are-eligible-for-the-bfcache&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A highly effective technique for keeping CLS scores low is to ensure your web pages are eligible for the &lt;a href=&quot;https://web.dev/bfcache/&quot;&gt;back/forward cache&lt;/a&gt; (bfcache).&lt;/p&gt;
&lt;p&gt;The bfcache keeps pages in browsers memory for a short period after you navigate away so if you return to them, then they will be restored exactly as you left them. This means the fully loaded page is instantly available—without any shifts which may be normally seen during load due to any of the reasons above.&lt;/p&gt;
&lt;p&gt;While this does potentially still mean the initial page load encounters layout shifts, when a user goes back through pages they are not seeing the same layout shifts repeatedly. You should always aim to avoid the shifts even on the initial load, but where that is more tricky to resolve fully, you can at least reduce the impact by avoiding them on any bfcache navigations.&lt;/p&gt;
&lt;p&gt;Back and forward navigations are common on many sites. For example, returning to a contents page, or a category page, or search results.&lt;/p&gt;
&lt;p&gt;When this was rolled out to Chrome, we saw &lt;a href=&quot;https://twitter.com/anniesullie/status/1491399685961293828?s=20&amp;amp;t=Qj_nzSRZD0_c-HaAnfr98Q&quot; rel=&quot;noopener&quot;&gt;noticeable improvements in CLS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The bfcache is used by default by all browsers, but some sites are ineligible for the bfcache due to a variety of reasons. Read &lt;a href=&quot;https://web.dev/bfcache/&quot;&gt;the bfcache article&lt;/a&gt; for more details on how to test and identify any issues preventing bfcache usage to ensure you are making full use of this feature to help your overall CLS score for your site.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-cls/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are a number of techniques to identify and improve CLS as detailed above. There are allowances built into Core Web Vitals, so even if you cannot eliminate CLS completely, using some of these techniques should allow you to reduce the impact. This will be better for your users and hopefully allow you to stay within those limits.&lt;/p&gt;
&lt;p&gt;That&#39;s it for this guide. We hope it helps keep your pages just a little less shifty :)&lt;/p&gt;
</content>
    <author>
      <name>Barry Pollard</name>
    </author><author>
      <name>Addy Osmani</name>
    </author>
  </entry>
  
  <entry>
    <title>Preloading responsive images</title>
    <link href="https://web.dev/preload-responsive-images/"/>
    <updated>2019-09-30T00:00:00Z</updated>
    <id>https://web.dev/preload-responsive-images/</id>
    <content type="html" mode="escaped">&lt;div class=&quot;wdi-browser-compat&quot;&gt;
  &lt;span class=&quot;wdi-browser-compat__label&quot;&gt;Browser support&lt;/span&gt;
  &lt;ul class=&quot;wdi-browser-compat__items&quot;&gt;
    &lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;chrome&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Chrome 73, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      73
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox 78, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      78
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Edge 79, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      79
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Safari, Not supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;p&gt;&lt;/p&gt;
  &lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;This article gives me an opportunity to discuss two of my favorite things: responsive images &lt;em&gt;and&lt;/em&gt; preload. As someone who was heavily involved in developing both of those features, I&#39;m super excited to see them working together!&lt;/p&gt;
&lt;h2 id=&quot;responsive-images-overview&quot;&gt;Responsive images overview &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-responsive-images/#responsive-images-overview&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Suppose you&#39;re browsing the web on a screen that&#39;s 300 pixels wide, and the page just requested an image that&#39;s 1500 pixels wide. That page just wasted a lot of your cellular data because your screen can&#39;t do anything with all of that extra resolution. Ideally, the browser should fetch a version of the image that&#39;s just a &lt;em&gt;little&lt;/em&gt; wider than your screen size, say 325 pixels. This ensures a high-resolution image without wasting data. And, even better, the image will load faster. &lt;a href=&quot;https://web.dev/serve-responsive-images/#serve-multiple-image-versions&quot;&gt;Responsive images&lt;/a&gt; enable browsers to fetch different image resources to different devices. If you don&#39;t use an &lt;a href=&quot;https://web.dev/image-cdns/&quot;&gt;image CDN&lt;/a&gt; you need to save multiple dimensions for each image and specify them in the &lt;code&gt;srcset&lt;/code&gt; attribute. The &lt;code&gt;w&lt;/code&gt; value tells the browser the width of each version. Depending on the device, the browser can choose the appropriate one:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;small.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;small.jpg 500w, medium.jpg 1000w, large.jpg 1500w&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;…&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;preload-overview&quot;&gt;Preload overview &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-responsive-images/#preload-overview&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/preload-critical-assets&quot;&gt;Preload&lt;/a&gt; lets you tell the browser about critical resources that you want to load as soon as possible, before they are discovered in HTML. This is especially useful for resources that are not easily discoverable, such as fonts included in stylesheets, background images, or resources loaded from a script.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;important.png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;responsive-images-preload-=-faster-image-loads&quot;&gt;Responsive images + preload = faster image loads &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-responsive-images/#responsive-images-preload-=-faster-image-loads&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Responsive images and preload have been available for the last few years, but at the same time something was missing: there was no way to preload responsive images. &lt;a href=&quot;https://developers.google.com/web/updates/2019/03/nic73#more&quot; rel=&quot;noopener&quot;&gt;Starting in Chrome 73&lt;/a&gt;, the browser can preload the right variant of responsive images specified in &lt;code&gt;srcset&lt;/code&gt; before it discovers the &lt;code&gt;img&lt;/code&gt; tag!&lt;/p&gt;
&lt;p&gt;Depending on your site&#39;s structure, that could mean significantly faster image display! We ran tests on a site that uses JavaScript to lazy-load responsive images. Preloading resulted in images loading 1.2 seconds faster.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt;  Responsive images are &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/img#Browser_compatibility&quot;&gt;supported in all modern browsers&lt;/a&gt; while preloading them is &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Preloading_content#Browser_compatibility&quot;&gt;supported only in Chromium-based browsers&lt;/a&gt;.  &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;imagesrcset-and-imagesizes&quot;&gt;&lt;code&gt;imagesrcset&lt;/code&gt; and &lt;code&gt;imagesizes&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-responsive-images/#imagesrcset-and-imagesizes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To preload responsive images, new attributes were recently added to the &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; element: &lt;code&gt;imagesrcset&lt;/code&gt; and &lt;code&gt;imagesizes&lt;/code&gt;.  They are used with &lt;code&gt;&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&lt;/code&gt; and match the &lt;code&gt;srcset&lt;/code&gt; and &lt;code&gt;sizes&lt;/code&gt; syntax used in &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element.&lt;/p&gt;
&lt;p&gt;For example, if you want to preload a responsive image specified with:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;wolf.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;wolf_400px.jpg 400w, wolf_800px.jpg 800w, wolf_1600px.jpg 1600w&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;sizes&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;50vw&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;A rad wolf&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You can do that by adding the following to your HTML&#39;s &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;wolf.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;imagesrcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;wolf_400px.jpg 400w, wolf_800px.jpg 800w, wolf_1600px.jpg 1600w&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;imagesizes&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;50vw&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This kicks off a request using the same resource selection logic that &lt;code&gt;srcset&lt;/code&gt; and &lt;code&gt;sizes&lt;/code&gt; will apply.&lt;/p&gt;
&lt;h2 id=&quot;use-cases&quot;&gt;Use cases &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-responsive-images/#use-cases&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;preloading-dynamically-injected-responsive-images&quot;&gt;Preloading dynamically-injected responsive images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-responsive-images/#preloading-dynamically-injected-responsive-images&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Let&#39;s say you&#39;re dynamically-loading hero images as part of a slideshow and know which image will be displayed first. In that case, you probably want to avoid waiting for the script before loading the image in question, as that would delay when users can see it.&lt;/p&gt;
&lt;p&gt;You can inspect this issue on a website with a dynamically-loaded image gallery:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open &lt;a href=&quot;https://responsive-preload.glitch.me/no_preload.html&quot; rel=&quot;noopener&quot;&gt;this example website&lt;/a&gt; in a new tab.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the &lt;strong&gt;Network&lt;/strong&gt; tab.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the &lt;strong&gt;Throttling&lt;/strong&gt; drop-down list, select &lt;strong&gt;Fast 3G&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Disable the &lt;strong&gt;Disable cache&lt;/strong&gt; checkbox.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reload the page.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
&lt;img alt=&quot;Screenshot of Chrome DevTools Network panel.&quot; decoding=&quot;async&quot; height=&quot;481&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/cyocwRmB3XlfY26vUZ5h.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/cyocwRmB3XlfY26vUZ5h.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/cyocwRmB3XlfY26vUZ5h.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/cyocwRmB3XlfY26vUZ5h.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/cyocwRmB3XlfY26vUZ5h.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/cyocwRmB3XlfY26vUZ5h.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/cyocwRmB3XlfY26vUZ5h.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/cyocwRmB3XlfY26vUZ5h.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/cyocwRmB3XlfY26vUZ5h.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/cyocwRmB3XlfY26vUZ5h.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/cyocwRmB3XlfY26vUZ5h.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/cyocwRmB3XlfY26vUZ5h.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/cyocwRmB3XlfY26vUZ5h.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/cyocwRmB3XlfY26vUZ5h.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/cyocwRmB3XlfY26vUZ5h.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/cyocwRmB3XlfY26vUZ5h.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/cyocwRmB3XlfY26vUZ5h.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/cyocwRmB3XlfY26vUZ5h.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;figcaption&gt;This waterfall shows that the images only start loading after the browser has finished running the script, introducing unnecessary delay to the time the image is initially displayed to the user.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Using &lt;code&gt;preload&lt;/code&gt; helps here because the image starts loading ahead of time and is likely to already be there when the browser needs to display it.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Screenshot of Chrome DevTools Network panel.&quot; decoding=&quot;async&quot; height=&quot;481&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/rIRdFypLWf1ljMaXCVCs.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/rIRdFypLWf1ljMaXCVCs.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/rIRdFypLWf1ljMaXCVCs.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/rIRdFypLWf1ljMaXCVCs.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/rIRdFypLWf1ljMaXCVCs.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/rIRdFypLWf1ljMaXCVCs.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/rIRdFypLWf1ljMaXCVCs.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/rIRdFypLWf1ljMaXCVCs.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/rIRdFypLWf1ljMaXCVCs.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/rIRdFypLWf1ljMaXCVCs.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/rIRdFypLWf1ljMaXCVCs.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/rIRdFypLWf1ljMaXCVCs.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/rIRdFypLWf1ljMaXCVCs.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/rIRdFypLWf1ljMaXCVCs.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/rIRdFypLWf1ljMaXCVCs.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/rIRdFypLWf1ljMaXCVCs.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/rIRdFypLWf1ljMaXCVCs.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/rIRdFypLWf1ljMaXCVCs.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;figcaption&gt;This waterfall shows that the first image started loading at the same time as the script, avoiding unnecessary delays and resulting in faster displaying images.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To see the difference that preloading makes, you can inspect the same dynamically-loaded image gallery but &lt;a href=&quot;https://responsive-preload.glitch.me/preload.html&quot; rel=&quot;noopener&quot;&gt;with preloaded first image&lt;/a&gt; by following the steps from the first example.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; An alternative way to avoid the problem would be to use a markup-based carousel and have the &lt;a href=&quot;https://hacks.mozilla.org/2017/09/building-the-dom-faster-speculative-parsing-async-defer-and-preload/&quot;&gt;browser&#39;s preloader&lt;/a&gt; pick up the required resources. However, this approach may not always be practical. (For example, if you are reusing an existing component, which is not markup-based.) &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;preloading-background-images-using-image-set&quot;&gt;Preloading background images using image-set &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-responsive-images/#preloading-background-images-using-image-set&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you have different background images for different screen densities, you can specify them in your CSS with the &lt;code&gt;image-set&lt;/code&gt; syntax. The browser can then choose which one to display based on the screen&#39;s &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Window/devicePixelRatio&quot; rel=&quot;noopener&quot;&gt;DPR&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;image-set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;cat.png&quot;&lt;/span&gt; 1x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;cat-2x.png&quot;&lt;/span&gt; 2x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt;  The above syntax ignores the fact that vendor prefixes are needed for this feature in both Chromium and WebKit based browsers. If you&#39;re planning to use this feature, you should consider using &lt;a href=&quot;https://github.com/postcss/autoprefixer&quot;&gt;Autoprefixer&lt;/a&gt; (&lt;a href=&quot;https://goonlinetools.com/autoprefixer/&quot;&gt;available as an online tool&lt;/a&gt;) to address that automatically.  &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The problem with CSS background images is that they are discovered by the browser only after it has downloaded and processed all the CSS in the page&#39;s &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;, which can be a lot of CSS…&lt;/p&gt;
&lt;p&gt;You can inspect this issue on an example website with &lt;a href=&quot;https://responsive-preload.glitch.me/background_no_preload.html&quot; rel=&quot;noopener&quot;&gt;responsive background image&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Screenshot of Chrome DevTools Network panel.&quot; decoding=&quot;async&quot; height=&quot;451&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/7sjFt1RsoEOKn5zlS5zb.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/7sjFt1RsoEOKn5zlS5zb.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/7sjFt1RsoEOKn5zlS5zb.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/7sjFt1RsoEOKn5zlS5zb.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/7sjFt1RsoEOKn5zlS5zb.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/7sjFt1RsoEOKn5zlS5zb.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/7sjFt1RsoEOKn5zlS5zb.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/7sjFt1RsoEOKn5zlS5zb.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/7sjFt1RsoEOKn5zlS5zb.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/7sjFt1RsoEOKn5zlS5zb.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/7sjFt1RsoEOKn5zlS5zb.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/7sjFt1RsoEOKn5zlS5zb.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/7sjFt1RsoEOKn5zlS5zb.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/7sjFt1RsoEOKn5zlS5zb.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/7sjFt1RsoEOKn5zlS5zb.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/7sjFt1RsoEOKn5zlS5zb.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/7sjFt1RsoEOKn5zlS5zb.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/7sjFt1RsoEOKn5zlS5zb.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;figcaption&gt;In this example, the image download doesn&#39;t start until the CSS is fully downloaded, resulting in unnecessary lag to the image&#39;s display.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Responsive image preloading provides a simple and hack-free way to load those images faster.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;imagesrcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;cat.png 1x, cat-2x.png 2x&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Note that by excluding the &lt;code&gt;href&lt;/code&gt; attribute, you can ensure the browsers that do not support &lt;code&gt;imagesrcset&lt;/code&gt; on the &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; element, but do support &lt;code&gt;image-set&lt;/code&gt; in CSS, will not download an incorrect source. However, they will also not benefit from the preload in this case.&lt;/p&gt;
&lt;p&gt;You can inspect how the previous example behaves with &lt;a href=&quot;https://responsive-preload.glitch.me/background_preload.html&quot; rel=&quot;noopener&quot;&gt;preloaded responsive background image&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Screenshot of Chrome DevTools Network panel.&quot; decoding=&quot;async&quot; height=&quot;439&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/dOI6EmChfahBujnZOke7.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/dOI6EmChfahBujnZOke7.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/dOI6EmChfahBujnZOke7.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/dOI6EmChfahBujnZOke7.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/dOI6EmChfahBujnZOke7.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/dOI6EmChfahBujnZOke7.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/dOI6EmChfahBujnZOke7.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/dOI6EmChfahBujnZOke7.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/dOI6EmChfahBujnZOke7.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/dOI6EmChfahBujnZOke7.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/dOI6EmChfahBujnZOke7.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/dOI6EmChfahBujnZOke7.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/dOI6EmChfahBujnZOke7.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/dOI6EmChfahBujnZOke7.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/dOI6EmChfahBujnZOke7.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/dOI6EmChfahBujnZOke7.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/dOI6EmChfahBujnZOke7.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/dOI6EmChfahBujnZOke7.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;figcaption&gt;Here the image and CSS start downloading at the same time, avoiding delays and resulting in a faster loading image.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;preloading-responsive-images-in-action&quot;&gt;Preloading responsive images in action &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-responsive-images/#preloading-responsive-images-in-action&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Preloading your responsive images can speed them up in theory, but what does it do in practice?&lt;/p&gt;
&lt;p&gt;To answer that I created two copies of a &lt;a href=&quot;https://github.com/GoogleChromeLabs/sample-pie-shop&quot; rel=&quot;noopener&quot;&gt;demo PWA shop&lt;/a&gt;: &lt;a href=&quot;https://20190710t144416-dot-pie-shop-app.appspot.com/apparel&quot; rel=&quot;noopener&quot;&gt;one that does not preload images&lt;/a&gt;, and &lt;a href=&quot;https://20190710t132936-dot-pie-shop-app.appspot.com/apparel&quot; rel=&quot;noopener&quot;&gt;one that preloads some of them&lt;/a&gt;. Since the site lazy loads images using JavaScript, it&#39;s likely to benefit from preloading the ones that will be in the initial viewport.&lt;/p&gt;
&lt;p&gt;That gave me the following results for &lt;a href=&quot;https://www.webpagetest.org/result/190710_VM_30b9d4c993a1e60befba17e1261ba1ca/&quot; rel=&quot;noopener&quot;&gt;no preload&lt;/a&gt; and for &lt;a href=&quot;https://www.webpagetest.org/result/190710_7B_a99e792121760f81a270b4b9c847797b/&quot; rel=&quot;noopener&quot;&gt;image preload&lt;/a&gt;. Looking at the raw numbers we see that &lt;a href=&quot;https://github.com/WPO-Foundation/webpagetest-docs/blob/main/src/getting-started.md#start-render&quot; rel=&quot;noopener&quot;&gt;Start Render&lt;/a&gt; stayed the same, &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/speed-index/&quot; rel=&quot;noopener&quot;&gt;Speed Index&lt;/a&gt; slightly improved (273 ms, as images arrive faster, but don&#39;t take up a huge chunk of the pixel area), but the real metric which captures the difference is the &lt;a href=&quot;https://github.com/WPO-Foundation/webpagetest/blob/master/docs/Metrics/HeroElements.md&quot; rel=&quot;noopener&quot;&gt;Last Painted Hero&lt;/a&gt; metric, which improved by 1.2 seconds. 🎉🎉&lt;/p&gt;
&lt;p&gt;Of course, nothing captures the visual difference quite like a filmstrip comparison:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Screenshot of WebPageTest filmstrip comparison showing preloaded images are displayed about 1.5 seconds faster.&quot; decoding=&quot;async&quot; height=&quot;328&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/sXyZOvsNoAY0K2NRqT4U.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/sXyZOvsNoAY0K2NRqT4U.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/sXyZOvsNoAY0K2NRqT4U.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/sXyZOvsNoAY0K2NRqT4U.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/sXyZOvsNoAY0K2NRqT4U.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/sXyZOvsNoAY0K2NRqT4U.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/sXyZOvsNoAY0K2NRqT4U.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/sXyZOvsNoAY0K2NRqT4U.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/sXyZOvsNoAY0K2NRqT4U.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/sXyZOvsNoAY0K2NRqT4U.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/sXyZOvsNoAY0K2NRqT4U.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/sXyZOvsNoAY0K2NRqT4U.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/sXyZOvsNoAY0K2NRqT4U.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/sXyZOvsNoAY0K2NRqT4U.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/sXyZOvsNoAY0K2NRqT4U.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/sXyZOvsNoAY0K2NRqT4U.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/sXyZOvsNoAY0K2NRqT4U.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/sXyZOvsNoAY0K2NRqT4U.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;figcaption&gt;The filmstrip shows that images arrive significantly faster when preloaded, resulting in a hugely-improved user experience.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;preload-and-lesspicturegreater&quot;&gt;Preload and &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt;? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-responsive-images/#preload-and-lesspicturegreater&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you&#39;re familiar with responsive images, you may be wondering &amp;quot;What about &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/picture&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt;&lt;/a&gt;?&amp;quot;.&lt;/p&gt;
&lt;p&gt;The Web Performance Working Group is talking about adding a preload equivalent for &lt;code&gt;srcset&lt;/code&gt; and &lt;code&gt;sizes&lt;/code&gt;, but not the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element, which tackles the &lt;a href=&quot;https://web.dev/codelab-art-direction/&quot;&gt;&amp;quot;art direction&amp;quot;&lt;/a&gt; use-case.&lt;/p&gt;
&lt;p&gt;Why is this use-case being &amp;quot;neglected&amp;quot;?&lt;/p&gt;
&lt;p&gt;While there&#39;s interest in solving that use case as well, there are still a number of &lt;a href=&quot;https://calendar.perfplanet.com/2018/how-the-sausage-is-made-webperfwg-meeting-summary/&quot; rel=&quot;noopener&quot;&gt;technical issues to sort out&lt;/a&gt; which means that a solution here would have significant complexity. On top of that, it seems like for the most part, the use-case can be addressed today, even if in a hacky way (see below).&lt;/p&gt;
&lt;p&gt;Given that, the Web Performance WG decided to ship &lt;code&gt;srcset&lt;/code&gt; first and see if the demand for equivalent &lt;code&gt;picture&lt;/code&gt; support arises.&lt;/p&gt;
&lt;p&gt;If you do find yourself in a position to preload &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; you may be able to use the following technique as a workaround.&lt;/p&gt;
&lt;p&gt;Given the following scenario:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;small_cat.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(max-width: 400px)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;medium_cat.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(max-width: 800px)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;large_cat.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element&#39;s logic (or the image source selection logic, to be precise), would be to go over the &lt;code&gt;media&lt;/code&gt; attributes of the &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; elements in order, find the first one that matches, and use the attached resource.&lt;/p&gt;
&lt;p&gt;Because responsive preload has no notion of &amp;quot;order&amp;quot; or &amp;quot;first match&amp;quot;, the breakpoints need to be translated into something like:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;small_cat.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(max-width: 400px)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;medium_cat.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(min-width: 400.1px) and (max-width: 800px)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;large_cat.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(min-width: 800.1px)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;preload-and-type&quot;&gt;Preload and &lt;code&gt;type&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-responsive-images/#preload-and-type&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element also supports matching on the first &lt;code&gt;type&lt;/code&gt;, to allow different image formats to be provided and the browser to pick the first image format it supports. This use case is not currently supported with preload.&lt;/p&gt;
&lt;p&gt;For sites using this, avoiding preload is the best option, and instead having the &lt;a href=&quot;https://web.dev/preload-scanner/&quot;&gt;preload scanner&lt;/a&gt; pick these up from the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; elements instead. This is a best practice anyway, particular with the support of &lt;a href=&quot;https://web.dev/priority-hints/&quot;&gt;Priority Hints&lt;/a&gt; which allows the appropriate image to be prioritised in a better manner than preload alone.&lt;/p&gt;
&lt;h2 id=&quot;effects-on-largest-contentful-paint-lcp&quot;&gt;Effects on Largest Contentful Paint (LCP) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-responsive-images/#effects-on-largest-contentful-paint-lcp&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Since images can be &lt;a href=&quot;https://web.dev/lcp/#what-elements-are-considered&quot;&gt;candidates for Largest Contentful Paint (LCP)&lt;/a&gt;, preloading them may improve your website&#39;s LCP. Using the techniques above, you can also ensure that your responsive images will load more quickly.&lt;/p&gt;
&lt;p&gt;Regardless of whether the image you&#39;re preloading is responsive, be aware that preloads work especially well when the image resource isn&#39;t discoverable in the initial markup payload. For websites that send complete markup from the server, you may not realize a huge benefit. However, if your website renders markup on the client—which sidesteps the browser&#39;s &lt;a href=&quot;https://web.dev/preload-scanner/&quot;&gt;preload scanner&lt;/a&gt;—there&#39;s an opportunity on the table to preload potential LCP images to improve performance.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-responsive-images/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Responsive image preload gives us new and exciting possibilities to preload responsive images in ways that were previously only possible using hacks. It&#39;s an important new addition to the speed-conscious developer&#39;s toolbox and enables us to make sure the important images we want to get in front of our users as soon as possible will be there when we need them.&lt;/p&gt;
</content>
    <author>
      <name>Yoav Weiss</name>
    </author><author>
      <name>Barry Pollard</name>
    </author>
  </entry>
  
  <entry>
    <title>Use image CDNs to optimize images</title>
    <link href="https://web.dev/image-cdns/"/>
    <updated>2019-08-14T00:00:00Z</updated>
    <id>https://web.dev/image-cdns/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;why-use-an-image-cdn&quot;&gt;Why use an image CDN? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#why-use-an-image-cdn&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Image content delivery networks (CDNs) are excellent at optimizing images. Switching to an image CDN can yield a &lt;a href=&quot;https://www.youtube.com/watch?v=YJGCZCaIZkQ&amp;amp;t=1010s&quot; rel=&quot;noopener&quot;&gt;40–80% savings&lt;/a&gt; in image file size. In theory, it&#39;s possible to achieve the same results using only build scripts, but it&#39;s rare in practice.&lt;/p&gt;
&lt;h2 id=&quot;whats-an-image-cdn&quot;&gt;What&#39;s an image CDN? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#whats-an-image-cdn&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Image CDNs specialize in the transformation, optimization, and delivery of images. You can also think of them as APIs for accessing and manipulating the images used on your site. For images loaded from an image CDN, an image URL indicates not only which image to load, but also parameters like size, format, and quality. This makes it easy to create variations of an image for different use cases.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Shows the request/response flow between the image CDN and the client. Parameters like size and format are used to request variations of the same image.&quot; decoding=&quot;async&quot; height=&quot;408&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Examples of transformations image CDNs can perform based on parameters in image URLs.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Image CDNs are different from build-time image optimization scripts in that they create new versions of images as they&#39;re needed. As a result, CDNs are generally better suited to creating images that are heavily customized for each individual client than build scripts are.&lt;/p&gt;
&lt;h2 id=&quot;how-image-cdns-use-urls-to-indicate-optimization-options&quot;&gt;How image CDNs use URLs to indicate optimization options &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#how-image-cdns-use-urls-to-indicate-optimization-options&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Image URLs used by image CDNs convey important information about an image and the transformations and optimizations that should be applied to it. URL formats will vary depending on image CDN, but at a high-level, they all have similar features. Let&#39;s walk through some of the most common features.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Image URLs typically consist of the following components: origin, image, security key, and transformations.&quot; decoding=&quot;async&quot; height=&quot;127&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;origin&quot;&gt;Origin &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#origin&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;An image CDN can live on your own domain or the domain of your image CDN. Third-party image CDNs typically offer the option of using a custom domain for a fee. Using your own domain makes it easier to switch image CDNs at a later date because no URL changes will be needed.&lt;/p&gt;
&lt;p&gt;The example above uses the image CDN&#39;s domain (&amp;quot;example-cdn.com&amp;quot;) with a personalized subdomain, rather than a custom domain.&lt;/p&gt;
&lt;h3 id=&quot;image&quot;&gt;Image &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#image&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Image CDNs can usually be configured to automatically retrieve images from their existing locations when they&#39;re needed. This capability is often achieved by including the complete URL of the &lt;em&gt;existing image&lt;/em&gt; within the URL for the image generated by the image CDN. For example, you might see a URL that looks like this: &lt;code&gt;https://my-site.example-cdn.com/https://flowers.com/daisy.jpg/quality=auto&lt;/code&gt;. This URL would retrieve and optimize the image that exists at &lt;code&gt;https://flowers.com/daisy.jpg&lt;/code&gt;.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; As the above example shows, the file exension requested (&lt;code&gt;.jpg&lt;/code&gt; is this case) may not be the same as the image file format returned (WebP in this example). The &lt;code&gt;content-type&lt;/code&gt; HTTP Header will inform the browser which format the URL is in so it can process it appropriately. However, this may cause some confusion if the file is saved to disk and used by another program that expects the file format to match the file extension. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Another widely supported way of uploading images to an image CDN is to send them via an HTTP POST request to the image CDN&#39;s API.&lt;/p&gt;
&lt;h3 id=&quot;security-key&quot;&gt;Security key &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#security-key&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A security key prevents other people from creating new versions of your images. If this feature is enabled, each new version of an image requires a unique security key. If someone tries to change the parameters of the image URL but doesn&#39;t provide a valid security key, they won&#39;t be able to create a new version. Your image CDN will take care of the details of generating and tracking security keys for you.&lt;/p&gt;
&lt;h3 id=&quot;transformations&quot;&gt;Transformations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#transformations&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Image CDNs offer tens, and in some cases hundreds, of different image transformations. These transformations are specified via the URL string, and there are no restrictions on using multiple transformations at the same time. In the context of web performance, the most important image transformations are size, pixel density, format, and compression. These transformations are the reason why switching to an image CDN typically results in a significant reduction in image size.&lt;/p&gt;
&lt;p&gt;There tends to be an objectively best setting for performance transformations, so some image CDNs support an &amp;quot;auto&amp;quot; mode for these transformations. For example, instead of specifying that images be transformed to the WebP format, you could allow the CDN to automatically select and serve the optimal format. Signals that an image CDN can use to determine the best way to transform an image include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/blog/automating-resource-selection-with-client-hints/&quot; rel=&quot;noopener&quot;&gt;Client hints&lt;/a&gt; (for example, viewport width, DPR, and image width)&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Save-Data&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Save-Data&lt;/code&gt;&lt;/a&gt; header&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/User-Agent&quot; rel=&quot;noopener&quot;&gt;User-Agent&lt;/a&gt; request header&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Network_Information_API&quot; rel=&quot;noopener&quot;&gt;Network Information API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, the image CDN might serve AVIF to a Chrome browser, WebP to an Edge browser, and JPEG to a very old browser. Auto settings are popular because they allow you to take advantage of image CDNs&#39; significant expertise in optimizing images without the need for code changes to adopt new technologies once they&#39;re supported by the image CDN.&lt;/p&gt;
&lt;h2 id=&quot;types-of-image-cdns&quot;&gt;Types of Image CDNs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#types-of-image-cdns&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Image CDNs can be broken down into two categories: self-managed and third-party-managed.&lt;/p&gt;
&lt;h3 id=&quot;self-managed-image-cdns&quot;&gt;Self-managed image CDNs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#self-managed-image-cdns&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Self-managed CDNs can be a good choice for sites with engineering staff who are comfortable maintaining their own infrastructure.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/thumbor/thumbor&quot; rel=&quot;noopener&quot;&gt;Thumbor&lt;/a&gt; is the most popular self-managed image CDN. While it is open-source and free to use, it generally has fewer features than most commercial CDNs, and its documentation is somewhat limited. &lt;a href=&quot;https://wikitech.wikimedia.org/wiki/Thumbor&quot; rel=&quot;noopener&quot;&gt;Wikipedia&lt;/a&gt;, &lt;a href=&quot;https://medium.com/square-corner-blog/dynamic-images-with-thumbor-a430a1cfcd87&quot; rel=&quot;noopener&quot;&gt;Square&lt;/a&gt;, and &lt;a href=&quot;https://99designs.com/tech-blog/blog/2013/07/01/thumbnailing-with-thumbor/&quot; rel=&quot;noopener&quot;&gt;99designs&lt;/a&gt; are three sites that use Thumbor. See the &lt;a href=&quot;https://web.dev/install-thumbor&quot;&gt;How to install the Thumbor image CDN&lt;/a&gt; post for instructions on setting it up.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/h2non/imaginary&quot; rel=&quot;noopener&quot;&gt;Imaginary&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/cshum/imagor&quot; rel=&quot;noopener&quot;&gt;Imagor&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;third-party-image-cdns&quot;&gt;Third-party image CDNs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#third-party-image-cdns&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Third-party image CDNs provide image CDNs as a service. Just as cloud providers provide servers and other infrastructure for a fee; image CDNs provide image optimization and delivery for a fee. Because third-party image CDNs maintain the underlying technology, getting started is fairly simple and can usually be accomplished in 10-15 minutes, although a complete migration for a large site might take far longer. Third-party image CDNs are typically priced based on usage tiers, with most image CDNs providing either a free tier or a free trial to give you an opportunity to try out their product.&lt;/p&gt;
&lt;h2 id=&quot;choosing-an-image-cdn&quot;&gt;Choosing an image CDN &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#choosing-an-image-cdn&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are many good options for image CDNs. Some will have more features than others, but all of them will probably help you save bytes on your images and therefore load your pages faster. Besides feature sets, other factors to consider when choosing an image CDN are cost, support, documentation, and ease of setup or migration.&lt;/p&gt;
&lt;p&gt;Trying them out yourself before making a decision can also be helpful. Below you can find codelabs with instructions on how to quickly get started with several image CDNs.&lt;/p&gt;
&lt;h2 id=&quot;effects-on-largest-contentful-paint-lcp&quot;&gt;Effects on Largest Contentful Paint (LCP) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#effects-on-largest-contentful-paint-lcp&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Images are a vital part of the user experience on many websites, and thus factor into how well sites do when it comes to &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint&lt;/a&gt;. Here are a few things to keep in mind if you decide to use an image CDN:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Images served from CDNs may come from a cross-origin server, which involves extra connection setup time. When possible, try to use an image CDN that proxies through the primary origin so that you&#39;re not adding extra origins for the browser to connect to. This has the same effect as self-hosting images on the primary origin.&lt;/li&gt;
&lt;li&gt;Consider using a &lt;a href=&quot;https://web.dev/fetch-priority/#summary&quot;&gt;&lt;code&gt;fetchpriority&lt;/code&gt; attribute value of &lt;code&gt;&amp;quot;high&amp;quot;&lt;/code&gt;&lt;/a&gt; on the LCP image element so that the browser can begin loading that image as soon as possible.&lt;/li&gt;
&lt;li&gt;If an image is not immediately discoverable in the initial HTML, consider using a &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/uses-rel-preload/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;rel=preload&lt;/code&gt;&lt;/a&gt; hint for your LCP candidate image so that the browser can load that image ahead of time.&lt;/li&gt;
&lt;li&gt;If proxying through your origin is not possible, and the exact image to be loaded will not be known until later during page load, &lt;a href=&quot;https://web.dev/preconnect-and-dns-prefetch/&quot;&gt;you should set up a connection to the cross-origin image CDN as early as possible&lt;/a&gt; to shorten the resource loading phase of what could be your page&#39;s LCP candidate image.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Image CDNs are indispensable tools that eliminate the toil of manually optimizing images, and should be considered. As always, though, there are trade-offs to consider, and keeping an eye on your website&#39;s LCP candidate images and adding hints as appropriate can mitigate any added latency to those key requests.&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author><author>
      <name>Jeremy Wagner</name>
    </author><author>
      <name>Barry Pollard</name>
    </author>
  </entry>
  
  <entry>
    <title>Largest Contentful Paint (LCP)</title>
    <link href="https://web.dev/lcp/"/>
    <updated>2019-08-08T00:00:00Z</updated>
    <id>https://web.dev/lcp/</id>
    <content type="html" mode="escaped">&lt;div class=&quot;wdi-browser-compat&quot;&gt;
  &lt;span class=&quot;wdi-browser-compat__label&quot;&gt;Browser support&lt;/span&gt;
  &lt;ul class=&quot;wdi-browser-compat__items&quot;&gt;
    &lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;chrome&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Chrome 77, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      77
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox, Not supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Edge 79, Supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
79
&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Safari, Not supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;p&gt;&lt;/p&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/LargestContentfulPaint#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Largest Contentful Paint (LCP) is an important, &lt;a href=&quot;https://web.dev/vitals/#stable&quot;&gt;stable&lt;/a&gt; Core Web Vital metric for measuring &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/#types-of-metrics&quot;&gt;perceived load speed&lt;/a&gt; because it marks the point in the page load timeline when the page&#39;s main content has likely loaded—a fast LCP helps reassure the user that the page is &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/#questions&quot;&gt;useful&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Historically, it&#39;s been a challenge for web developers to measure how quickly
the main content of a web page loads and is visible to users.&lt;/p&gt;
&lt;p&gt;Older metrics like
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/Events/load&quot; rel=&quot;noopener&quot;&gt;load&lt;/a&gt; or
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/Events/DOMContentLoaded&quot; rel=&quot;noopener&quot;&gt;DOMContentLoaded&lt;/a&gt;
are not good because they don&#39;t necessarily correspond to what the user sees on
their screen. And newer, user-centric performance metrics like &lt;a href=&quot;https://web.dev/fcp/&quot;&gt;First Contentful
Paint (FCP)&lt;/a&gt; only capture the very beginning of the loading experience.
If a page shows a splash screen or displays a loading indicator, this moment is
not very relevant to the user.&lt;/p&gt;
&lt;p&gt;In the past we&#39;ve recommended performance metrics like &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/first-meaningful-paint/&quot; rel=&quot;noopener&quot;&gt;First Meaningful Paint
(FMP)&lt;/a&gt; and &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/speed-index/&quot; rel=&quot;noopener&quot;&gt;Speed Index (SI)&lt;/a&gt; (both
available in Lighthouse) to help capture more of the loading experience after
the initial paint, but these metrics are complex, hard to explain, and often
wrong—meaning they still do not identify when the main content of the page
has loaded.&lt;/p&gt;
&lt;p&gt;Sometimes simpler is better. Based on discussions in the &lt;a href=&quot;https://www.w3.org/webperf/&quot; rel=&quot;noopener&quot;&gt;W3C Web
Performance Working Group&lt;/a&gt; and research done at
Google, we&#39;ve found that a more accurate way to measure when the main content
of a page is loaded is to look at when the largest element was rendered.&lt;/p&gt;
&lt;h2 id=&quot;what-is-lcp&quot;&gt;What is LCP? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lcp/#what-is-lcp&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Largest Contentful Paint (LCP) metric reports the render time of the largest
&lt;a href=&quot;https://web.dev/lcp/#what-elements-are-considered&quot;&gt;image or text block&lt;/a&gt; visible within the
viewport, relative to when the page &lt;a href=&quot;https://w3c.github.io/hr-time/#timeorigin-attribute&quot; rel=&quot;noopener&quot;&gt;first started
loading&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;what-is-a-good-lcp-score&quot;&gt;What is a good LCP score? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lcp/#what-is-a-good-lcp-score&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To provide a good user experience, sites should strive to have Largest
Contentful Paint of &lt;strong&gt;2.5 seconds&lt;/strong&gt; or less. To ensure you&#39;re hitting this
target for most of your users, a good threshold to measure is the &lt;strong&gt;75th
percentile&lt;/strong&gt; of page loads, segmented across mobile and desktop devices.&lt;/p&gt;
&lt;figure&gt;
  &lt;picture&gt;
    &lt;source srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/elqsdYqQEefWJbUM2qMO.svg&quot; media=&quot;(min-width: 640px)&quot; width=&quot;800&quot; height=&quot;200&quot; /&gt;
    &lt;img alt=&quot;Good LCP values are 2.5 seconds or less, poor values are greater than 4.0 seconds, and anything in between needs improvement&quot; decoding=&quot;async&quot; height=&quot;480&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/eqprBhZUGfb8WYnumQ9ljAxRrA72/8ZW8LQsagLih1ZZoOmMR.svg&quot; width=&quot;640&quot; /&gt;
  &lt;/picture&gt;
&lt;/figure&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; To learn more about the research and methodology behind this recommendation, see: &lt;a href=&quot;https://web.dev/defining-core-web-vitals-thresholds/&quot;&gt;Defining the Core Web Vitals metrics thresholds&lt;/a&gt; &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;what-elements-are-considered&quot;&gt;What elements are considered? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lcp/#what-elements-are-considered&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As currently specified in the &lt;a href=&quot;https://wicg.github.io/largest-contentful-paint/&quot; rel=&quot;noopener&quot;&gt;Largest Contentful Paint
API&lt;/a&gt;, the types of elements
considered for Largest Contentful Paint are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;image&amp;gt;&lt;/code&gt; elements inside an &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; element&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; elements with a poster image (the poster image load time is used)&lt;/li&gt;
&lt;li&gt;An element with a background image loaded via the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/url()&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;url()&lt;/code&gt;&lt;/a&gt; function
(as opposed to a
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/CSS_Images/Using_CSS_gradients&quot; rel=&quot;noopener&quot;&gt;CSS gradient&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Block-level_elements&quot; rel=&quot;noopener&quot;&gt;Block-level&lt;/a&gt;
elements containing text nodes or other inline-level text elements children.&lt;/li&gt;
&lt;li&gt;The first frame painted for autoplaying &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; elements (as of &lt;a href=&quot;https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/speed/metrics_changelog/lcp.md&quot; rel=&quot;noopener&quot;&gt;August 2023&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;The first frame of an animated image format, such as animated GIFs (as of &lt;a href=&quot;https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/speed/metrics_changelog/lcp.md&quot; rel=&quot;noopener&quot;&gt;August 2023&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As well as only considering some elements, certain heurisitics are applied to exclude certain elements that are likely to be seen as &amp;quot;non-contentful&amp;quot; to users. For Chromium-based browsers, these include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Elements with an opacity of 0, that are invisible to the user&lt;/li&gt;
&lt;li&gt;Elements that cover the full viewport, that are likely considered as background rather than content&lt;/li&gt;
&lt;li&gt;Placeholder images or other images with a low entropy, that likely do not reflect the true content of the page&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Browsers are likely to continue to improve these heuristics to ensure we match user expectations of what the largest &lt;em&gt;contentful&lt;/em&gt; element is.&lt;/p&gt;
&lt;p&gt;These &amp;quot;contentful&amp;quot; heuristics may differ from those used by &lt;a href=&quot;https://web.dev/fcp/&quot;&gt;First Contentful Paint (FCP)&lt;/a&gt;, which may consider some of these elements, such as placeholder images or full viewport images, even if they are ineligible to be LCP candidates. Despite both using &amp;quot;contentful&amp;quot; in their name, the aim of these metrics is different. FCP measures when &lt;em&gt;any content&lt;/em&gt; is painted to screen and LCP when the &lt;em&gt;main content&lt;/em&gt; is painted so LCP is intented to be more selective.&lt;/p&gt;
&lt;h3 id=&quot;how-is-an-elements-size-determined&quot;&gt;How is an element&#39;s size determined? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lcp/#how-is-an-elements-size-determined&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The size of the element reported for Largest Contentful Paint is typically the
size that&#39;s visible to the user within the viewport. If the element extends
outside of the viewport, or if any of the element is clipped or has non-visible
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/overflow&quot; rel=&quot;noopener&quot;&gt;overflow&lt;/a&gt;, those
portions do not count toward the element&#39;s size.&lt;/p&gt;
&lt;p&gt;For image elements that have been resized from their &lt;a href=&quot;https://developer.mozilla.org/docs/Glossary/Intrinsic_Size&quot; rel=&quot;noopener&quot;&gt;intrinsic
size&lt;/a&gt;, the
size that gets reported is either the visible size or the intrinsic size,
whichever is smaller. For example, images that are shrunk down to a much
smaller than their intrinsic size will only report the size they&#39;re displayed
at, whereas images that are stretched or expanded to a larger size will only
report their intrinsic sizes.&lt;/p&gt;
&lt;p&gt;For text elements, only the size of their text nodes is considered (the smallest
rectangle that encompasses all text nodes).&lt;/p&gt;
&lt;p&gt;For all elements, any margin, padding, or border applied via CSS is not
considered.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Determining which text nodes belong to which elements can sometimes be tricky, especially for elements whose children includes inline elements and plain text nodes but also block-level elements. The key point is that every text node belongs to (and only to) its closest block-level ancestor element. In &lt;a href=&quot;https://wicg.github.io/element-timing/#set-of-owned-text-nodes&quot;&gt;spec terms&lt;/a&gt;: each text node belongs to the element that generates its &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/Containing_block&quot;&gt;containing block&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;when-is-largest-contentful-paint-reported&quot;&gt;When is largest contentful paint reported? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lcp/#when-is-largest-contentful-paint-reported&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Web pages often load in stages, and as a result, it&#39;s possible that the largest
element on the page might change.&lt;/p&gt;
&lt;p&gt;To handle this potential for change, the browser dispatches a
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/PerformanceEntry&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;PerformanceEntry&lt;/code&gt;&lt;/a&gt;
of type &lt;code&gt;largest-contentful-paint&lt;/code&gt; identifying the largest contentful element
as soon as the browser has painted the first frame. But then, after rendering
subsequent frames, it will dispatch another
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/PerformanceEntry&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;PerformanceEntry&lt;/code&gt;&lt;/a&gt;
any time the largest contentful element changes.&lt;/p&gt;
&lt;p&gt;For example, on a page with text and a hero image the browser may initially just
render the text—at which point the browser would dispatch a
&lt;code&gt;largest-contentful-paint&lt;/code&gt; entry whose &lt;code&gt;element&lt;/code&gt; property would likely reference
a &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;. Later, once the hero image finishes loading, a second
&lt;code&gt;largest-contentful-paint&lt;/code&gt; entry would be dispatched and its &lt;code&gt;element&lt;/code&gt; property
would reference the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It&#39;s important to note that an element can only be considered the largest
contentful element once it has rendered and is visible to the user. Images that
have not yet loaded are not considered &amp;quot;rendered&amp;quot;. Neither are text nodes using
web fonts during the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@font-face/font-display#The_font_display_timeline&quot; rel=&quot;noopener&quot;&gt;font block
period&lt;/a&gt;.
In such cases a smaller element may be reported as the largest contentful
element, but as soon as the larger element finishes rendering, it&#39;ll be
reported via another  &lt;code&gt;PerformanceEntry&lt;/code&gt; object.&lt;/p&gt;
&lt;p&gt;In addition to late-loading images and fonts, a page may add new elements to
the DOM as new content becomes available. If any of these new elements is
larger than the previous largest contentful element, a new &lt;code&gt;PerformanceEntry&lt;/code&gt;
will also be reported.&lt;/p&gt;
&lt;p&gt;If an element that is currently the largest contentful element is removed from
the viewport (or even removed from the DOM), it will remain the largest
contentful element unless a larger element is rendered.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Prior to Chrome 88, removed elements were not considered as largest contentful elements, and removing the current candidate would trigger a new &lt;code&gt;largest-contentful-paint&lt;/code&gt; entry to be dispatched. However, due to popular UI patterns such as image carousels that often removed DOM elements, the metric was updated to more accurately reflect what users experience. See the &lt;a href=&quot;https://chromium.googlesource.com/chromium/src/+/main/docs/speed/metrics_changelog/2020_11_lcp.md&quot;&gt;CHANGELOG&lt;/a&gt; for more details. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The browser will stop reporting new entries as soon as the user interacts with
the page (via a tap, scroll, or keypress), as user interaction often changes
what&#39;s visible to the user (which is especially true with scrolling).&lt;/p&gt;
&lt;p&gt;For analysis purposes, you should only report the most recently dispatched
&lt;code&gt;PerformanceEntry&lt;/code&gt; to your analytics service.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Since users can open pages in a background tab, it&#39;s possible that &lt;code&gt;largest-contentful-paint&lt;/code&gt; entries will not be dispatched until the user focuses the tab, which can be much later than when they first loaded it.  Google tools that measure LCP do not report this metric if the page was loaded in the background, as it does not reflect the user-perceived load time. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;load-time-vs-render-time&quot;&gt;Load time vs. render time &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lcp/#load-time-vs-render-time&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;For security reasons, the render timestamp of images is not exposed for
cross-origin images that lack the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Timing-Allow-Origin&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Timing-Allow-Origin&lt;/code&gt;&lt;/a&gt;
header. Instead, only their load time is exposed (since this is already exposed
via many other web APIs).&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://web.dev/lcp/#measure-lcp-in-javascript&quot;&gt;usage example&lt;/a&gt;
below shows how to handle elements whose render time is not available. But,
when possible, it&#39;s always recommended to set the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Timing-Allow-Origin&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Timing-Allow-Origin&lt;/code&gt;&lt;/a&gt;
header, so your metrics will be more accurate.&lt;/p&gt;
&lt;h3 id=&quot;how-are-element-layout-and-size-changes-handled&quot;&gt;How are element layout and size changes handled? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lcp/#how-are-element-layout-and-size-changes-handled&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To keep the performance overhead of calculating and dispatching new performance
entries low, changes to an element&#39;s size or position do not generate new LCP
candidates. Only the element&#39;s initial size and position in the viewport is
considered.&lt;/p&gt;
&lt;p&gt;This means images that are initially rendered off-screen and then transition
on-screen may not be reported. It also means elements initially rendered in the
viewport that then get pushed down, out of view will still report their
initial, in-viewport size.&lt;/p&gt;
&lt;h3 id=&quot;examples&quot;&gt;Examples &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lcp/#examples&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Here are some examples of when the Largest Contentful Paint occurs on a few
popular websites:&lt;/p&gt;
&lt;img alt=&quot;Largest Contentful Paint timeline from cnn.com&quot; decoding=&quot;async&quot; height=&quot;311&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/bsBm8poY1uQbq7mNvVJm.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/bsBm8poY1uQbq7mNvVJm.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/bsBm8poY1uQbq7mNvVJm.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/bsBm8poY1uQbq7mNvVJm.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/bsBm8poY1uQbq7mNvVJm.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/bsBm8poY1uQbq7mNvVJm.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/bsBm8poY1uQbq7mNvVJm.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/bsBm8poY1uQbq7mNvVJm.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/bsBm8poY1uQbq7mNvVJm.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/bsBm8poY1uQbq7mNvVJm.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/bsBm8poY1uQbq7mNvVJm.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/bsBm8poY1uQbq7mNvVJm.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/bsBm8poY1uQbq7mNvVJm.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/bsBm8poY1uQbq7mNvVJm.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/bsBm8poY1uQbq7mNvVJm.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/bsBm8poY1uQbq7mNvVJm.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/bsBm8poY1uQbq7mNvVJm.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/bsBm8poY1uQbq7mNvVJm.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;img alt=&quot;Largest Contentful Paint timeline from techcrunch.com&quot; decoding=&quot;async&quot; height=&quot;311&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/xAvLL1u2KFRaqoZZiI71.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/xAvLL1u2KFRaqoZZiI71.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/xAvLL1u2KFRaqoZZiI71.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/xAvLL1u2KFRaqoZZiI71.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/xAvLL1u2KFRaqoZZiI71.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/xAvLL1u2KFRaqoZZiI71.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/xAvLL1u2KFRaqoZZiI71.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/xAvLL1u2KFRaqoZZiI71.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/xAvLL1u2KFRaqoZZiI71.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/xAvLL1u2KFRaqoZZiI71.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/xAvLL1u2KFRaqoZZiI71.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/xAvLL1u2KFRaqoZZiI71.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/xAvLL1u2KFRaqoZZiI71.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/xAvLL1u2KFRaqoZZiI71.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/xAvLL1u2KFRaqoZZiI71.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/xAvLL1u2KFRaqoZZiI71.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/xAvLL1u2KFRaqoZZiI71.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/xAvLL1u2KFRaqoZZiI71.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;In both of the timelines above, the largest element changes as content loads.
In the first example, new content is added to the DOM and that changes what
element is the largest. In the second example, the layout changes and content
that was previously the largest is removed from the viewport.&lt;/p&gt;
&lt;p&gt;While it&#39;s often the case that late-loading content is larger than content
already on the page, that&#39;s not necessarily the case. The next two examples
show the Largest Contentful Paint occurring before the page fully loads.&lt;/p&gt;
&lt;img alt=&quot;Largest Contentful Paint timeline from instagram.com&quot; decoding=&quot;async&quot; height=&quot;311&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/uJAGswhXK3bE6Vs4I5bP.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/uJAGswhXK3bE6Vs4I5bP.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/uJAGswhXK3bE6Vs4I5bP.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/uJAGswhXK3bE6Vs4I5bP.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/uJAGswhXK3bE6Vs4I5bP.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/uJAGswhXK3bE6Vs4I5bP.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/uJAGswhXK3bE6Vs4I5bP.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/uJAGswhXK3bE6Vs4I5bP.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/uJAGswhXK3bE6Vs4I5bP.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/uJAGswhXK3bE6Vs4I5bP.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/uJAGswhXK3bE6Vs4I5bP.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/uJAGswhXK3bE6Vs4I5bP.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/uJAGswhXK3bE6Vs4I5bP.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/uJAGswhXK3bE6Vs4I5bP.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/uJAGswhXK3bE6Vs4I5bP.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/uJAGswhXK3bE6Vs4I5bP.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/uJAGswhXK3bE6Vs4I5bP.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/uJAGswhXK3bE6Vs4I5bP.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;img alt=&quot;Largest Contentful Paint timeline from google.com&quot; decoding=&quot;async&quot; height=&quot;311&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/e0O2woQjZJ92aYlPOJzT.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/e0O2woQjZJ92aYlPOJzT.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/e0O2woQjZJ92aYlPOJzT.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/e0O2woQjZJ92aYlPOJzT.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/e0O2woQjZJ92aYlPOJzT.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/e0O2woQjZJ92aYlPOJzT.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/e0O2woQjZJ92aYlPOJzT.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/e0O2woQjZJ92aYlPOJzT.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/e0O2woQjZJ92aYlPOJzT.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/e0O2woQjZJ92aYlPOJzT.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/e0O2woQjZJ92aYlPOJzT.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/e0O2woQjZJ92aYlPOJzT.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/e0O2woQjZJ92aYlPOJzT.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/e0O2woQjZJ92aYlPOJzT.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/e0O2woQjZJ92aYlPOJzT.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/e0O2woQjZJ92aYlPOJzT.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/e0O2woQjZJ92aYlPOJzT.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/e0O2woQjZJ92aYlPOJzT.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;In the first example, the Instagram logo is loaded relatively early and it
remains the largest element even as other content is progressively shown. In
the Google search results page example, the largest element is a paragraph of
text that is displayed before any of the images or logo finish loading. Since
all the individual images are smaller than this paragraph, it remains the
largest element throughout the load process.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; In the first frame of the Instagram timeline, you may notice the camera logo does not have a green box around it. That&#39;s because it&#39;s an &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; element, and &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; elements are not currently considered LCP candidates. The first LCP candidate is the text in the second frame. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;how-to-measure-lcp&quot;&gt;How to measure LCP &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lcp/#how-to-measure-lcp&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;LCP can be measured &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/#in-the-lab&quot;&gt;in the lab&lt;/a&gt;
or &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/#in-the-field&quot;&gt;in the field&lt;/a&gt;, and it&#39;s
available in the following tools:&lt;/p&gt;
&lt;h3 id=&quot;field-tools&quot;&gt;Field tools &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lcp/#field-tools&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/crux/&quot; rel=&quot;noopener&quot;&gt;Chrome User Experience
Report&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pagespeed.web.dev/&quot; rel=&quot;noopener&quot;&gt;PageSpeed Insights&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://support.google.com/webmasters/answer/9205520&quot; rel=&quot;noopener&quot;&gt;Search Console (Core Web Vitals
report)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/GoogleChrome/web-vitals&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;web-vitals&lt;/code&gt; JavaScript library&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;lab-tools&quot;&gt;Lab tools &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lcp/#lab-tools&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/devtools/&quot; rel=&quot;noopener&quot;&gt;Chrome DevTools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pagespeed.web.dev/&quot; rel=&quot;noopener&quot;&gt;PageSpeed Insights&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webpagetest.org/&quot; rel=&quot;noopener&quot;&gt;WebPageTest&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;measure-lcp-in-javascript&quot;&gt;Measure LCP in JavaScript &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lcp/#measure-lcp-in-javascript&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To measure LCP in JavaScript, you can use the &lt;a href=&quot;https://wicg.github.io/largest-contentful-paint/&quot; rel=&quot;noopener&quot;&gt;Largest Contentful Paint
API&lt;/a&gt;. The following example
shows how to create a
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/PerformanceObserver&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;PerformanceObserver&lt;/code&gt;&lt;/a&gt;
that listens for &lt;code&gt;largest-contentful-paint&lt;/code&gt; entries and logs them to the
console.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PerformanceObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entryList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; entry &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; entryList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEntries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;LCP candidate:&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;startTime&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;largest-contentful-paint&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;buffered&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-warn-bg color-state-warn-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block color-state-warn-text&quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Warning sign&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M23 21L12 2 1 21h22zm-12-3v-2h2v2h-2zm0-4h2v-4h-2v4z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Warning&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt;  This code shows how to log &lt;code&gt;largest-contentful-paint&lt;/code&gt; entries to the console, but measuring LCP in JavaScript is more complicated. See below for details:  &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;In the above example, each logged &lt;code&gt;largest-contentful-paint&lt;/code&gt; entry represents
the current LCP candidate. In general, the &lt;code&gt;startTime&lt;/code&gt; value of the last entry
emitted is the LCP value—however, that is not always the case. Not all
&lt;code&gt;largest-contentful-paint&lt;/code&gt; entries are valid for measuring LCP.&lt;/p&gt;
&lt;p&gt;The following section lists the differences between what the API reports and how
the metric is calculated.&lt;/p&gt;
&lt;h4 id=&quot;differences-between-the-metric-and-the-api&quot;&gt;Differences between the metric and the API &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lcp/#differences-between-the-metric-and-the-api&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;The API will dispatch &lt;code&gt;largest-contentful-paint&lt;/code&gt; entries for pages loaded in a
background tab, but those pages should be ignored when calculating LCP.&lt;/li&gt;
&lt;li&gt;The API will continue to dispatch &lt;code&gt;largest-contentful-paint&lt;/code&gt; entries after a
page has been backgrounded, but those entries should be ignored when
calculating LCP (elements may only be considered if the page was in the
foreground the entire time).&lt;/li&gt;
&lt;li&gt;The API does not report &lt;code&gt;largest-contentful-paint&lt;/code&gt; entries when the page is
restored from the &lt;a href=&quot;https://web.dev/bfcache/#impact-on-core-web-vitals&quot;&gt;back/forward cache&lt;/a&gt;,
but LCP should be measured in these cases since users experience them as
distinct page visits.&lt;/li&gt;
&lt;li&gt;The API does not consider elements within iframes but the metric does as they
are part of the user experience of the page. In pages with an LCP within an
iframe—for example a poster image on an embedded video—this will
&lt;a href=&quot;https://web.dev/crux-and-rum-differences/#iframes&quot;&gt;show as a difference between CrUX and RUM&lt;/a&gt;.
To properly measure LCP you should consider them. Sub-frames can use the API
to report their &lt;code&gt;largest-contentful-paint&lt;/code&gt; entries to the parent frame for
aggregation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Rather than memorizing all these subtle differences, developers can use the
&lt;a href=&quot;https://github.com/GoogleChrome/web-vitals&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;web-vitals&lt;/code&gt; JavaScript library&lt;/a&gt; to
measure LCP, which handles these differences for you (where possible):&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;onLCP&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;web-vitals&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Measure and log LCP as soon as it&#39;s available.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;onLCP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You can refer to &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals/blob/main/src/onLCP.ts&quot; rel=&quot;noopener&quot;&gt;the source code for
&lt;code&gt;onLCP()&lt;/code&gt;&lt;/a&gt;
for a complete example of how to measure LCP in JavaScript.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; In some cases (such as cross-origin iframes) it&#39;s not possible to measure LCP in JavaScript. See the &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals#limitations&quot;&gt;limitations&lt;/a&gt; section of the &lt;code&gt;web-vitals&lt;/code&gt; library for details. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;what-if-the-largest-element-isnt-the-most-important&quot;&gt;What if the largest element isn&#39;t the most important? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lcp/#what-if-the-largest-element-isnt-the-most-important&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In some cases the most important element (or elements) on the page is not the
same as the largest element, and developers may be more interested in measuring
the render times of these other elements instead. This is possible using the
&lt;a href=&quot;https://wicg.github.io/element-timing/&quot; rel=&quot;noopener&quot;&gt;Element Timing API&lt;/a&gt;, as described in
the article on &lt;a href=&quot;https://web.dev/custom-metrics/#element-timing-api&quot;&gt;custom metrics&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;how-to-improve-lcp&quot;&gt;How to improve LCP &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lcp/#how-to-improve-lcp&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A full guide on &lt;a href=&quot;https://web.dev/optimize-lcp/&quot;&gt;optimizing LCP&lt;/a&gt; is available to guide you through the process of identifying LCP timings in the field and using lab data to drill down and optimize them.&lt;/p&gt;
&lt;h2 id=&quot;additional-resources&quot;&gt;Additional resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lcp/#additional-resources&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/ctavZT87syI&quot; rel=&quot;noopener&quot;&gt;Lessons learned from performance monitoring in Chrome&lt;/a&gt; by &lt;a href=&quot;https://anniesullie.com/&quot; rel=&quot;noopener&quot;&gt;Annie Sullivan&lt;/a&gt; at &lt;a href=&quot;https://perfnow.nl/&quot; rel=&quot;noopener&quot;&gt;performance.now()&lt;/a&gt; (2019)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;changelog&quot;&gt;CHANGELOG &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lcp/#changelog&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Occasionally, bugs are discovered in the APIs used to measure metrics, and sometimes in the definitions of the metrics themselves. As a result, changes must sometimes be made, and these changes can show up as improvements or regressions in your internal reports and dashboards.&lt;/p&gt;
&lt;p&gt;To help you manage this, all changes to either the implementation or definition of these metrics will be surfaced in this &lt;a href=&quot;http://bit.ly/chrome-speed-metrics-changelog&quot; rel=&quot;noopener&quot;&gt;CHANGELOG&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you have feedback for these metrics, you can provide it in the &lt;a href=&quot;https://groups.google.com/g/web-vitals-feedback&quot; rel=&quot;noopener&quot;&gt;web-vitals-feedback Google group&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Philip Walton</name>
    </author><author>
      <name>Barry Pollard</name>
    </author>
  </entry>
  
  <entry>
    <title>Browser-level image lazy loading for the web</title>
    <link href="https://web.dev/browser-level-image-lazy-loading/"/>
    <updated>2019-08-06T00:00:00Z</updated>
    <id>https://web.dev/browser-level-image-lazy-loading/</id>
    <content type="html" mode="escaped">&lt;p&gt;Browser-level support for lazy loading images is now supported on the web! This video shows a &lt;a href=&quot;https://mathiasbynens.be/demo/img-loading-lazy&quot; rel=&quot;noopener&quot;&gt;demo&lt;/a&gt; of the feature:&lt;/p&gt;
&lt;figure&gt;
  &lt;video controls=&quot;&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot;&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/native-lazy-loading/lazyload.webm&quot; type=&quot;video/webm&quot; /&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/native-lazy-loading/lazyload.mp4&quot; type=&quot;video/mp4&quot; /&gt;
  &lt;/video&gt;
&lt;/figure&gt;
&lt;p&gt;You can use the &lt;code&gt;loading&lt;/code&gt; attribute to lazy-load images without the need to write custom lazy loading code or use a separate JavaScript library. Let&#39;s dive into the details.&lt;/p&gt;
&lt;h2 id=&quot;browser-compatibility&quot;&gt;Browser compatibility &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#browser-compatibility&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;wdi-browser-compat&quot;&gt;
  &lt;span class=&quot;wdi-browser-compat__label&quot;&gt;Browser support&lt;/span&gt;
  &lt;ul class=&quot;wdi-browser-compat__items&quot;&gt;
    &lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;chrome&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Chrome 77, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      77
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox 75, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      75
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Edge 79, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      79
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Safari 15.4, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      15.4
    &lt;/span&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/Performance/Lazy_loading#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Browsers that do not support the &lt;code&gt;loading&lt;/code&gt; attribute simply ignore it without side effects.&lt;/p&gt;
&lt;h2 id=&quot;why-browser-level-lazy-loading&quot;&gt;Why browser-level lazy loading? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#why-browser-level-lazy-loading&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;According to the &lt;a href=&quot;https://httparchive.org/reports/page-weight&quot; rel=&quot;noopener&quot;&gt;HTTP Archive&lt;/a&gt;, images are the most requested asset type for most websites and usually take up more bandwidth than any other resource. At the 90th percentile, sites send over 5 MB of images on desktop and mobile. That&#39;s a lot of &lt;a href=&quot;https://en.wikipedia.org/wiki/Cats_and_the_Internet&quot; rel=&quot;noopener&quot;&gt;cat pictures&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Previously, there were two ways to defer the loading of off-screen images:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using the &lt;a href=&quot;https://developer.chrome.com/blog/intersectionobserver/&quot; rel=&quot;noopener&quot;&gt;Intersection Observer API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;scroll&lt;/code&gt;, &lt;code&gt;resize&lt;/code&gt;, or &lt;code&gt;orientationchange&lt;/code&gt; &lt;a href=&quot;https://web.dev/lazy-loading-images/&quot;&gt;event handlers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Either option can let developers include lazy loading functionality, and many developers have built third-party libraries to provide abstractions that are even easier to use. With lazy loading supported directly by the browser, however, there&#39;s no need for an external library. Browser-level lazymloading also ensures that deferred loading of images still works even if JavaScript is disabled on the client.&lt;/p&gt;
&lt;h2 id=&quot;the-loading-attribute&quot;&gt;The &lt;code&gt;loading&lt;/code&gt; attribute &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#the-loading-attribute&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Chrome loads images at different priorities depending on where they&#39;re located with respect to the device viewport. Images below the viewport are loaded with a lower priority, but they&#39;re still fetched as the page loads.&lt;/p&gt;
&lt;p&gt;You can use the &lt;code&gt;loading&lt;/code&gt; attribute to completely defer the loading of offscreen images that are reached by scrolling:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image.png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;…&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Here are the supported values for the &lt;code&gt;loading&lt;/code&gt; attribute:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;lazy&lt;/code&gt;: Defer loading of the resource until it reaches a &lt;a href=&quot;https://web.dev/browser-level-image-lazy-loading/#distance-from-viewport-thresholds&quot;&gt;calculated distance&lt;/a&gt; from the viewport.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;eager&lt;/code&gt;: Default loading behavior of the browser, which is the same as not including the attribute and means the image is loaded regardless of where it&#39;s located on the page. While this is the default, it can be useful to explicitly set this if your tooling automatically adds &lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt; if there is no explicit value, or if your linter complains if it is not explicitly set.&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Images that are highly likely to be in-viewport, and in particular &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;LCP&lt;/a&gt; images, &lt;a href=&quot;https://web.dev/browser-level-image-lazy-loading/#avoid-lazy-loading-images-that-are-in-the-first-visible-viewport&quot;&gt;should not be lazy-loaded&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;relationship-between-the-loading-attribute-and-fetch-priority&quot;&gt;Relationship between the &lt;code&gt;loading&lt;/code&gt; attribute and fetch priority &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#relationship-between-the-loading-attribute-and-fetch-priority&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;eager&lt;/code&gt; value is simply an instruction to load the image as usual, without delaying the load further if it is off-screen. It does not imply that the image is loaded any quicker than another image without the &lt;code&gt;loading=&amp;quot;eager&amp;quot;&lt;/code&gt; attribute.&lt;/p&gt;
&lt;p&gt;Browsers prioritize resources based on various heuristics, and the &lt;code&gt;loading&lt;/code&gt; attribute just states &lt;em&gt;when&lt;/em&gt; the image resource is queued, not &lt;em&gt;how&lt;/em&gt; it is prioritized in that queue. &lt;code&gt;eager&lt;/code&gt; just implies the usual eager queueing browsers use by default.&lt;/p&gt;
&lt;p&gt;If you want to increase the fetch priority of an important image (for example the LCP image), then &lt;a href=&quot;https://web.dev/fetch-priority/&quot;&gt;Fetch Priority&lt;/a&gt; should be used with &lt;code&gt;fetchpriority=&amp;quot;high&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Note that an image with &lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt; and &lt;code&gt;fetchpriority=&amp;quot;high&amp;quot;&lt;/code&gt; will still be delayed while it is off-screen, and then fetched with a high priority when it is nearly within the viewport. It would likely be fetched with a high priority in this case anyway, so this combination should not really be needed nor used.&lt;/p&gt;
&lt;h3 id=&quot;distance-from-viewport-thresholds&quot;&gt;Distance-from-viewport thresholds &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#distance-from-viewport-thresholds&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;All images that are above the fold—that is, immediately viewable without scrolling—load normally. Those that are far below the device viewport are only fetched when the user scrolls near them.&lt;/p&gt;
&lt;p&gt;Chromium&#39;s implementation of lazy loading tries to ensure that offscreen images are loaded early enough so that they have finished loading once the user scrolls near to them. By fetching nearby images well before they become visible in the viewport, we maximize the chance they are already loaded by the time they become visible.&lt;/p&gt;
&lt;p&gt;Compared to JavaScript lazy loading libraries, the thresholds for fetching images that scroll into view may be considered conservative.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Experiments conducted using Chrome on Android suggest that on 4G, 97.5% of below-the-fold images that are lazy-loaded were fully loaded within 10ms of becoming visible. Even on slow 2G networks, 92.6% of below-the-fold images were fully loaded within 10ms. This means browser-level lazy loading offers a stable experience regarding the visibility of elements that are scrolled into view. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The distance threshold is not fixed and varies depending on several factors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The type of image resource being fetched&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://googlechrome.github.io/samples/network-information/&quot; rel=&quot;noopener&quot;&gt;effective connection type&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can find the default values for the different effective connection types in the &lt;a href=&quot;https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/frame/settings.json5;l=963-995&quot; rel=&quot;noopener&quot;&gt;Chromium source&lt;/a&gt;. These numbers, and even the approach of fetching only when a certain distance from the viewport is reached, may change in the future as the Chrome team improves heuristics to determine when to begin loading.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; You can experiment with these different thresholds by &lt;a href=&quot;https://developer.chrome.com/docs/devtools/network/#throttle&quot;&gt;throttling the network&lt;/a&gt; in DevTools. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;improved-data-savings-and-distance-from-viewport-thresholds&quot;&gt;Improved data-savings and distance-from-viewport thresholds &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#improved-data-savings-and-distance-from-viewport-thresholds&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As of July 2020, Chrome has made significant improvements to align the image lazy loading distance-from-viewport thresholds to better meet developer expectations.&lt;/p&gt;
&lt;p&gt;On fast connections (4G), we reduced Chrome&#39;s distance-from-viewport thresholds from &lt;code&gt;3000px&lt;/code&gt; to &lt;code&gt;1250px&lt;/code&gt; and on slower connections (3G or lower), changed the threshold from &lt;code&gt;4000px&lt;/code&gt; to &lt;code&gt;2500px&lt;/code&gt;. This change achieves two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;img loading=lazy&amp;gt;&lt;/code&gt; behaves closer to the experience offered by JavaScript lazy loading libraries.&lt;/li&gt;
&lt;li&gt;The new distance-from-viewport thresholds still allow us to guarantee images have probably loaded by the time a user has scrolled to them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can find a comparison between the old vs. new distance-from-viewport thresholds for one of our demos on a fast connection (4G) below:&lt;/p&gt;
&lt;p&gt;Old thresholds. vs new thresholds:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;The new and improved thresholds for image lazy loading, reducing the distance-from-viewport thresholds for fast connections from 3000px down to 1250px&quot; decoding=&quot;async&quot; height=&quot;460&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/xSZMqpbioBRwRTnenK8f.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/xSZMqpbioBRwRTnenK8f.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/xSZMqpbioBRwRTnenK8f.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/xSZMqpbioBRwRTnenK8f.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/xSZMqpbioBRwRTnenK8f.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/xSZMqpbioBRwRTnenK8f.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/xSZMqpbioBRwRTnenK8f.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/xSZMqpbioBRwRTnenK8f.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/xSZMqpbioBRwRTnenK8f.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/xSZMqpbioBRwRTnenK8f.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/xSZMqpbioBRwRTnenK8f.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/xSZMqpbioBRwRTnenK8f.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/xSZMqpbioBRwRTnenK8f.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/xSZMqpbioBRwRTnenK8f.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/xSZMqpbioBRwRTnenK8f.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/xSZMqpbioBRwRTnenK8f.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/xSZMqpbioBRwRTnenK8f.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/xSZMqpbioBRwRTnenK8f.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;and the new thresholds vs. LazySizes (a popular JS lazy loading library):&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;The new  distance-from-viewport thresholds in Chrome loading 90KB of images compared to LazySizes loading in 70KB under the same network conditions&quot; decoding=&quot;async&quot; height=&quot;355&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/oHMFvflk9aesT7r0iJbx.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/oHMFvflk9aesT7r0iJbx.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/oHMFvflk9aesT7r0iJbx.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/oHMFvflk9aesT7r0iJbx.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/oHMFvflk9aesT7r0iJbx.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/oHMFvflk9aesT7r0iJbx.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/oHMFvflk9aesT7r0iJbx.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/oHMFvflk9aesT7r0iJbx.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/oHMFvflk9aesT7r0iJbx.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/oHMFvflk9aesT7r0iJbx.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/oHMFvflk9aesT7r0iJbx.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/oHMFvflk9aesT7r0iJbx.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/oHMFvflk9aesT7r0iJbx.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/oHMFvflk9aesT7r0iJbx.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/oHMFvflk9aesT7r0iJbx.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/oHMFvflk9aesT7r0iJbx.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/oHMFvflk9aesT7r0iJbx.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/oHMFvflk9aesT7r0iJbx.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;We are committed to working with the web standards community to explore better alignment in how distance-from-viewport thresholds are approached across different browsers.&lt;/p&gt;
&lt;h3 id=&quot;images-should-include-dimension-attributes&quot;&gt;Images should include dimension attributes &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#images-should-include-dimension-attributes&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;While the browser loads an image, it does not immediately know the image&#39;s dimensions, unless these are explicitly specified. To enable the browser to reserve sufficient space on a page for images, it is recommended that all &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags include both &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes. Without dimensions specified, &lt;a href=&quot;https://web.dev/cls/&quot;&gt;layout shifts&lt;/a&gt; can occur, which are more noticeable on pages that take some time to load.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image.png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;…&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Alternatively, specify their values directly in an inline style:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image.png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;…&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The best practice of setting dimensions applies to &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags regardless of whether or not they are being loaded lazily. With lazy loading, this can become more relevant. Setting &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; on images in modern browsers also allows browsers to infer their intrinsic size.&lt;/p&gt;
&lt;p&gt;In most scenarios images still lazy-load if dimensions are not included, but there are a few edge cases you should be aware of. Without &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; specified, image dimensions are 0×0 pixels at first. If you have a gallery of such images, the browser may conclude that all of them fit inside the viewport at the start, as each takes up practically no space and no image is pushed offscreen. In this case the browser determines that all of them are visible to the user and decides to load everything.&lt;/p&gt;
&lt;p&gt;Also, &lt;a href=&quot;https://www.youtube.com/watch?v=4-d_SoCHeWE&quot; rel=&quot;noopener&quot;&gt;specifying image dimensions decreases the chances of layout shifts happening&lt;/a&gt;. If you are unable to include dimensions for your images, lazy loading them can be a trade-off between saving network resources and potentially being more at risk of layout shift.&lt;/p&gt;
&lt;p&gt;While lazy loading in Chromium is implemented in a way such that images are likely to be loaded once they are visible, there is still a small chance that they might not be loaded yet. In this case, missing &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes on such images increase their impact on Cumulative Layout Shift.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Take a look at this &lt;a href=&quot;https://mathiasbynens.be/demo/img-loading-lazy&quot;&gt;demo&lt;/a&gt; to see how the &lt;code&gt;loading&lt;/code&gt; attribute works with 100 pictures. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Images that are defined using the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element can also be lazy-loaded:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(min-width: 800px)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;large.jpg 1x, larger.jpg 2x&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;photo.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Although a browser will decide which image to load from any of the &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; elements, the &lt;code&gt;loading&lt;/code&gt; attribute only needs to be included to the fallback &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element.&lt;/p&gt;
&lt;h2 id=&quot;avoid-lazy-loading-images-that-are-in-the-first-visible-viewport&quot;&gt;Avoid lazy loading images that are in the first visible viewport &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#avoid-lazy-loading-images-that-are-in-the-first-visible-viewport&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You should avoid setting &lt;code&gt;loading=lazy&lt;/code&gt; for any images that are in the first visible viewport. This is particularly relevant for LCP images. See the article &lt;a href=&quot;https://web.dev/lcp-lazy-loading/&quot;&gt;The performance effects of too much lazy-loading&lt;/a&gt; for more information.&lt;/p&gt;
&lt;p&gt;It is recommended to only add &lt;code&gt;loading=lazy&lt;/code&gt; to images which are positioned below the fold, if possible. Images that are eagerly loaded can be fetched right away, while images which are loaded lazily the browser currently needs to wait until it knows where the image is positioned on the page, which relies on the &lt;code&gt;IntersectionObserver&lt;/code&gt; to be available.&lt;/p&gt;
&lt;p&gt;Generally, any images within the viewport should be loaded eagerly using the browser&#39;s defaults. You do not need to specify &lt;code&gt;loading=eager&lt;/code&gt; for this to be the case for in-viewport images.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- visible in the viewport --&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;product-1.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;...&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;product-2.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;...&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;product-3.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;...&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- offscreen images --&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;product-4.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;...&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;product-5.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;...&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;product-6.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;...&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;200&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;graceful-degradation&quot;&gt;Graceful degradation &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#graceful-degradation&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Browsers that do not support the &lt;code&gt;loading&lt;/code&gt; attribute will ignore its presence. While these browsers will of course not get the benefits of lazy loading, including the attribute has no negative impact on them.&lt;/p&gt;
&lt;h2 id=&quot;faq&quot;&gt;FAQ &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#faq&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;are-there-plans-to-automatically-lazy-load-images-in-chrome&quot;&gt;Are there plans to automatically lazy-load images in Chrome? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#are-there-plans-to-automatically-lazy-load-images-in-chrome&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Previously, Chromium automatically lazy-loaded any images that were well suited to being deferred if &lt;a href=&quot;https://blog.chromium.org/2019/04/data-saver-is-now-lite-mode.html&quot; rel=&quot;noopener&quot;&gt;Lite mode&lt;/a&gt; was enabled on Chrome for Android and the &lt;code&gt;loading&lt;/code&gt; attribute was either not provided or set as &lt;code&gt;loading=&amp;quot;auto&amp;quot;&lt;/code&gt;. However, &lt;a href=&quot;https://support.google.com/chrome/thread/151853370/sunsetting-chrome-lite-mode-in-m100-and-older?hl=en&quot; rel=&quot;noopener&quot;&gt;Lite mode has been deprecated&lt;/a&gt; (as was the non-standard &lt;code&gt;loading=&amp;quot;auto&amp;quot;&lt;/code&gt;) and there are currently no plans to provide automatically lazy-load of images in Chrome.&lt;/p&gt;
&lt;h3 id=&quot;can-i-change-how-close-an-image-needs-to-be-before-a-load-is-triggered&quot;&gt;Can I change how close an image needs to be before a load is triggered? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#can-i-change-how-close-an-image-needs-to-be-before-a-load-is-triggered&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;These values are hardcoded and can&#39;t be changed through the API. However, they may change in the
future as browsers experiment with different threshold distances and variables.&lt;/p&gt;
&lt;h3 id=&quot;can-css-background-images-take-advantage-of-the-loading-attribute&quot;&gt;Can CSS background images take advantage of the &lt;code&gt;loading&lt;/code&gt; attribute? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#can-css-background-images-take-advantage-of-the-loading-attribute&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;No, it can currently only be used with &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags.&lt;/p&gt;
&lt;h3 id=&quot;is-there-a-downside-to-lazy-loading-images-that-are-within-the-device-viewport&quot;&gt;Is there a downside to lazy loading images that are within the device viewport? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#is-there-a-downside-to-lazy-loading-images-that-are-within-the-device-viewport&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It is safer to avoid putting &lt;code&gt;loading=lazy&lt;/code&gt; on above-the-fold images, as Chrome won&#39;t preload &lt;code&gt;loading=lazy&lt;/code&gt; images in the &lt;a href=&quot;https://web.dev/preload-scanner/&quot;&gt;preload scanner&lt;/a&gt; and will also delay fetching such images until all layout is complete. See &lt;a href=&quot;https://web.dev/browser-level-image-lazy-loading/#avoid-lazy-loading-images-that-are-in-the-first-visible-viewport&quot;&gt;Avoid lazy-loading images that are in the first visible viewport&lt;/a&gt; for more information.&lt;/p&gt;
&lt;h3 id=&quot;how-does-the-loading-attribute-work-with-images-that-are-in-the-viewport-but-not-immediately-visible-for-example-behind-a-carousel,-or-hidden-by-css-for-certain-screen-sizes&quot;&gt;How does the &lt;code&gt;loading&lt;/code&gt; attribute work with images that are in the viewport but not immediately visible (for example: behind a carousel, or hidden by CSS for certain screen sizes)? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#how-does-the-loading-attribute-work-with-images-that-are-in-the-viewport-but-not-immediately-visible-for-example-behind-a-carousel,-or-hidden-by-css-for-certain-screen-sizes&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Using &lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt; &lt;em&gt;may&lt;/em&gt; prevent them being loaded when they are not visible but within the &lt;a href=&quot;https://web.dev/browser-level-image-lazy-loading/#distance-from-viewport-thresholds&quot;&gt;calculated-distance&lt;/a&gt;. For example, Chrome, Safari and Firefox do not load images using &lt;code&gt;display: none;&lt;/code&gt; styling—either on the image element or on a parent element. However, other techniques to hide images—such as using &lt;code&gt;opacity:0&lt;/code&gt; styling—will still result in the images being loaded. Always test your implementation thoroughly to ensure it&#39;s acting as intended.&lt;/p&gt;
&lt;h3 id=&quot;what-if-im-already-using-a-third-party-library-or-a-script-to-lazy-load-images&quot;&gt;What if I&#39;m already using a third-party library or a script to lazy-load images? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#what-if-im-already-using-a-third-party-library-or-a-script-to-lazy-load-images&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;With full support of native lazy loading now available in modern browsers, you may wish to reconsider if you still need a third-party library or script to lazy-load images.&lt;/p&gt;
&lt;p&gt;One reason to continue to use a third-party library along with &lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt; is to provide a polyfill for browsers that do not support the attribute, or to have more control over when lazy loading is triggered.&lt;/p&gt;
&lt;h3 id=&quot;how-do-i-handle-browsers-that-dont-support-lazy-loading&quot;&gt;How do I handle browsers that don&#39;t support lazy loading? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#how-do-i-handle-browsers-that-dont-support-lazy-loading&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Create a polyfill or use a third-party library to lazy-load images on your site. The &lt;code&gt;loading&lt;/code&gt;
property can be used to detect if the feature is supported in the browser:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;loading&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTMLImageElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// supported in browser&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// fetch polyfill/third-party library&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;For example, &lt;a href=&quot;https://github.com/aFarkas/lazysizes&quot; rel=&quot;noopener&quot;&gt;lazysizes&lt;/a&gt; is a popular JavaScript lazy loading library. You can detect support for the &lt;code&gt;loading&lt;/code&gt; attribute to load lazysizes as a fallback library only when &lt;code&gt;loading&lt;/code&gt; isn&#39;t supported. This works as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Replace &lt;code&gt;&amp;lt;img src&amp;gt;&lt;/code&gt; with &lt;code&gt;&amp;lt;img data-src&amp;gt;&lt;/code&gt; to avoid an eager load in unsupported browsers. If the &lt;code&gt;loading&lt;/code&gt; attribute is supported, swap &lt;code&gt;data-src&lt;/code&gt; for &lt;code&gt;src&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;loading&lt;/code&gt; is not supported, load a fallback (lazysizes) and initiate it. As per lazysizes docs, you use the &lt;code&gt;lazyload&lt;/code&gt; class as a way to indicate to lazysizes which images to lazy-load.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Let&#39;s load this in-viewport image normally --&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;hero.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;…&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Let&#39;s lazy-load the rest of these images --&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;unicorn.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;…&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazyload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;cats.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;…&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazyload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;dogs.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;…&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazyload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;loading&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTMLImageElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; images &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;img[loading=&quot;lazy&quot;]&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    images&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      img&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; img&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataset&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Dynamically import the LazySizes library&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; script &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;script&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    script&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token string&quot;&gt;&#39;https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.1.2/lazysizes.min.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;script&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Here&#39;s a &lt;a href=&quot;https://lazy-loading.firebaseapp.com/lazy_loading_lib.html&quot; rel=&quot;noopener&quot;&gt;demo&lt;/a&gt; of this pattern. Try it out in an older browser to see the fallback in action.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The lazysizes library also provides a &lt;a href=&quot;https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/native-loading&quot;&gt;loading plugin&lt;/a&gt; that uses browser-level lazy loading when available but falls back to the library&#39;s custom functionality when needed. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;is-lazy-loading-for-iframes-also-supported-in-browsers&quot;&gt;Is lazy loading for iframes also supported in browsers? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#is-lazy-loading-for-iframes-also-supported-in-browsers&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;wdi-browser-compat&quot;&gt;
  &lt;span class=&quot;wdi-browser-compat__label&quot;&gt;Browser support&lt;/span&gt;
  &lt;ul class=&quot;wdi-browser-compat__items&quot;&gt;
    &lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;chrome&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Chrome 77, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      77
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox, Not supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Edge 79, Supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
79
&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Safari 16.4, Supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
16.4
&lt;/span&gt;
&lt;/li&gt;&lt;p&gt;&lt;/p&gt;
  &lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;iframe loading=lazy&amp;gt;&lt;/code&gt; has also been standardized and is already implemented in Chromium and Safari. This allows you to lazy-load iframes using the &lt;code&gt;loading&lt;/code&gt; attribute. See &lt;a href=&quot;https://web.dev/iframe-lazy-loading/&quot;&gt;this dedicated article about iframe lazy-loading&lt;/a&gt; for more information.&lt;/p&gt;
&lt;h3 id=&quot;how-does-browser-level-lazy-loading-affect-advertisements-on-a-web-page&quot;&gt;How does browser-level lazy loading affect advertisements on a web page? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#how-does-browser-level-lazy-loading-affect-advertisements-on-a-web-page&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;All ads displayed to the user in the form of an image or iframe lazy-load just like any other image or iframe.&lt;/p&gt;
&lt;h3 id=&quot;how-are-images-handled-when-a-web-page-is-printed&quot;&gt;How are images handled when a web page is printed? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#how-are-images-handled-when-a-web-page-is-printed&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;All images and iframes are immediately loaded if the page is printed. See &lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/detail?id=875403&quot; rel=&quot;noopener&quot;&gt;issue #875403&lt;/a&gt; for details.&lt;/p&gt;
&lt;h3 id=&quot;does-lighthouse-recognize-browser-level-lazy-loading&quot;&gt;Does Lighthouse recognize browser-level lazy loading? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#does-lighthouse-recognize-browser-level-lazy-loading&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/lighthouse-whats-new-6.0/&quot;&gt;Lighthouse 6.0&lt;/a&gt; and above factor in approaches for offscreen image lazy loading that may use different thresholds, allowing them to pass the &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/offscreen-images/&quot; rel=&quot;noopener&quot;&gt;Defer offscreen images&lt;/a&gt; audit.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/browser-level-image-lazy-loading/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Baking in support for lazy loading images can make it significantly easier for you to improve the performance of your web pages.&lt;/p&gt;
&lt;p&gt;Are you noticing any unusual behavior with this feature enabled in Chrome? &lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/entry?summary=%5BLazyLoad%5D:&amp;amp;comment=Application%20Version%20%28from%20%22Chrome%20Settings%20%3E%20About%20Chrome%22%29:%20%0DAndroid%20Build%20Number%20%28from%20%22Android%20Settings%20%3E%20About%20Phone/Tablet%22%29:%20%0DDevice:%20%0D%0DSteps%20to%20reproduce:%20%0D%0DObserved%20behavior:%20%0D%0DExpected%20behavior:%20%0D%0DFrequency:%20%0D%3Cnumber%20of%20times%20you%20were%20able%20to%20reproduce%3E%20%0D%0DAdditional%20comments:%20%0D&amp;amp;labels=Pri-2&amp;amp;components=Blink%3ELoader%3ELazyLoad%2C&quot; rel=&quot;noopener&quot;&gt;File a bug&lt;/a&gt;!&lt;/p&gt;
</content>
    <author>
      <name>Houssein Djirdeh</name>
    </author><author>
      <name>Addy Osmani</name>
    </author><author>
      <name>Mathias Bynens</name>
    </author><author>
      <name>Barry Pollard</name>
    </author>
  </entry>
</feed>
