<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://web.dev/</id>
  <title>Jeremy Wagner on web.dev</title>
  <updated>2026-04-15T23:21:06Z</updated>
  <author>
    <name>Jeremy Wagner</name>
  </author>
  <link href="https://web.dev/authors/jlwagner/feed.xml" rel="self"/>
  <link href="https://web.dev/"/>
  <icon>https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YROiiX412sAFeV58NStI.jpg?auto=format</icon>
  <logo>https://web.dev/images/shared/rss-banner.png</logo>
  <subtitle>Tech writin&amp;#39;, doc slingin&amp;#39; reprobate.</subtitle>
  
  
  <entry>
    <title>Client-side rendering of HTML and interactivity</title>
    <link href="https://web.dev/client-side-rendering-of-html-and-interactivity/"/>
    <updated>2023-05-09T00:00:00Z</updated>
    <id>https://web.dev/client-side-rendering-of-html-and-interactivity/</id>
    <content type="html" mode="escaped">&lt;p&gt;Parsing and rendering of HTML is something that browsers do very well by default for websites that use the browser&#39;s built-in navigation logic—sometimes called &amp;quot;traditional page loads&amp;quot; or &amp;quot;hard navigations&amp;quot;. Such websites are sometimes called multi-page applications (MPAs).&lt;/p&gt;
&lt;p&gt;However, developers may work around browser defaults to suit their application needs. This is certainly the case for websites using the &lt;a href=&quot;https://en.wikipedia.org/wiki/Single-page_application&quot; rel=&quot;noopener&quot;&gt;single page application (SPA) pattern&lt;/a&gt;, which dynamically creates large parts of the HTML/DOM on the client with JavaScript. Client-side rendering is the name for this design pattern, and it can have effects on your website&#39;s &lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt; if the work involved is excessive.&lt;/p&gt;
&lt;p&gt;This guide will help you weigh the difference between using HTML sent by the server to the browser versus creating it on the client with JavaScript, and how the latter can result in high interaction latency at crucial moments.&lt;/p&gt;
&lt;h2 id=&quot;how-the-browser-renders-html-provided-by-the-server&quot;&gt;How the browser renders HTML provided by the server &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/client-side-rendering-of-html-and-interactivity/#how-the-browser-renders-html-provided-by-the-server&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The navigation pattern used in traditional page loads involves receiving HTML from the server on every navigation. If you enter a URL in the address bar of your browser or click on a link in an MPA, the following series of events occurs:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The browser sends a navigation request for the URL provided.&lt;/li&gt;
&lt;li&gt;The server responds with HTML in chunks.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The last step of these is key. It&#39;s also one of the most fundamental performance optimizations in the server/browser exchange, and is known as &lt;em&gt;streaming&lt;/em&gt;. If the server can begin sending HTML as soon as possible, and the browser doesn&#39;t wait for the entire response to arrive, the browser can process HTML in chunks as it arrives.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of parsing of HTML sent by the server visualized in the performance panel of Chrome DevTools. As the HTML streams in, chunks of it are processed across multiple shorter tasks, and rendering is incremental.&quot; decoding=&quot;async&quot; height=&quot;379&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8ZgdJRdWzvk5L72XKv2Z.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8ZgdJRdWzvk5L72XKv2Z.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8ZgdJRdWzvk5L72XKv2Z.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8ZgdJRdWzvk5L72XKv2Z.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8ZgdJRdWzvk5L72XKv2Z.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8ZgdJRdWzvk5L72XKv2Z.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8ZgdJRdWzvk5L72XKv2Z.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8ZgdJRdWzvk5L72XKv2Z.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8ZgdJRdWzvk5L72XKv2Z.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8ZgdJRdWzvk5L72XKv2Z.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8ZgdJRdWzvk5L72XKv2Z.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8ZgdJRdWzvk5L72XKv2Z.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8ZgdJRdWzvk5L72XKv2Z.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8ZgdJRdWzvk5L72XKv2Z.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8ZgdJRdWzvk5L72XKv2Z.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8ZgdJRdWzvk5L72XKv2Z.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8ZgdJRdWzvk5L72XKv2Z.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8ZgdJRdWzvk5L72XKv2Z.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Parsing and rendering of HTML provided by the server as visualized in the performance panel of Chrome DevTools. The tasks involved in parsing HTML and rendering it are split into chunks.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Like most things that happen in the browser, parsing HTML occurs within tasks. When HTML is streamed from the server to the browser, the browser optimizes parsing of that HTML by doing so a bit at a time as bits of that stream arrive in chunks. The consequence is that the browser yields to the main thread periodically after processing each chunk, which avoids &lt;a href=&quot;https://web.dev/long-tasks-devtools/#what-are-long-tasks&quot;&gt;long tasks&lt;/a&gt;. This means that other work can occur while HTML is being parsed, including the incremental rendering work necessary to present a page to the user, as well as processing user interactions that may occur during the page&#39;s crucial startup period. This approach translates to a better &lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt; score for the page.&lt;/p&gt;
&lt;p&gt;The takeaway? When you stream HTML from the server, &lt;strong&gt;you get incremental parsing and rendering of HTML and automatic yielding to the main thread for free.&lt;/strong&gt; You don&#39;t get that with client-side rendering.&lt;/p&gt;
&lt;h2 id=&quot;how-the-browser-renders-html-provided-by-javascript&quot;&gt;How the browser renders HTML provided by JavaScript &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/client-side-rendering-of-html-and-interactivity/#how-the-browser-renders-html-provided-by-javascript&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While every navigation request to a page requires some amount of HTML to be provided by the server, some websites will use the SPA pattern. This approach often involves a minimal initial payload of HTML is provided by the server, but then the client will populate the main content area of a page with HTML assembled from data fetched from the server. Subsequent navigations—sometimes referred to as &amp;quot;soft navigations&amp;quot; in this case—are handled entirely by JavaScript to populate the page with new HTML.&lt;/p&gt;
&lt;p&gt;Client-side rendering may also occur in non-SPAs in more limited cases where HTML is dynamically added to the DOM through JavaScript.&lt;/p&gt;
&lt;p&gt;There are a few common ways of creating HTML or adding to the DOM through JavaScript:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Element/innerHTML&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;innerHTML&lt;/code&gt; property&lt;/a&gt; allows you to set the content on an existing element via a string, which the browser parses into DOM.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Document/createElement&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;document.createElement&lt;/code&gt; method&lt;/a&gt; allows you to create new elements to be added to the DOM without using any browser HTML parsing.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Document/write&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;document.write&lt;/code&gt; method&lt;/a&gt; allows you to write HTML to the document (and the browser parses it, just like in approach #1). Due to &lt;a href=&quot;https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document.write()&quot; rel=&quot;noopener&quot;&gt;a number of reasons&lt;/a&gt;, however, &lt;strong&gt;usage of &lt;code&gt;document.write&lt;/code&gt; is strongly discouraged.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of parsing of HTML rendered via JavaScript visualized in the performance panel of Chrome DevTools. The work occurs in a single, long task that blocks the main thread.&quot; decoding=&quot;async&quot; height=&quot;141&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Xam8fQoVZBmDPET1dVQ1.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Xam8fQoVZBmDPET1dVQ1.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Xam8fQoVZBmDPET1dVQ1.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Xam8fQoVZBmDPET1dVQ1.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Xam8fQoVZBmDPET1dVQ1.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Xam8fQoVZBmDPET1dVQ1.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Xam8fQoVZBmDPET1dVQ1.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Xam8fQoVZBmDPET1dVQ1.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Xam8fQoVZBmDPET1dVQ1.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Xam8fQoVZBmDPET1dVQ1.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Xam8fQoVZBmDPET1dVQ1.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Xam8fQoVZBmDPET1dVQ1.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Xam8fQoVZBmDPET1dVQ1.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Xam8fQoVZBmDPET1dVQ1.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Xam8fQoVZBmDPET1dVQ1.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Xam8fQoVZBmDPET1dVQ1.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Xam8fQoVZBmDPET1dVQ1.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Xam8fQoVZBmDPET1dVQ1.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Parsing and rendering of HTML through JavaScript on the client as visualized in the performance panel of Chrome DevTools. The tasks involved in parsing and rendering it are not chunked up, resulting in a long task that blocks the main thread.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The consequences of creating HTML/DOM through client-side JavaScript can be significant:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Unlike HTML streamed by the server in response to a navigation request, JavaScript tasks on the client are not automatically chunked up, which may result in long tasks that block the main thread. This means that your page&#39;s INP can be negatively affected if you&#39;re creating too much HTML/DOM at a time on the client.&lt;/li&gt;
&lt;li&gt;If HTML is created on the client during startup, resources referenced within it &lt;a href=&quot;https://web.dev/preload-scanner/#rendering-markup-with-client-side-javascript&quot;&gt;will &lt;em&gt;not&lt;/em&gt; be discovered by the browser preload scanner&lt;/a&gt;. This will certainly have a negative effect on a page&#39;s &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt;. While this is not a runtime performance issue (instead it&#39;s an issue of network delay in fetching important resources), you don&#39;t want your website&#39;s LCP to be affected by sidestepping this fundamental browser performance optimization.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-you-can-do-about-the-performance-impact-of-client-side-rendering&quot;&gt;What you can do about the performance impact of client-side rendering &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/client-side-rendering-of-html-and-interactivity/#what-you-can-do-about-the-performance-impact-of-client-side-rendering&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If your website depends heavily on client-side rendering and you&#39;ve observed &lt;a href=&quot;https://web.dev/find-slow-interactions-in-the-field/&quot;&gt;poor INP values in your field data&lt;/a&gt;, you might be wondering if client-side rendering has anything to do with the problem. For example, if your website is an SPA, your field data may reveal interactions responsible for considerable rendering work.&lt;/p&gt;
&lt;p&gt;Whatever the cause, here are some potential causes you can explore to help get things back on track.&lt;/p&gt;
&lt;h3 id=&quot;provide-as-much-html-from-the-server-as-possible&quot;&gt;Provide as much HTML from the server as possible &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/client-side-rendering-of-html-and-interactivity/#provide-as-much-html-from-the-server-as-possible&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As mentioned earlier, the browser handles HTML from the server in a very performant way by default. It will break up parsing and rendering of HTML in a way that avoids long tasks, and optimizes the amount of total main thread time. This leads to a lower &lt;a href=&quot;https://web.dev/tbt/&quot;&gt;Total Blocking Time (TBT)&lt;/a&gt;, and TBT is &lt;a href=&quot;https://almanac.httparchive.org/en/2022/performance#inp-and-tbt&quot; rel=&quot;noopener&quot;&gt;strongly correlated with INP&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You may be relying on a frontend framework to build your website. If so, you&#39;ll want to make sure you&#39;re rendering component HTML on the server. This will limit the amount of initial client-side rendering your website will require, and should result in a better experience.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For React, you&#39;ll want to make use of the &lt;a href=&quot;https://react.dev/reference/react-dom/server&quot; rel=&quot;noopener&quot;&gt;Server DOM API&lt;/a&gt; to render HTML on the server. But be aware: the traditional method of server-side rendering uses &lt;a href=&quot;https://react.dev/reference/react-dom/server#server-apis-for-non-streaming-environments&quot; rel=&quot;noopener&quot;&gt;a synchronous approach&lt;/a&gt;, which can lead to a longer &lt;a href=&quot;https://web.dev/ttfb/&quot;&gt;Time to First Byte (TTFB)&lt;/a&gt;, as well as subsequent metrics such as &lt;a href=&quot;https://web.dev/fcp/&quot;&gt;First Contentful Paint (FCP)&lt;/a&gt; and LCP. Where possible, make sure you&#39;re using the streaming APIs for &lt;a href=&quot;https://react.dev/reference/react-dom/server#server-apis-for-nodejs-streams&quot; rel=&quot;noopener&quot;&gt;Node.js&lt;/a&gt; or &lt;a href=&quot;https://react.dev/reference/react-dom/server#server-apis-for-web-streams&quot; rel=&quot;noopener&quot;&gt;other JavaScript runtimes&lt;/a&gt; so that the server can begin streaming HTML to the browser as soon as possible. Next.js—a React-based framework—provides many best practices by default. In addition to automatically rendering HTML on the server, it can also statically generate HTML for pages that don&#39;t change based on user context (such as authentication).&lt;/li&gt;
&lt;li&gt;Vue also performs client-side rendering by default. However, like React, Vue can also &lt;a href=&quot;https://vuejs.org/guide/scaling-up/ssr.html&quot; rel=&quot;noopener&quot;&gt;render your component HTML on the server&lt;/a&gt;. Either take advantage of these server-side APIs where possible, or consider a &lt;a href=&quot;https://vuejs.org/guide/scaling-up/ssr.html#higher-level-solutions&quot; rel=&quot;noopener&quot;&gt;higher-level abstraction&lt;/a&gt; for your Vue project to make the best practices easier to implement.&lt;/li&gt;
&lt;li&gt;Svelte &lt;a href=&quot;https://learn.svelte.dev/tutorial/ssr&quot; rel=&quot;noopener&quot;&gt;renders HTML on the server&lt;/a&gt; by default—although if your component code needs access to browser-exclusive namespaces (&lt;code&gt;window&lt;/code&gt;, for example), you may not be able to render that component&#39;s HTML on the server. Explore alternative approaches wherever possible so that you&#39;re not causing unnecessary client-side rendering. &lt;a href=&quot;https://kit.svelte.dev/&quot; rel=&quot;noopener&quot;&gt;SvelteKit&lt;/a&gt;—which is to Svelte as Next.js is to React—embeds many best practices into your Svelte projects as possible, so you can avoid potential pitfalls in projects that use Svelte alone.&lt;/li&gt;
&lt;/ul&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; If your application uses either of these frameworks and requires interactivity on the client, you&#39;ll still need to &lt;a href=&quot;https://web.dev/rendering-on-the-web/#combining-server-rendering-and-csr-via-rehydration&quot;&gt;hydrate your components on the client&lt;/a&gt; so that they&#39;ll be interactive. For a comprehensive evaluation of the different approaches to rendering HTML, and for some advice on how to handle component hydration on the client with performance in mind, read &lt;a href=&quot;https://web.dev/rendering-on-the-web/&quot;&gt;Rendering on the web&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;limit-the-amount-of-dom-nodes-created-on-the-client&quot;&gt;Limit the amount of DOM nodes created on the client &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/client-side-rendering-of-html-and-interactivity/#limit-the-amount-of-dom-nodes-created-on-the-client&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When DOMs are large, the amount of processing time required to render them tends to increase. Whether your website is a full-fledged SPA, or is injecting new nodes into an existing DOM as the result of an interaction for an MPA, consider keeping those DOMs as small as possible. This will help reduce the work required during client-side rendering to display that HTML, hopefully helping to keep your website&#39;s INP lower.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-good-bg color-state-good-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; height=&quot;24&quot; width=&quot;24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Check&quot;&gt;   &lt;path d=&quot;M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Objective&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Read to learn more:&lt;/strong&gt; &lt;a href=&quot;https://web.dev/dom-size-and-interactivity/&quot;&gt;DOM size and interactivity&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;consider-a-streaming-service-worker-architecture&quot;&gt;Consider a streaming service worker architecture &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/client-side-rendering-of-html-and-interactivity/#consider-a-streaming-service-worker-architecture&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This is an advanced technique—one that may not work easily with every use case—but it&#39;s one that can turn your MPA into a website that feels like it&#39;s loading instantly when users navigate from one page to the next. You can use a service worker to precache the static parts of your website in &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/CacheStorage&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;CacheStorage&lt;/code&gt;&lt;/a&gt; while using the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/ReadableStream&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;ReadableStream&lt;/code&gt; API&lt;/a&gt; to fetch the rest of a page&#39;s HTML from the server.&lt;/p&gt;
&lt;p&gt;When you use this technique successfully, you aren&#39;t creating HTML on the client, but the instant loading of content partials from the cache will give the impression that your site is loading quickly. Websites using this approach can feel almost like an SPA, but without the downfalls of client-side rendering. It also &lt;a href=&quot;https://philipwalton.com/articles/smaller-html-payloads-with-service-workers/&quot; rel=&quot;noopener&quot;&gt;reduces the amount of HTML you&#39;re requesting from the server&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In short, a streaming service worker architecture doesn&#39;t &lt;em&gt;replace&lt;/em&gt; the browser&#39;s built-in navigation logic—it &lt;em&gt;adds&lt;/em&gt; to it. For more information on how to achieve this with &lt;a href=&quot;https://developer.chrome.com/docs/workbox/&quot; rel=&quot;noopener&quot;&gt;Workbox&lt;/a&gt;, read &lt;a href=&quot;https://developer.chrome.com/docs/workbox/faster-multipage-applications-with-streams/&quot; rel=&quot;noopener&quot;&gt;Faster multipage applications with streams&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/client-side-rendering-of-html-and-interactivity/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;How your website receives and renders HTML has an impact on performance. When you rely on the server to send all (or the bulk of) the HTML required for your website to function, you&#39;re getting a lot for free: incremental parsing and rendering, and automatic yielding to the main thread to avoid long tasks.&lt;/p&gt;
&lt;p&gt;Client-side HTML rendering introduces a number of potential performance issues that can be avoidable in many cases. Due to each individual website&#39;s requirements, however, it&#39;s not entirely avoidable 100% of the time. To mitigate the potential long tasks that can result from excessive client-site rendering, make sure you&#39;re sending as much of your website&#39;s HTML from the server whenever possible, keep your DOM sizes as small as possible for HTML that must be rendered on the client, and consider alternative architectures to speed the delivery of HTML to the client while also taking advantage of the incremental parsing and rendering the browser provides for HTML loaded from the server.&lt;/p&gt;
&lt;p&gt;If you can get your website&#39;s client-side rendering to be as minimal as possible, you&#39;ll improve not just your website&#39;s INP, but other metrics such as LCP, TBT, and possibly even your TTFB in some cases.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hero image from &lt;a href=&quot;https://unsplash.com/&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;, by &lt;a href=&quot;https://unsplash.com/de/fotos/nZcMWOKAJrY&quot; rel=&quot;noopener&quot;&gt;Maik Jonietz&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Diagnose slow interactions in the lab</title>
    <link href="https://web.dev/diagnose-slow-interactions-in-the-lab/"/>
    <updated>2023-05-09T00:00:00Z</updated>
    <id>https://web.dev/diagnose-slow-interactions-in-the-lab/</id>
    <content type="html" mode="escaped">&lt;p&gt;One challenging aspect of optimizing &lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt; is figuring out what&#39;s causing poor INP. There&#39;s a large variety of potential causes: third-party scripts that schedule many tasks on the main thread, large &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Document_Object_Model&quot; rel=&quot;noopener&quot;&gt;DOM&lt;/a&gt; sizes, expensive event callbacks, and so on.&lt;/p&gt;
&lt;p&gt;Finding ways to fix poor INP can be difficult. To start, you have to know which interactions tend to be responsible for a page&#39;s INP. If you don&#39;t know this already, start by reading &lt;a href=&quot;https://web.dev/find-slow-interactions-in-the-field/&quot;&gt;Find slow interactions in the field&lt;/a&gt;. Once you have that field data and know what interactions to test, you can do so in lab tools to work out why those interactions are slow.&lt;/p&gt;
&lt;h2 id=&quot;what-if-you-dont-have-field-data&quot;&gt;What if you don&#39;t have field data? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/diagnose-slow-interactions-in-the-lab/#what-if-you-dont-have-field-data&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Ideally, you&#39;ll want field data, as it saves you a lot of time trying to figure out which interactions need to be optimized. You might be in a position where you don&#39;t have field data, though. If that&#39;s your situation, you can still find interactions to improve: you&#39;ll just have to take a different approach.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/tbt/&quot;&gt;Total Blocking Time (TBT)&lt;/a&gt; is a metric that assesses page responsiveness during load. &lt;a href=&quot;https://almanac.httparchive.org/en/2022/performance#inp-and-tbt&quot; rel=&quot;noopener&quot;&gt;It correlates very well with INP&lt;/a&gt;, and can give you an idea if there might be interactions you can profile while the page is loading.&lt;/p&gt;
&lt;p&gt;You can use either &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt; or &lt;a href=&quot;https://pagespeed.web.dev/&quot; rel=&quot;noopener&quot;&gt;PageSpeed Insights&lt;/a&gt; to measure your page&#39;s TBT. If your TBT is either poor or needs improvement, there&#39;s a good chance there are interactions that might be slow during page load.&lt;/p&gt;
&lt;p&gt;To find slow interactions after the page has loaded, you might need to rely on other types of data, such as common user flows that you may already have in your website&#39;s analytics. If you work on an ecommerce website, for example, a common user flow would be the actions users take when they&#39;re adding items to an online shopping cart or going through an online checkout.&lt;/p&gt;
&lt;p&gt;Whether or not you have field data, the next step involves reproducing that interaction—because it&#39;s only when you&#39;re able to conclusively identify a slow interaction that you&#39;ll be able to fix it.&lt;/p&gt;
&lt;h2 id=&quot;reproducing-slow-interactions-in-the-lab&quot;&gt;Reproducing slow interactions in the lab &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/diagnose-slow-interactions-in-the-lab/#reproducing-slow-interactions-in-the-lab&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once you&#39;ve identified a slow interaction, the next step is to test it in the lab to see if it&#39;s reliably slow.&lt;/p&gt;
&lt;h3 id=&quot;dont-reach-for-the-performance-profiler-right-away&quot;&gt;Don&#39;t reach for the performance profiler right away &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/diagnose-slow-interactions-in-the-lab/#dont-reach-for-the-performance-profiler-right-away&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Chrome performance profiler—while invaluable—doesn&#39;t provide a live view while interacting with the page. It&#39;s more efficient to test interaction latency in a much faster way first, so you can quickly assess whether a given interaction is reliably slow, and then reach for the performance profiler when you need more information.&lt;/p&gt;
&lt;h4 id=&quot;use-the-web-vitals-chrome-extension&quot;&gt;Use the Web Vitals Chrome Extension &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/diagnose-slow-interactions-in-the-lab/#use-the-web-vitals-chrome-extension&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;a href=&quot;https://chrome.google.com/webstore/detail/web-vitals/ahfhijdlegdabablpippeagghigmibma?hl=en&quot; rel=&quot;noopener&quot;&gt;Web Vitals Chrome Extension&lt;/a&gt; involves the lowest amount of effort in testing interaction latency. Once installed, the extension will display interaction data in the console if you do the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In Chrome, click the extensions icon to the right of the address bar.&lt;/li&gt;
&lt;li&gt;Locate the Web Vitals extension in the drop-down menu.&lt;/li&gt;
&lt;li&gt;Click the icon at the right to open the extension&#39;s settings.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Options&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Enable the &lt;strong&gt;Console logging&lt;/strong&gt; checkbox in the resulting screen, and then click &lt;strong&gt;Save&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once this has been done, open the console in Chrome DevTools, and begin testing the desired interactions on your website. As you interact with the page, you&#39;ll receive useful console logs giving you detailed diagnostic information for the interaction.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of the console logging that the Web Vitals extension provides for interactions. The logging contains details such timings and other contextual information.&quot; decoding=&quot;async&quot; height=&quot;264&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aVIMXrpfirRew3rTM2PM.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aVIMXrpfirRew3rTM2PM.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aVIMXrpfirRew3rTM2PM.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aVIMXrpfirRew3rTM2PM.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aVIMXrpfirRew3rTM2PM.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aVIMXrpfirRew3rTM2PM.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aVIMXrpfirRew3rTM2PM.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aVIMXrpfirRew3rTM2PM.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aVIMXrpfirRew3rTM2PM.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aVIMXrpfirRew3rTM2PM.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aVIMXrpfirRew3rTM2PM.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aVIMXrpfirRew3rTM2PM.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aVIMXrpfirRew3rTM2PM.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aVIMXrpfirRew3rTM2PM.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aVIMXrpfirRew3rTM2PM.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aVIMXrpfirRew3rTM2PM.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aVIMXrpfirRew3rTM2PM.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aVIMXrpfirRew3rTM2PM.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    A console entry from the Web Vitals extension when console logging is turned on. Each qualifying interaction will log interaction data to the console.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h4 id=&quot;use-a-javascript-snippet&quot;&gt;Use a JavaScript snippet &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/diagnose-slow-interactions-in-the-lab/#use-a-javascript-snippet&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Using the Web Vitals extension may not be a viable option for a number of reasons. Extensions can be blocked in some cases, and they also can&#39;t be installed on mobile devices. The latter is problematic if you want to test on a physical Android device with &lt;a href=&quot;https://developer.chrome.com/docs/devtools/remote-debugging/&quot; rel=&quot;noopener&quot;&gt;remote debugging&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;An alternate method involves copying and pasting some JavaScript into the console of Chrome DevTools. The following code yields the same console output as the Web Vitals extension for every interaction:&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; worstInp &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;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; observer &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;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 class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; obs&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&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;let&lt;/span&gt; entry &lt;span class=&quot;token keyword&quot;&gt;of&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 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;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;interactionId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;renderTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;startTime &lt;span class=&quot;token operator&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;br /&gt;    worstInp &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;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;duration&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; worstInp&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 string&quot;&gt;&#39;[Interaction]&#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;duration&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;type: &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;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; interactionCount: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;performance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;interactionCount&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;, worstInp: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;worstInp&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; options&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;br /&gt;&lt;br /&gt;observer&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;event&#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;durationThreshold&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;span class=&quot;token comment&quot;&gt;// 16 minimum by spec&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-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; A more convenient way of using the preceding code is to use the &lt;a href=&quot;https://developer.chrome.com/docs/devtools/javascript/snippets/&quot;&gt;snippets feature in Chrome DevTools&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Once you have determined that the interaction is reliably slow, you can then profile the interaction to get more detailed information on &lt;em&gt;why&lt;/em&gt; that interaction is slow.&lt;/p&gt;
&lt;h4 id=&quot;what-if-you-cant-reproduce-a-slow-interaction&quot;&gt;What if you can&#39;t reproduce a slow interaction? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/diagnose-slow-interactions-in-the-lab/#what-if-you-cant-reproduce-a-slow-interaction&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;What if you&#39;ve managed to find something in your field data that suggests a particular interaction is slow, but you can&#39;t reproduce it in the lab?&lt;/p&gt;
&lt;p&gt;For one, this is a common challenge in troubleshooting performance issues of any kind. Your point of view when testing is entirely relative and dependent on the hardware you&#39;re using. After all, you may be on a fast device with a fast internet connection—but that doesn&#39;t mean your users are too. For this situation you can do one of three things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If you have a physical Android device, use &lt;a href=&quot;https://developer.chrome.com/docs/devtools/remote-debugging/&quot; rel=&quot;noopener&quot;&gt;remote debugging&lt;/a&gt; to open a Chrome DevTools instance on your host machine and try to reproduce slow interactions there.&lt;/li&gt;
&lt;li&gt;If you don&#39;t have a physical device, &lt;a href=&quot;https://umaar.com/dev-tips/88-cpu-throttling/&quot; rel=&quot;noopener&quot;&gt;enable the CPU throttling feature in Chrome DevTools&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Follow both steps 1 and 2, as you can also enable CPU throttling on the DevTools instance for a physical Android device.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Another cause could be that you&#39;re waiting for a page to load before you interact with it, but your users are not. If you&#39;re on a fast network, enable &lt;a href=&quot;https://developer.chrome.com/docs/devtools/settings/throttling/&quot; rel=&quot;noopener&quot;&gt;network throttling&lt;/a&gt; and interact with the page as soon as it paints. You should do this because the main thread is often busiest during startup, and testing at that time might help reveal what your users are actually experiencing.&lt;/p&gt;
&lt;h3 id=&quot;record-a-trace&quot;&gt;Record a trace &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/diagnose-slow-interactions-in-the-lab/#record-a-trace&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To get more information on why an interaction is slow, you&#39;ll need to take things to the next level by &lt;a href=&quot;https://developer.chrome.com/docs/devtools/performance/&quot; rel=&quot;noopener&quot;&gt;using the performance profiler in Chrome DevTools&lt;/a&gt;. To profile an interaction in Chrome&#39;s performance profiler, do the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;With the page you need to profile loaded and ready for interactions, open Chrome DevTools and go to the &lt;strong&gt;Performance&lt;/strong&gt; panel.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Record&lt;/strong&gt; button at the upper left of the panel to start tracing.&lt;/li&gt;
&lt;li&gt;Perform the desired interaction.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Record&lt;/strong&gt; button again to stop tracing.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When the profile populates, the first place to look should be the activity summary at the top of the profiler. The activity summary will show red bars at the top where long tasks occurred in the recording. This allows you to quickly zoom in on problem areas.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of the activity summary in the performance panel of Chrome DevTools. The activity displayed is mostly from JavaScript that causes a long task, which is highlighted in red above the flame chart.&quot; decoding=&quot;async&quot; height=&quot;126&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 674px) 674px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UcSCVSfw65rg6u1YkqmY.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UcSCVSfw65rg6u1YkqmY.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UcSCVSfw65rg6u1YkqmY.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UcSCVSfw65rg6u1YkqmY.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UcSCVSfw65rg6u1YkqmY.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UcSCVSfw65rg6u1YkqmY.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UcSCVSfw65rg6u1YkqmY.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UcSCVSfw65rg6u1YkqmY.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UcSCVSfw65rg6u1YkqmY.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UcSCVSfw65rg6u1YkqmY.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UcSCVSfw65rg6u1YkqmY.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UcSCVSfw65rg6u1YkqmY.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UcSCVSfw65rg6u1YkqmY.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UcSCVSfw65rg6u1YkqmY.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UcSCVSfw65rg6u1YkqmY.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UcSCVSfw65rg6u1YkqmY.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UcSCVSfw65rg6u1YkqmY.png?auto=format&amp;w=1348 1348w&quot; width=&quot;674&quot; /&gt;
  &lt;figcaption&gt;
    The activity summary at the top of Chrome&#39;s performance profiler. Long tasks are highlighted in red above the activity flame chart. In this case, significant scripting work was responsible for most of the work in the long task.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You can quickly focus on the problem area by dragging and selecting a region in the activity summary. Once you&#39;ve focused to where the interaction occurred, the &lt;strong&gt;Interactions&lt;/strong&gt; track will help you line up the interaction and the activity that occurred in the main thread track below it:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of an interaction as visualized in the performance panel of Chrome DevTools. An interactions track above the main thread track shows the duration of an interaction, which can be lined up with main thread activity.&quot; decoding=&quot;async&quot; height=&quot;325&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YKYLTuog3110ynQyXDdM.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YKYLTuog3110ynQyXDdM.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YKYLTuog3110ynQyXDdM.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YKYLTuog3110ynQyXDdM.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YKYLTuog3110ynQyXDdM.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YKYLTuog3110ynQyXDdM.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YKYLTuog3110ynQyXDdM.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YKYLTuog3110ynQyXDdM.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YKYLTuog3110ynQyXDdM.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YKYLTuog3110ynQyXDdM.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YKYLTuog3110ynQyXDdM.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YKYLTuog3110ynQyXDdM.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YKYLTuog3110ynQyXDdM.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YKYLTuog3110ynQyXDdM.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YKYLTuog3110ynQyXDdM.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YKYLTuog3110ynQyXDdM.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YKYLTuog3110ynQyXDdM.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YKYLTuog3110ynQyXDdM.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    An interaction profiled in the performance profiler in Chrome&#39;s DevTools. The &lt;strong&gt;Interactions&lt;/strong&gt; track shows a series of events that amount to a click interaction. The Interactions track entries span across the tasks responsible for driving the interaction.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;From here, it&#39;s a matter of observing what the problem with the interaction might be. There are many things that can contribute to high interaction latency, so let&#39;s go through what some of the culprits could be.&lt;/p&gt;
&lt;h3 id=&quot;use-lighthouse-timespans-as-an-alternative-to-tracing&quot;&gt;Use Lighthouse timespans as an alternative to tracing &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/diagnose-slow-interactions-in-the-lab/#use-lighthouse-timespans-as-an-alternative-to-tracing&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Chrome&#39;s performance profiler—while rich with diagnostic information—can be a bit intimidating to the uninitiated. One alternative to the performance profiler is Lighthouse&#39;s timespan mode. To use this mode, do the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;With DevTools open, go to the &lt;strong&gt;Lighthouse&lt;/strong&gt; tab in DevTools.&lt;/li&gt;
&lt;li&gt;Under the section labeled &lt;strong&gt;Mode&lt;/strong&gt;, select the &lt;strong&gt;Timespan&lt;/strong&gt; option.&lt;/li&gt;
&lt;li&gt;Select the desired device type under the section labeled &lt;strong&gt;Device&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Ensure at least the checkbox labeled &lt;strong&gt;Performance&lt;/strong&gt; is selected under the &lt;strong&gt;Categories&lt;/strong&gt; label.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Start timespan&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt;Test the desired interaction(s) on the page.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;End timespan&lt;/strong&gt; button and wait for the audit to appear&lt;/li&gt;
&lt;li&gt;Once the audit populates in the Lighthouse tab, you can filter the audits by INP by clicking the &lt;strong&gt;INP&lt;/strong&gt; link next to the label which reads &lt;strong&gt;Show audits relevant to&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At this point, you&#39;ll see a drop-down list for audits that have failed or passed. When you expand that drop-down, you&#39;ll probably see a breakdown of time spent during the interaction.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of a Lighthouse audit provided by its timespan mode. The audit is specific to INP, and shows details for an interaction, including a screenshot of the element that triggered it, and a table beneath detailing where time was spent processing the interaction.&quot; decoding=&quot;async&quot; height=&quot;662&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/F3fWEdo61KDoAE1OQx9X.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/F3fWEdo61KDoAE1OQx9X.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/F3fWEdo61KDoAE1OQx9X.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/F3fWEdo61KDoAE1OQx9X.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/F3fWEdo61KDoAE1OQx9X.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/F3fWEdo61KDoAE1OQx9X.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/F3fWEdo61KDoAE1OQx9X.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/F3fWEdo61KDoAE1OQx9X.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/F3fWEdo61KDoAE1OQx9X.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/F3fWEdo61KDoAE1OQx9X.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/F3fWEdo61KDoAE1OQx9X.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/F3fWEdo61KDoAE1OQx9X.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/F3fWEdo61KDoAE1OQx9X.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/F3fWEdo61KDoAE1OQx9X.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/F3fWEdo61KDoAE1OQx9X.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/F3fWEdo61KDoAE1OQx9X.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/F3fWEdo61KDoAE1OQx9X.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/F3fWEdo61KDoAE1OQx9X.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    An interaction profiled in Lighthouse&#39;s timespan mode. When interactions are made with the page, Lighthouse provides an audit detailing where the time during an interaction was spent, and breaks it down by input delay, processing delay, and presentation delay.
  &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 you want to further investigate the cause behind a slow interaction, you can do so by clicking the &lt;strong&gt;View Trace&lt;/strong&gt; button just above the filmstrip. This will take you to the performance profiler that shows interactions details for the interactions that you tested in the timespan mode. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;how-to-identify-long-input-delays&quot;&gt;How to identify long input delays &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/diagnose-slow-interactions-in-the-lab/#how-to-identify-long-input-delays&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One thing that could be contributing to high interaction latency is input delay. The &lt;em&gt;input delay&lt;/em&gt; is the first phase of an interaction. This is the period of time from when the user action is first received by the operating system until the browser is able to start processing the first event triggered by that input. The input delay ends right as the event callbacks for the interaction begin to run.&lt;/p&gt;
&lt;p&gt;Identifying input delay in Chrome&#39;s performance profiler can be done by finding the start of an interaction in the interactions panel, and then finding the beginning of when the event callbacks for that interaction start to run.&lt;/p&gt;
&lt;p&gt;You&#39;ll always incur at least some input delay, as it takes some time for the operating system to pass the input event to the browser—but you do have some control over how long the input delay is. &lt;strong&gt;The key is to figure out if there is work running on the main thread that&#39;s preventing your callbacks from running.&lt;/strong&gt;&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A depiction of input delay in Chrome&amp;#x27;s performance panel. The start of the interaction comes significantly before the event callbacks because of increased input delay due to a timer firing from a third-party script.&quot; decoding=&quot;async&quot; height=&quot;291&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UrkbAOpDSW2ABcG5ZHE3.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UrkbAOpDSW2ABcG5ZHE3.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UrkbAOpDSW2ABcG5ZHE3.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UrkbAOpDSW2ABcG5ZHE3.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UrkbAOpDSW2ABcG5ZHE3.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UrkbAOpDSW2ABcG5ZHE3.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UrkbAOpDSW2ABcG5ZHE3.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UrkbAOpDSW2ABcG5ZHE3.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UrkbAOpDSW2ABcG5ZHE3.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UrkbAOpDSW2ABcG5ZHE3.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UrkbAOpDSW2ABcG5ZHE3.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UrkbAOpDSW2ABcG5ZHE3.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UrkbAOpDSW2ABcG5ZHE3.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UrkbAOpDSW2ABcG5ZHE3.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UrkbAOpDSW2ABcG5ZHE3.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UrkbAOpDSW2ABcG5ZHE3.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UrkbAOpDSW2ABcG5ZHE3.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/UrkbAOpDSW2ABcG5ZHE3.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Input delay caused by a task fired by a timer from a third-party script.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In the previous figure, a task from a third-party script is running as the user attempts to interact with the page, and therefore extends the input delay. The extended input delay affects the interaction&#39;s latency, and could therefore affect the page&#39;s INP.&lt;/p&gt;
&lt;p&gt;For more information on how you can resolve long input delays, read about how you can &lt;a href=&quot;https://web.dev/optimize-input-delay/&quot;&gt;identify and reduce input delay&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;how-to-identify-expensive-event-callbacks&quot;&gt;How to identify expensive event callbacks &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/diagnose-slow-interactions-in-the-lab/#how-to-identify-expensive-event-callbacks&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Event callbacks occur immediately after the input delay. If an event callback runs too long, it delays the browser from presenting the next frame, and can add significantly to an interaction&#39;s total latency. Event callbacks can often run for too long, whether they run as the result of first-party or third-party JavaScript—and in some cases, both.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A depiction of event callback tasks in Chrome&amp;#x27;s performance panel. The event callbacks occur for the pointerdown and click events, which occur in a long task.&quot; decoding=&quot;async&quot; height=&quot;291&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/h0UKtDuUBvWxd10vjrsn.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/h0UKtDuUBvWxd10vjrsn.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/h0UKtDuUBvWxd10vjrsn.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/h0UKtDuUBvWxd10vjrsn.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/h0UKtDuUBvWxd10vjrsn.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/h0UKtDuUBvWxd10vjrsn.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/h0UKtDuUBvWxd10vjrsn.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/h0UKtDuUBvWxd10vjrsn.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/h0UKtDuUBvWxd10vjrsn.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/h0UKtDuUBvWxd10vjrsn.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/h0UKtDuUBvWxd10vjrsn.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/h0UKtDuUBvWxd10vjrsn.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/h0UKtDuUBvWxd10vjrsn.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/h0UKtDuUBvWxd10vjrsn.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/h0UKtDuUBvWxd10vjrsn.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/h0UKtDuUBvWxd10vjrsn.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/h0UKtDuUBvWxd10vjrsn.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/h0UKtDuUBvWxd10vjrsn.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    The event callbacks that run in response to a click interaction, as shown in the performance profiler in Chrome DevTools. Note the red triangle in the upper right corner of both the &lt;strong&gt;Event: pointerdown&lt;/strong&gt; and &lt;strong&gt;Event: click&lt;/strong&gt; entries, which identifies expensive event callbacks.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Finding expensive event callbacks can be done by observing the following in a trace for a specific interaction:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Determine whether the task associated with the event callbacks is a &lt;a href=&quot;https://web.dev/long-tasks-devtools/#what-are-long-tasks&quot;&gt;long task&lt;/a&gt;. To reveal long tasks in a lab setting more reliably, you may need to &lt;a href=&quot;https://developer.chrome.com/blog/new-in-devtools-61/#throttling&quot; rel=&quot;noopener&quot;&gt;enable CPU throttling&lt;/a&gt; in the performance panel, or connect a low to mid-tier Android device and use &lt;a href=&quot;https://developer.chrome.com/docs/devtools/remote-debugging/&quot; rel=&quot;noopener&quot;&gt;remote debugging&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;If the task that runs the event callbacks is a long task, look for event handler entries (entries with names such as &lt;strong&gt;Event: click&lt;/strong&gt;, for example) in the call stack that have a red triangle at the upper right corner of the entry. These are expensive event callbacks.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To address expensive event callbacks, try one of the following strategies:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Do as little work as possible.&lt;/strong&gt; Is everything that happens in an expensive event callback strictly necessary? If not, consider removing that code altogether if you can, or deferring its execution to a later point in time if you can&#39;t. You can also take advantage of framework features to help. For example, React&#39;s &lt;a href=&quot;https://beta.reactjs.org/reference/react/PureComponent&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;PureComponent&lt;/code&gt; class&lt;/a&gt; and &lt;a href=&quot;https://react.dev/reference/react/memo&quot; rel=&quot;noopener&quot;&gt;memoization feature&lt;/a&gt; can skip unnecessary rendering work when props and state haven&#39;t changed for a component.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Defer non-rendering work in the event callback to a later point in time.&lt;/strong&gt; Long tasks can be broken up 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;. Whenever you yield to the main thread, you&#39;re ending execution of the current task and breaking up the remainder of the work into a separate task. This gives the renderer a chance to process updates to the user interface that were processed earlier in the event callback. If you happen to be developing in React, its &lt;a href=&quot;https://react.dev/reference/react/useTransition&quot; rel=&quot;noopener&quot;&gt;transitions feature&lt;/a&gt; will do this for you.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By employing these strategies, you should be able to get your event callbacks in a place where they&#39;re responding more quickly to user input.&lt;/p&gt;
&lt;h2 id=&quot;how-to-identify-presentation-delays&quot;&gt;How to identify presentation delays &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/diagnose-slow-interactions-in-the-lab/#how-to-identify-presentation-delays&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Long input delays and expensive event callbacks aren&#39;t the only possible culprits of poor INP. Sometimes the rendering updates that occur in response to even small amounts of event callback code can be expensive. The time it takes for the browser to render visual updates to the user interface to reflect the result of an interaction is known as &lt;em&gt;presentation delay&lt;/em&gt;.&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; The &lt;a href=&quot;https://web.dev/rendering-performance/&quot;&gt;rendering performance guide&lt;/a&gt; is a good primer on understanding how the browser renders a page. &lt;a href=&quot;https://www.youtube.com/watch?v=K2QHdgAKP-s&quot;&gt;Life of a pixel&lt;/a&gt; goes further in depth on how rendering works in Chromium-based browsers. &lt;/div&gt;&lt;/aside&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Rendering work as visualized in the performance panel of Chrome DevTools. The rendering work occurs after the event callback in order to paint the next frame.&quot; decoding=&quot;async&quot; height=&quot;229&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/pFmaZMXrlbugrC9WPoHM.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/pFmaZMXrlbugrC9WPoHM.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/pFmaZMXrlbugrC9WPoHM.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/pFmaZMXrlbugrC9WPoHM.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/pFmaZMXrlbugrC9WPoHM.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/pFmaZMXrlbugrC9WPoHM.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/pFmaZMXrlbugrC9WPoHM.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/pFmaZMXrlbugrC9WPoHM.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/pFmaZMXrlbugrC9WPoHM.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/pFmaZMXrlbugrC9WPoHM.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/pFmaZMXrlbugrC9WPoHM.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/pFmaZMXrlbugrC9WPoHM.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/pFmaZMXrlbugrC9WPoHM.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/pFmaZMXrlbugrC9WPoHM.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/pFmaZMXrlbugrC9WPoHM.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/pFmaZMXrlbugrC9WPoHM.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/pFmaZMXrlbugrC9WPoHM.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/pFmaZMXrlbugrC9WPoHM.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Rendering tasks as shown in Chrome&#39;s performance profiler. The rendering work is shown in purple, with paint work in green.
  &lt;/figcaption&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; When profiling slow interactions due to expensive rendering work, you should enable the &lt;strong&gt;Screenshots&lt;/strong&gt; checkbox at the top of the performance profiler. This will help you to line up user interface updates with the corresponding rendering work on the main thread. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Of all the possible causes of high interaction latency, rendering work can be the most difficult to troubleshoot and fix, but the result is worth the effort. Excessive rendering work could be caused by any of the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Large DOM sizes.&lt;/strong&gt; Rendering work often increases along with the size of the page&#39;s DOM. For more information, read &lt;a href=&quot;https://web.dev/dom-size-and-interactivity/&quot;&gt;How large DOM sizes affect interactivity—and what you can do about it&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Forced reflows.&lt;/strong&gt; This happens when you apply style changes to elements in JavaScript, and &lt;em&gt;then&lt;/em&gt; query the results of that work. The result is that the browser has to perform the layout work before doing anything else, so that the browser can return the updated styles. For more information and tips on avoiding forced reflows, read &lt;a href=&quot;https://web.dev/avoid-large-complex-layouts-and-layout-thrashing/&quot;&gt;Avoid large, complex layouts and layout thrashing&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Excessive or unnecessary work in &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; callbacks.&lt;/strong&gt; &lt;code&gt;requestAnimationFrame()&lt;/code&gt; callbacks are run during the rendering phase of the event loop, and must complete before the next frame can be presented. If you&#39;re using &lt;code&gt;requestAnimationFrame()&lt;/code&gt; to do work that doesn&#39;t involve changes to the user interface, understand that you could be delaying the next frame.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/ResizeObserver&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;ResizeObserver&lt;/code&gt;&lt;/a&gt; callbacks.&lt;/strong&gt; Such callbacks run prior to rendering, and may delay presentation of the next frame if the work in them is expensive. As with event callbacks, defer any logic not needed for the next frame.&lt;/li&gt;
&lt;/ul&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; Troubleshooting might be made easier by enabling the &lt;strong&gt;Timeline: event initiators&lt;/strong&gt; experiment in Chrome DevTools. Enabling this experiment will draw an arrow from any style recalculation task to the JavaScript task that kicked it off. To enable this experiment, go into the Chrome DevTools settings, click on &lt;strong&gt;Experiments&lt;/strong&gt; in the left hand menu, the click the checkbox labeled &lt;strong&gt;Timeline: event initiators&lt;/strong&gt;, and restart DevTools. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;troubleshooting-inp-is-an-iterative-process&quot;&gt;Troubleshooting INP is an iterative process &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/diagnose-slow-interactions-in-the-lab/#troubleshooting-inp-is-an-iterative-process&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Finding out what&#39;s causing high interaction latency that contributes to poor INP takes a lot of work—but if you can pin down the causes, you&#39;re halfway there. By following a methodical approach to troubleshooting poor INP, you can reliably pin down what&#39;s causing a problem, and arrive more quickly to the right fix. To review:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/find-slow-interactions-in-the-field/&quot;&gt;Rely on field data to find slow interactions&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Test problematic field interactions in the lab to see if they&#39;re reproducible.&lt;/li&gt;
&lt;li&gt;Identify whether the cause is due to long input delay, expensive event callbacks, or expensive rendering work.&lt;/li&gt;
&lt;li&gt;Repeat.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The last of these is the most important. Like most other work you must do to improve page performance, troubleshooting and improving INP is a cyclical process. When you fix one slow interaction, you&#39;ll need to move onto the next, and continue to do so until you start to see results. Stay vigilant!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hero image from &lt;a href=&quot;https://unsplash.com/&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;, by &lt;a href=&quot;https://unsplash.com/@_louisreed&quot; rel=&quot;noopener&quot;&gt;Louis Reed&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>How large DOM sizes affect interactivity, and what you can do about it</title>
    <link href="https://web.dev/dom-size-and-interactivity/"/>
    <updated>2023-05-09T00:00:00Z</updated>
    <id>https://web.dev/dom-size-and-interactivity/</id>
    <content type="html" mode="escaped">&lt;p&gt;There&#39;s no way around it: when you build a web page, that page is going to have a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Document_Object_Model&quot; rel=&quot;noopener&quot;&gt;Document Object Model (DOM)&lt;/a&gt;. The DOM represents the structure of your page&#39;s HTML, and gives JavaScript and CSS access to a page&#39;s structure and contents.&lt;/p&gt;
&lt;p&gt;The problem, however, is that the &lt;em&gt;size&lt;/em&gt; of the DOM affects a browser&#39;s ability to render a page quickly and efficiently. Generally speaking, the larger a DOM is, the more expensive it is to initially render that page and update its rendering later on in the page lifecycle.&lt;/p&gt;
&lt;p&gt;This becomes problematic in pages with very large DOMs when interactions that modify or update the DOM trigger expensive layout work that affects the ability of the page to respond quickly. Expensive layout work can affect a page&#39;s &lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt;; If you want a page to respond quickly to user interactions, it&#39;s important to ensure your DOM sizes are only as large as necessary.&lt;/p&gt;
&lt;h2 id=&quot;when-is-a-pages-dom-too-large&quot;&gt;When is a page&#39;s DOM &lt;em&gt;too&lt;/em&gt; large? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/dom-size-and-interactivity/#when-is-a-pages-dom-too-large&quot;&gt;#&lt;/a&gt;&lt;/h2&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; It&#39;s important to know the difference between DOM elements and DOM nodes. A DOM &lt;em&gt;element&lt;/em&gt; refers to a specific &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/HTMLElement&quot;&gt;HTML element&lt;/a&gt; in the DOM tree. A &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Node&quot;&gt;DOM &lt;em&gt;node&lt;/em&gt;&lt;/a&gt; has overlapping meaning with the DOM element term, but its definition is expanded to include comments, whitespace, and text. While the &lt;a href=&quot;https://developer.chrome.com/en/docs/lighthouse/performance/dom-size/&quot;&gt;Lighthouse DOM size audit&lt;/a&gt; refers to DOM nodes, this guide will refer to DOM elements over nodes whenever possible. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/en/docs/lighthouse/performance/dom-size/#how-the-lighthouse-dom-size-audit-fails&quot; rel=&quot;noopener&quot;&gt;According to Lighthouse&lt;/a&gt;, a page&#39;s DOM size is excessive when it exceeds 1,400 nodes. Lighthouse will begin to throw warnings when a page&#39;s DOM exceeds 800 nodes. Take the following HTML 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 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;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;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;List item one.&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;li&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;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;List item two.&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;li&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;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;List item three.&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;li&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 the above code, there are four DOM elements: the &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; element, and its three &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; child elements. Your web page will almost certainly have many more nodes than this, so it&#39;s important to understand what you can do to keep DOM sizes in check—as well as other strategies to optimize the rendering work once you&#39;ve gotten a page&#39;s DOM as small as it can be.&lt;/p&gt;
&lt;h2 id=&quot;how-do-large-doms-affect-page-performance&quot;&gt;How do large DOMs affect page performance? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/dom-size-and-interactivity/#how-do-large-doms-affect-page-performance&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Large DOMs affect page performance in a few ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;During the page&#39;s initial render. When CSS is applied to a page, a structure similar to the DOM known as the &lt;a href=&quot;https://web.dev/critical-rendering-path-constructing-the-object-model/#css-object-model-cssom&quot;&gt;CSS Object Model (CSSOM)&lt;/a&gt; is created. As CSS selectors increase in specificity, the CSSOM becomes more complex, and more time is needed to run the necessary layout, styling, compositing, and paint work necessary to draw the web page to the screen. This added work increases interaction latency for interactions that occur early on during page load.&lt;/li&gt;
&lt;li&gt;When interactions modify the DOM, either through element insertion or deletion, or by modifying DOM contents and styles, the work necessary to render that update can result in very costly layout, styling, compositing, and paint work. As is the case with the page&#39;s initial render, an increase in CSS selector specificity can add to rendering work when HTML elements are inserted into the DOM as the result of an interaction.&lt;/li&gt;
&lt;li&gt;When JavaScript queries the DOM, references to DOM elements are stored in memory. For example, if you call &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Document/querySelectorAll&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;document.querySelectorAll&lt;/code&gt;&lt;/a&gt; to select all &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elements on a page, the memory cost could be considerable if the result returns a large number of DOM elements.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of a long task caused by excessive rendering work in the performance panel of Chrome DevTools. The long task&amp;#x27;s call stack shows significant time spent recalculating page styles, as well as pre-paint.&quot; decoding=&quot;async&quot; height=&quot;248&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qqLuCIvjmvKn5L8YTLGt.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qqLuCIvjmvKn5L8YTLGt.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qqLuCIvjmvKn5L8YTLGt.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qqLuCIvjmvKn5L8YTLGt.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qqLuCIvjmvKn5L8YTLGt.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qqLuCIvjmvKn5L8YTLGt.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qqLuCIvjmvKn5L8YTLGt.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qqLuCIvjmvKn5L8YTLGt.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qqLuCIvjmvKn5L8YTLGt.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qqLuCIvjmvKn5L8YTLGt.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qqLuCIvjmvKn5L8YTLGt.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qqLuCIvjmvKn5L8YTLGt.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qqLuCIvjmvKn5L8YTLGt.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qqLuCIvjmvKn5L8YTLGt.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qqLuCIvjmvKn5L8YTLGt.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qqLuCIvjmvKn5L8YTLGt.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qqLuCIvjmvKn5L8YTLGt.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qqLuCIvjmvKn5L8YTLGt.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    A long task as shown in the performance profiler in Chrome DevTools. The long task shown is caused by inserting DOM elements into a large DOM via JavaScript.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;All of these can affect interactivity, but the second item in the list above is of particular importance. If an interaction results in a change to the DOM, it can kick off a lot of work that can contribute to a poor INP on a page.&lt;/p&gt;
&lt;h2 id=&quot;how-do-i-measure-dom-size&quot;&gt;How do I measure DOM size? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/dom-size-and-interactivity/#how-do-i-measure-dom-size&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can measure DOM size in a couple of ways. The first method uses Lighthouse. When you run an audit, statistics on the current page&#39;s DOM will be in the &amp;quot;Avoid an excessive DOM size&amp;quot; audit under the &amp;quot;Diagnostics&amp;quot; heading. In this section, you can see the total number of DOM elements, the DOM element containing the most child elements, as well as the deepest DOM element.&lt;/p&gt;
&lt;p&gt;A simpler method involves using the JavaScript console in the developer tools in any major browser. To get the total number of HTML elements in the DOM, you can use the following code in the console after the page has loaded:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&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;*&#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;length&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; Note that the code snippet above only includes the number of HTML &lt;em&gt;elements&lt;/em&gt; in the DOM. It doesn&#39;t include all the &lt;em&gt;nodes&lt;/em&gt; in the DOM. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;If you want to see the DOM size update in realtime, you can also use the &lt;a href=&quot;https://developer.chrome.com/blog/new-in-devtools-64/#perf-monitor&quot; rel=&quot;noopener&quot;&gt;performance monitor tool&lt;/a&gt;. Using this tool, you can correlate layout and styling operations (and other performance aspects) along with the current DOM size.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of the performance monitor in Chrome DevTools. At left, there are various aspects of page performance that can be continuously monitored during the life of the page. In the screenshot, the number of DOM nodes, layouts per second, and style recalculations per section are actively being monitored.&quot; decoding=&quot;async&quot; height=&quot;273&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mjhhoz41DjdR4cZFwU89.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mjhhoz41DjdR4cZFwU89.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mjhhoz41DjdR4cZFwU89.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mjhhoz41DjdR4cZFwU89.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mjhhoz41DjdR4cZFwU89.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mjhhoz41DjdR4cZFwU89.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mjhhoz41DjdR4cZFwU89.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mjhhoz41DjdR4cZFwU89.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mjhhoz41DjdR4cZFwU89.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mjhhoz41DjdR4cZFwU89.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mjhhoz41DjdR4cZFwU89.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mjhhoz41DjdR4cZFwU89.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mjhhoz41DjdR4cZFwU89.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mjhhoz41DjdR4cZFwU89.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mjhhoz41DjdR4cZFwU89.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mjhhoz41DjdR4cZFwU89.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mjhhoz41DjdR4cZFwU89.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mjhhoz41DjdR4cZFwU89.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    The performance monitor in Chrome DevTools. In this view, the page&#39;s current number of DOM nodes is charted along with layout operations and style recalculations performed per second.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If the DOM&#39;s size is approaching Lighthouse DOM size&#39;s warning threshold—or fails altogether—the next step is to figure out how to reduce the DOM&#39;s size to improve your page&#39;s ability to respond to user interactions so that your website&#39;s INP can improve.&lt;/p&gt;
&lt;h2 id=&quot;how-can-i-measure-the-number-of-dom-elements-affected-by-an-interaction&quot;&gt;How can I measure the number of DOM elements affected by an interaction? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/dom-size-and-interactivity/#how-can-i-measure-the-number-of-dom-elements-affected-by-an-interaction&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you&#39;re profiling a slow interaction in the lab that you suspect might have something to do with the size of the page&#39;s DOM, you can figure out how many DOM elements were affected by selecting any piece of activity in the profiler labeled &amp;quot;Recalculate Style&amp;quot; and observe the contextual data in the bottom panel.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of selected style recalculation activity in the performance panel of Chrome DevTools. At top, the interactions track shows a click interaction, and the majority of the work is spent doing style recalculation and pre-paint work. At the bottom, a panel shows more detail for the selected activity, which reports that 2,547 DOM elements were affected.&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/IjTPVbiLoerWFO5eClbl.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/IjTPVbiLoerWFO5eClbl.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/IjTPVbiLoerWFO5eClbl.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/IjTPVbiLoerWFO5eClbl.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/IjTPVbiLoerWFO5eClbl.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/IjTPVbiLoerWFO5eClbl.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/IjTPVbiLoerWFO5eClbl.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/IjTPVbiLoerWFO5eClbl.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/IjTPVbiLoerWFO5eClbl.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/IjTPVbiLoerWFO5eClbl.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/IjTPVbiLoerWFO5eClbl.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/IjTPVbiLoerWFO5eClbl.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/IjTPVbiLoerWFO5eClbl.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/IjTPVbiLoerWFO5eClbl.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/IjTPVbiLoerWFO5eClbl.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/IjTPVbiLoerWFO5eClbl.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/IjTPVbiLoerWFO5eClbl.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/IjTPVbiLoerWFO5eClbl.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Observing the number of affected elements in the DOM as the result of style recalculation work. Note that the shaded portion of the interaction in the interactions track represents the portion of the interaction duration that was over 200 milliseconds, which is &lt;a href=&quot;https://web.dev/inp/#what-is-a-good-inp-score&quot; rel=&quot;noopener&quot;&gt;the designated &quot;good&quot; threshold for INP&lt;/a&gt;.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In the above screenshot, observe that the style recalculation of the work—when selected—shows the number of affected elements. While the above screenshot shows an extreme case of the effect of DOM size on rendering work on a page with many DOM elements, this diagnostic info is useful in any case to determine if the size of the DOM is a limiting factor in how long it takes for the next frame to paint in response to an interaction.&lt;/p&gt;
&lt;h2 id=&quot;how-can-i-reduce-dom-size&quot;&gt;How can I reduce DOM size? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/dom-size-and-interactivity/#how-can-i-reduce-dom-size&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Beyond auditing your website&#39;s HTML for unnecessary markup, the principal way to reduce DOM size is to reduce DOM depth. One signal that your DOM might be unnecessarily deep is if you&#39;re seeing markup that looks something like this in the &lt;strong&gt;Elements&lt;/strong&gt; tab of your browser&#39;s developer tools:&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;div&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;div&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;div&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;div&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 comment&quot;&gt;&amp;lt;!-- Contents --&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;div&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;div&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;div&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;div&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 you see patterns like this, you can probably simplify them by flattening your DOM structure. Doing so will reduce the number of DOM elements, and likely give you an opportunity to simplify page styles.&lt;/p&gt;
&lt;p&gt;DOM depth may also be a symptom of the frameworks you use. In particular, component-based frameworks—such as those that rely on &lt;a href=&quot;https://reactjs.org/docs/introducing-jsx.html&quot; rel=&quot;noopener&quot;&gt;JSX&lt;/a&gt;—require you to nest multiple components in a parent container.&lt;/p&gt;
&lt;p&gt;However, many frameworks allow you to avoid nesting components by using what are known as fragments. Component-based frameworks that offer fragments as a feature include (but are not limited to) the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://reactjs.org/docs/fragments.html&quot; rel=&quot;noopener&quot;&gt;React&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://preactjs.com/guide/v10/components/#fragments&quot; rel=&quot;noopener&quot;&gt;Preact&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://v3-migration.vuejs.org/new/fragments.html&quot; rel=&quot;noopener&quot;&gt;Vue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://svelte.dev/tutorial/svelte-fragment&quot; rel=&quot;noopener&quot;&gt;Svelte&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By using fragments in your framework of choice, you can reduce DOM depth. If you&#39;re concerned about the impact flattening DOM structure has on styling, you might benefit from using more modern (and faster) layout modes such as &lt;a href=&quot;https://developer.mozilla.org/docs/Learn/CSS/CSS_layout/Flexbox&quot; rel=&quot;noopener&quot;&gt;flexbox&lt;/a&gt; or &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/CSS_Grid_Layout&quot; rel=&quot;noopener&quot;&gt;grid&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;other-strategies-to-consider&quot;&gt;Other strategies to consider &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/dom-size-and-interactivity/#other-strategies-to-consider&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Even if you take pains to flatten your DOM tree and remove unnecessary HTML elements to keep your DOM as small as possible, it can still be quite large and kick off a lot of rendering work as it changes in response to user interactions. If you find yourself in this position, there are some other strategies you can consider to limit rendering work.&lt;/p&gt;
&lt;h3 id=&quot;consider-an-additive-approach&quot;&gt;Consider an additive approach &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/dom-size-and-interactivity/#consider-an-additive-approach&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You might be in a position where large parts of your page aren&#39;t initially visible to the user when it first renders. This could be an opportunity to lazy load HTML by omitting those parts of the DOM on startup, but add them in when the user interacts with the parts of the page that require the initially hidden aspects of the page.&lt;/p&gt;
&lt;p&gt;This approach is useful both during the initial load and perhaps even afterwards. For the initial page load, you&#39;re taking on less rendering work up front, meaning that your initial HTML payload will be lighter, and will render more quickly. This will give interactions during that crucial period more opportunities to run with less competition for the main thread&#39;s attention.&lt;/p&gt;
&lt;p&gt;If you have many parts of the page that are initially hidden on load, it could also speed up other interactions that trigger re-rendering work. However, as other interactions add more to the DOM, rendering work will increase as the DOM grows throughout the page lifecycle.&lt;/p&gt;
&lt;p&gt;Adding to the DOM over time can be tricky, and it has its own tradeoffs. If you&#39;re going this route, you&#39;re likely making network requests to get data to populate the HTML you intend to add to the page in response to a user interaction. While in-flight network requests are not counted towards INP, it can increase perceived latency. If possible, show a loading spinner or other indicator that data is being fetched so that users understand that something is happening.&lt;/p&gt;
&lt;h3 id=&quot;limit-css-selector-complexity&quot;&gt;Limit CSS selector complexity &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/dom-size-and-interactivity/#limit-css-selector-complexity&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When the browser parses selectors in your CSS, it has to traverse the DOM tree to understand how—and if—those selectors apply to the current layout. The more complex these selectors are, the more work the browser has to do in order to perform both the initial rendering of the page, as well as increased style recalculations and layout work if the page changes as the result of an interaction.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-good-bg color-state-good-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; height=&quot;24&quot; width=&quot;24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Check&quot;&gt;   &lt;path d=&quot;M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Objective&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Read to learn more:&lt;/strong&gt; &lt;a href=&quot;https://web.dev/reduce-the-scope-and-complexity-of-style-calculations/&quot;&gt;Reduce the scope and complexity of style calculations&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;use-the-content-visibility-property&quot;&gt;Use the &lt;code&gt;content-visibility&lt;/code&gt; property &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/dom-size-and-interactivity/#use-the-content-visibility-property&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;CSS offers the &lt;code&gt;content-visibility&lt;/code&gt; property, which is effectively a way to lazily render off-screen DOM elements. As the elements approach the viewport, they&#39;re rendered on demand. The benefits of &lt;code&gt;content-visibility&lt;/code&gt; don&#39;t just cut out a significant amount of rendering work on the initial page render, but also skip rendering work for offscreen elements when the page DOM is changed as the result of a user interaction.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-good-bg color-state-good-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; height=&quot;24&quot; width=&quot;24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Check&quot;&gt;   &lt;path d=&quot;M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Objective&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Read to learn more:&lt;/strong&gt; &lt;a href=&quot;https://web.dev/content-visibility/&quot;&gt;&lt;code&gt;content-visibility&lt;/code&gt;: the new CSS property that boosts your rendering performance&lt;/a&gt;. &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/dom-size-and-interactivity/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Reducing your DOM size to only what is strictly necessary is a good way to optimize your website&#39;s INP. By doing so, you can reduce the amount of time it takes for the browser to perform layout and rendering work when the DOM is updated. Even if you can&#39;t meaningfully reduce DOM size, there are some techniques you can use to isolate rendering work to a DOM subtree, such as CSS containment and the &lt;code&gt;content-visibility&lt;/code&gt; CSS property.&lt;/p&gt;
&lt;p&gt;However you go about it, creating an environment where rendering work is minimized—as well as reducing the amount of rendering work your page does in response to interactions—the result will be that your website will feel more responsive to users when they interact with them. That means you&#39;ll have a lower INP for your website, and that translates to a better user experience.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hero image from &lt;a href=&quot;https://unsplash.com/&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;, by &lt;a href=&quot;https://unsplash.com/@_louisreed&quot; rel=&quot;noopener&quot;&gt;Louis Reed&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Find slow interactions in the field</title>
    <link href="https://web.dev/find-slow-interactions-in-the-field/"/>
    <updated>2023-05-09T00:00:00Z</updated>
    <id>https://web.dev/find-slow-interactions-in-the-field/</id>
    <content type="html" mode="escaped">&lt;p&gt;&lt;a href=&quot;https://web.dev/lab-and-field-data-differences/#field-data&quot;&gt;Field data&lt;/a&gt; is &lt;em&gt;the&lt;/em&gt; authoritative source when it comes to how actual users are experiencing your website. It teases out issues you may not see in &lt;a href=&quot;https://web.dev/lab-and-field-data-differences/#lab-data&quot;&gt;lab data&lt;/a&gt; alone. While interactions can be simulated in lab-based tools, you&#39;re not going to be able to reproduce every single interaction in the lab in the way that users in the field experience them. Gathering field data for &lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt; is critical to understanding how responsive your page is to real users, and it contains the clues to make their experience even better.&lt;/p&gt;
&lt;h2 id=&quot;what-you-should-collect-in-the-field-and-why&quot;&gt;What you should collect in the field and why &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/find-slow-interactions-in-the-field/#what-you-should-collect-in-the-field-and-why&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When collecting INP data in the field, you&#39;ll want to capture the following to give context to why interactions were slow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The INP value.&lt;/strong&gt; This is the key piece of data you&#39;ll need to collect. The distribution of these values will determine whether the page meets &lt;a href=&quot;https://web.dev/inp/#what-is-a-good-inp-score&quot;&gt;the INP thresholds&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The element selector string responsible for the page&#39;s INP.&lt;/strong&gt; Knowing a page&#39;s INP is good, but not good enough by itself. Without knowing what element was responsible for it, you&#39;ll be in the dark. By logging element selector strings, you’ll know exactly what elements are responsible for interactions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The loading state of the page for the interaction that is the page&#39;s INP.&lt;/strong&gt; When a page is loading, it&#39;s reasonable to assume that there&#39;s more main thread activity occurring that could result in higher interaction latency. During page load, there&#39;s HTML parsing, stylesheet parsing, and JavaScript evaluation and execution going on. Knowing whether an interaction has taken place during page load or afterwards helps you to figure out if you need to optimize for faster startup so interactions have more room on the main thread to run quickly, or if the interaction responsible for the page&#39;s INP in itself is slow regardless.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The interaction&#39;s &lt;code&gt;startTime&lt;/code&gt;.&lt;/strong&gt; Logging the interaction&#39;s &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/PerformanceEntry/startTime&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;startTime&lt;/code&gt;&lt;/a&gt; lets you know exactly when the interaction occurred on the performance timeline.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The event type.&lt;/strong&gt; The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Event/type&quot; rel=&quot;noopener&quot;&gt;event type&lt;/a&gt; is good to know, as it will tell you whether the interaction was the result of a &lt;code&gt;click&lt;/code&gt;, &lt;code&gt;keypress&lt;/code&gt;, or other eligible event type. A user interaction may contain multiple callbacks, and can help you to pinpoint exactly which event callback in the interaction took the longest to run.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While this all seems like a lot to take into account, you don&#39;t have to reinvent the wheel to get there. Thankfully, this data is exposed in 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;, and you&#39;ll learn how to gather it in the next section.&lt;/p&gt;
&lt;h2 id=&quot;measure-interactions-in-the-field-with-the-web-vitals-javascript-library&quot;&gt;Measure interactions in the field with the &lt;code&gt;web-vitals&lt;/code&gt; JavaScript library &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/find-slow-interactions-in-the-field/#measure-interactions-in-the-field-with-the-web-vitals-javascript-library&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;web-vitals&lt;/code&gt; JavaScript library is a great way to find slow interactions in the field, thanks in large part to its ability to provide &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals#attribution-build&quot; rel=&quot;noopener&quot;&gt;attribution&lt;/a&gt; for what&#39;s causing them. INP can be collected in browsers that support the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/PerformanceEventTiming&quot; rel=&quot;noopener&quot;&gt;Event Timing API&lt;/a&gt; and its &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/PerformanceEventTiming/interactionId&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;interactionId&lt;/code&gt; property&lt;/a&gt;.&lt;/p&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 96, 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;
      96
    &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 96, 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;
96
&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/PerformanceEventTiming/interactionId#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Using a &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; provider to get INP is most convenient, but not always an option. If that&#39;s your case, for example, you can use the &lt;code&gt;web-vitals&lt;/code&gt; JavaScript library to collect and transmit INP data to Google Analytics for later evaluation:&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;// Be sure to import from the attribution build:&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;onINP&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/attribution&#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 keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sendToGoogleAnalytics&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; attribution&lt;span class=&quot;token punctuation&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 comment&quot;&gt;// Destructure the attribution object:&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;eventEntry&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; eventTarget&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; eventType&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; loadState&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; attribution&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 timings from the event timing entry:&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;startTime&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; processingStart&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; processingEnd&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; duration&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; interactionId&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; eventEntry&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;const&lt;/span&gt; eventParams &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 comment&quot;&gt;// The page&#39;s INP value:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;metric_inp_value&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// A unique ID for the page session, which is useful&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// for computing totals when you group by the ID.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;metric_id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// The event target (a CSS selector string pointing&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// to the element responsible for the interaction):&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;metric_inp_event_target&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; eventTarget&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// The type of event that triggered the interaction:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;metric_inp_event_type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; eventType&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Whether the page was loaded when the interaction&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// took place. Useful for identifying startup versus&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// post-load interactions:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;metric_inp_load_state&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; loadState&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// The time (in milliseconds) after page load when&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// the interaction took place:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;metric_inp_start_time&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; startTime&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// When processing of the event callbacks in the&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// interaction started to run:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;metric_inp_processing_start&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; processingStart&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// When processing of the event callbacks in the&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// interaction finished:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;metric_inp_processing_end&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; processingEnd&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// The total duration of the interaction. Note: this&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// value is rounded to 8 milliseconds of granularity:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;metric_inp_duration&lt;/span&gt;&lt;span class=&quot;token operator&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 comment&quot;&gt;// The interaction ID assigned to the interaction by&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// the Event Timing API. This could be useful in cases&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// where you might want to aggregate related events:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;metric_inp_interaction_id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; interactionId&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;// Send to Google Analytics&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; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; eventParams&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 comment&quot;&gt;// Pass the reporting function to the web-vitals INP reporter:&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;onINP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sendToGoogleAnalytics&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-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; Where element attribution is concerned, not every interaction can be attributed to a specific element. This is due to a limitation of the underlying &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/PerformanceEventTiming&quot;&gt;Event Timing API&lt;/a&gt;, which is used to calculate the page&#39;s INP. In this API, &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Event/target&quot;&gt;the event target&lt;/a&gt; may be absent if the element responsible for the interaction was disconnected from the DOM, or if the element was in the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Web_Components/Using_shadow_DOM&quot;&gt;shadow DOM&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;If you have Google Analytics and run the preceding code on your website, you&#39;ll get a detailed reporting of not just the page&#39;s INP, but also useful contextual information that can give you a better sense of where to begin optimizing slow interactions.&lt;/p&gt;
&lt;h3 id=&quot;monitor-full-session-duration,-not-just-up-to-onload&quot;&gt;Monitor full session duration, not just up to &lt;code&gt;onload&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/find-slow-interactions-in-the-field/#monitor-full-session-duration,-not-just-up-to-onload&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Using the &lt;code&gt;web-vitals&lt;/code&gt; JavaScript library, as previously mentioned, may result in multiple analytics events being sent to Google Analytics. This is fine, as the ID &lt;code&gt;web-vitals&lt;/code&gt; generates for the current page will stay the same, and Google Analytics will allow you to overwrite previous data.&lt;/p&gt;
&lt;p&gt;However, not all RUM providers operate this way, so if you&#39;re building your own RUM collection solution, you&#39;ll need to take this into account. If your current analytics provider won&#39;t allow overwrites of existing records, you&#39;ll need to record all the &lt;code&gt;delta&lt;/code&gt; values—that is, the difference between a metric&#39;s past and current states— for a metric and transmit them using the same ID provided by the &lt;code&gt;web-vitals&lt;/code&gt; library;  then you can sum those changes by grouping on the ID. For more more information, consult the &lt;code&gt;web-vitals&lt;/code&gt; documentation&#39;s &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals#report-only-the-delta-of-changes&quot; rel=&quot;noopener&quot;&gt;section on handling deltas&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;get-field-data-quickly-with-crux&quot;&gt;Get field data quickly with CrUX &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/find-slow-interactions-in-the-field/#get-field-data-quickly-with-crux&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;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 the official dataset of the Web Vitals program. While data from CrUX alone doesn&#39;t give you all the information you need to troubleshoot specific INP issues, it does let you know whether you have a problem in the first place. Even if you&#39;re already collecting field data through a RUM provider, consider contrasting it with CrUX data for your website (if available), as there are &lt;a href=&quot;https://web.dev/crux-and-rum-differences/&quot;&gt;differences in the methodologies they use&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can evaluate your website&#39;s INP and view its CrUX data using &lt;a href=&quot;https://pagespeed.web.dev/&quot; rel=&quot;noopener&quot;&gt;PageSpeed Insights (PSI)&lt;/a&gt;. PageSpeed Insights may provide page-level field data for websites that are included in the CrUX dataset. To audit a URL with PageSpeed Insights, go to &lt;a href=&quot;https://pagespeed.web.dev/&quot; rel=&quot;noopener&quot;&gt;https://pagespeed.web.dev/&lt;/a&gt;, enter a URL to test, and click the &lt;strong&gt;Analyze&lt;/strong&gt; button.&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; The URL you enter &lt;a href=&quot;https://web.dev/find-slow-interactions-in-the-field/%E2%80%8B%E2%80%8Bhttps://developer.chrome.com/docs/crux/methodology/#eligibility&quot;&gt;may not be eligible for CrUX&lt;/a&gt;. If this is the case, you&#39;ll need to collect your own field data, which is explained in the previous section. Also, where page-level data is not available for the given URL in PageSpeed Insights, the provided field data will be aggregated over the entire website. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Once the assessment begins, CrUX data will be available instantly as Lighthouse (a &lt;a href=&quot;https://web.dev/lab-and-field-data-differences/#lab-data&quot;&gt;lab tool&lt;/a&gt;) runs.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of INP field data in a distribution. The distribution aligns with the INP thresholds, and in this example, the field INP value is 545 milliseconds, which lands in the poor range.&quot; decoding=&quot;async&quot; height=&quot;161&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/JZjcvOGERDsYovSJVn7o.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/JZjcvOGERDsYovSJVn7o.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/JZjcvOGERDsYovSJVn7o.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/JZjcvOGERDsYovSJVn7o.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/JZjcvOGERDsYovSJVn7o.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/JZjcvOGERDsYovSJVn7o.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/JZjcvOGERDsYovSJVn7o.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/JZjcvOGERDsYovSJVn7o.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/JZjcvOGERDsYovSJVn7o.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/JZjcvOGERDsYovSJVn7o.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/JZjcvOGERDsYovSJVn7o.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/JZjcvOGERDsYovSJVn7o.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/JZjcvOGERDsYovSJVn7o.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/JZjcvOGERDsYovSJVn7o.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/JZjcvOGERDsYovSJVn7o.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/JZjcvOGERDsYovSJVn7o.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/JZjcvOGERDsYovSJVn7o.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/JZjcvOGERDsYovSJVn7o.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;The distribution of INP experiences as seen in PageSpeed Insights.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;When Lighthouse has finished running its assessment, PSI will populate the assessment with Lighthouse audits.&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; Because PageSpeed Insights is not scripted to interact with the page, there will be no audits relevant to INP. However, &lt;a href=&quot;https://web.dev/tbt/&quot;&gt;Total Blocking Time (TBT)&lt;/a&gt; is &lt;a href=&quot;https://almanac.httparchive.org/en/2022/performance#inp-and-tbt&quot;&gt;highly correlated with INP&lt;/a&gt;. Therefore, you can filter audits by TBT to find opportunities to reduce the amount of time a page blocks the main thread during the test. &lt;/div&gt;&lt;/aside&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of Lighthouse audits as seen in PageSpeed Insights. The audits are filtered by the TBT metric, showing tips for minimizing main thread work, avoiding an excessive DOM size, and avoiding long main thread tasks.&quot; decoding=&quot;async&quot; height=&quot;346&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Ra3g6IEXkYsgMhpQsjEV.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Ra3g6IEXkYsgMhpQsjEV.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Ra3g6IEXkYsgMhpQsjEV.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Ra3g6IEXkYsgMhpQsjEV.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Ra3g6IEXkYsgMhpQsjEV.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Ra3g6IEXkYsgMhpQsjEV.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Ra3g6IEXkYsgMhpQsjEV.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Ra3g6IEXkYsgMhpQsjEV.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Ra3g6IEXkYsgMhpQsjEV.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Ra3g6IEXkYsgMhpQsjEV.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Ra3g6IEXkYsgMhpQsjEV.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Ra3g6IEXkYsgMhpQsjEV.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Ra3g6IEXkYsgMhpQsjEV.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Ra3g6IEXkYsgMhpQsjEV.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Ra3g6IEXkYsgMhpQsjEV.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Ra3g6IEXkYsgMhpQsjEV.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Ra3g6IEXkYsgMhpQsjEV.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Ra3g6IEXkYsgMhpQsjEV.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Audits for Total Blocking Time&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;what-if-i-dont-have-field-data&quot;&gt;What if I don&#39;t have field data? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/find-slow-interactions-in-the-field/#what-if-i-dont-have-field-data&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You might be in a situation where you don&#39;t have or can’t even collect field data. If this describes your situation, then you&#39;ll be entirely dependent on lab tools in order to find slow interactions. For more information on lab testing, read &lt;a href=&quot;https://web.dev/diagnose-slow-interactions-in-the-lab/&quot;&gt;How to diagnose what&#39;s causing poor INP in the lab&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/find-slow-interactions-in-the-field/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Field data is the best source of information you can draw on when it comes to understanding which interactions are problematic for actual users in the field. By drawing on information available in PageSpeed Insights, or relying on field data collection tools such as the &lt;code&gt;web-vitals&lt;/code&gt; JavaScript library (or your RUM provider), you can be more confident about which interactions are most problematic, and then move on to &lt;a href=&quot;https://web.dev/diagnose-slow-interactions-in-the-lab/&quot;&gt;reproducing problematic interactions in the lab&lt;/a&gt; and then go about fixing them.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hero image from &lt;a href=&quot;https://unsplash.com/&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;, by &lt;a href=&quot;https://unsplash.com/@federicorespini&quot; rel=&quot;noopener&quot;&gt;Federico Respini&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Optimize input delay</title>
    <link href="https://web.dev/optimize-input-delay/"/>
    <updated>2023-05-09T00:00:00Z</updated>
    <id>https://web.dev/optimize-input-delay/</id>
    <content type="html" mode="escaped">&lt;p&gt;Interactions on the web are complicated things, with all sorts of activity occurring in the browser to drive them. What they all have in common, though, is that they incur some input delay before their event callbacks begin to run. In this guide, you&#39;ll learn what input delay is, and what you can do to minimize it so your website&#39;s interactions run faster.&lt;/p&gt;
&lt;h2 id=&quot;what-is-input-delay&quot;&gt;What is input delay? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-input-delay/#what-is-input-delay&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Input delay&lt;/em&gt; is the period of time beginning from when the user first interacts with a page—such as tapping on a screen, clicking with a mouse, or pressing a key—up to when the event callbacks for the interaction begin to run. Every interaction begins with some amount of input delay.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A simplified visualization of input delay. At left, there is line art of a mouse cursor with a starburst behind it, signifying the start of an interaction. To the right is line art of a cogwheel, signifying when the event handlers for an interaction begin to run. The space in between is noted as the input delay with a curly brace.&quot; decoding=&quot;async&quot; height=&quot;303&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 736px) 736px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WVS8po7gV1gKbeWcVadI.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WVS8po7gV1gKbeWcVadI.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WVS8po7gV1gKbeWcVadI.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WVS8po7gV1gKbeWcVadI.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WVS8po7gV1gKbeWcVadI.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WVS8po7gV1gKbeWcVadI.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WVS8po7gV1gKbeWcVadI.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WVS8po7gV1gKbeWcVadI.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WVS8po7gV1gKbeWcVadI.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WVS8po7gV1gKbeWcVadI.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WVS8po7gV1gKbeWcVadI.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WVS8po7gV1gKbeWcVadI.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WVS8po7gV1gKbeWcVadI.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WVS8po7gV1gKbeWcVadI.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WVS8po7gV1gKbeWcVadI.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WVS8po7gV1gKbeWcVadI.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WVS8po7gV1gKbeWcVadI.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WVS8po7gV1gKbeWcVadI.png?auto=format&amp;w=1472 1472w&quot; width=&quot;736&quot; /&gt;
  &lt;figcaption&gt;
    The mechanics behind input delay. When an input is received by the operating system, it must be passed to the browser before the interaction starts. This takes a certain amount of time, and can be increased by existing main thread work.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Some portion of the input delay is unavoidable: it always takes some amount of time for the operating system to recognize an input event and pass it to the browser. However, that portion of the input delay is often not even noticeable, and there are other things that happen on the page itself that can make input delays long enough to cause problems.&lt;/p&gt;
&lt;h2 id=&quot;how-to-think-about-input-delay&quot;&gt;How to think about input delay &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-input-delay/#how-to-think-about-input-delay&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Generally speaking, you want to keep every part of an interaction as short as possible so that your website has the best chance of meeting &lt;a href=&quot;https://web.dev/inp/#what-is-a-good-inp-score&quot;&gt;the Interaction to Next Paint (INP) metric&#39;s &amp;quot;good&amp;quot; threshold&lt;/a&gt;, regardless of the user&#39;s device. Keeping input delay in check is just one part of meeting that threshold.&lt;/p&gt;
&lt;p&gt;You might be tempted to look to &lt;a href=&quot;https://web.dev/fid/#what-is-a-good-fid-score&quot;&gt;the First Input Delay (FID) thresholds&lt;/a&gt; to determine an allowance for input delays—but the &amp;quot;good&amp;quot; threshold for FID is &lt;strong&gt;100 milliseconds or less&lt;/strong&gt;. If you go by this threshold, you&#39;d be allotting half of your budget for INP to input delay alone. This is inadvisable when you consider that an interaction also requires time to run event callbacks and for the browser to paint the next frame.&lt;/p&gt;
&lt;p&gt;To meet INP&#39;s &amp;quot;good&amp;quot; threshold, you&#39;ll want to aim for the shortest input delay possible, but you shouldn&#39;t expect to eliminate it entirely, as that is impossible. As long as you&#39;re avoiding excessive main thread work while users are attempting to interact with your page, your input delay should be low enough to avoid problems.&lt;/p&gt;
&lt;h2 id=&quot;how-to-minimize-input-delay&quot;&gt;How to minimize input delay &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-input-delay/#how-to-minimize-input-delay&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As said previously, some input delay is unavoidable, but on the other hand, some input delay &lt;em&gt;is&lt;/em&gt; avoidable. Here are some things to consider if you&#39;re struggling with long input delays.&lt;/p&gt;
&lt;h3 id=&quot;avoid-recurring-timers-that-kick-off-excessive-main-thread-work&quot;&gt;Avoid recurring timers that kick off excessive main thread work &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-input-delay/#avoid-recurring-timers-that-kick-off-excessive-main-thread-work&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are two commonly used timer functions in JavaScript that can contribute to input delay: &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/setTimeout&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;setTimeout&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/setInterval&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;setInterval&lt;/code&gt;&lt;/a&gt;. The difference between the two is that &lt;code&gt;setTimeout&lt;/code&gt; schedules a callback to run after a specified time. &lt;code&gt;setInterval&lt;/code&gt;, on the other hand, schedules a callback to run every &lt;em&gt;n&lt;/em&gt; milliseconds in perpetuity, or until the timer is stopped with &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/clearInterval&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;clearInterval&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;setTimeout&lt;/code&gt; is not problematic in itself—in fact, it can be helpful in &lt;a href=&quot;https://web.dev/optimize-input-delay/#avoid-long-tasks&quot;&gt;avoiding long tasks&lt;/a&gt;. However, it depends on &lt;em&gt;when&lt;/em&gt; the timeout occurs, and whether the user attempts to interact with the page when the timeout callback runs.&lt;/p&gt;
&lt;p&gt;Additionally, &lt;code&gt;setTimeout&lt;/code&gt; can be run in a loop or recursively, where it acts more like &lt;code&gt;setInterval&lt;/code&gt;, though preferably not scheduling the next iteration until the previous one is completed. While this means the loop will yield to the main thread every time &lt;code&gt;setTimeout&lt;/code&gt; is called, you should take care to ensure its callback doesn&#39;t end up doing excessive work.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;setInterval&lt;/code&gt; runs a callback on an interval, and is therefore much more likely to get in the way of interactions. This is because—unlike a single instance of a &lt;code&gt;setTimeout&lt;/code&gt; call, which is a one-off callback that &lt;em&gt;may&lt;/em&gt; get in the way of a user interaction—&lt;code&gt;setInterval&lt;/code&gt;&#39;s recurring nature makes it much more likely that it &lt;em&gt;will&lt;/em&gt; get in the way of an interaction, thus increasing the interaction&#39;s input delay.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of the performance profiler in Chrome DevTools demonstrating input delay. A task fired by a timer function occurs just before a user initiates a click interaction. However, the timer extends the input delay, causing the interaction&amp;#x27;s event callbacks to run later than they otherwise would.&quot; decoding=&quot;async&quot; height=&quot;291&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/tjzVtS9wFXxd06gaPHNY.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/tjzVtS9wFXxd06gaPHNY.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/tjzVtS9wFXxd06gaPHNY.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/tjzVtS9wFXxd06gaPHNY.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/tjzVtS9wFXxd06gaPHNY.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/tjzVtS9wFXxd06gaPHNY.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/tjzVtS9wFXxd06gaPHNY.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/tjzVtS9wFXxd06gaPHNY.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/tjzVtS9wFXxd06gaPHNY.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/tjzVtS9wFXxd06gaPHNY.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/tjzVtS9wFXxd06gaPHNY.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/tjzVtS9wFXxd06gaPHNY.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/tjzVtS9wFXxd06gaPHNY.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/tjzVtS9wFXxd06gaPHNY.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/tjzVtS9wFXxd06gaPHNY.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/tjzVtS9wFXxd06gaPHNY.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/tjzVtS9wFXxd06gaPHNY.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/tjzVtS9wFXxd06gaPHNY.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    A timer registered by a previous &lt;code&gt;setInterval&lt;/code&gt; call contributing to input delay as depicted in the performance panel of Chrome DevTools. The added input delay causes the event callbacks for the interaction to run later than they otherwise could.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If timers are occurring in first-party code, then you have control over them. Evaluate whether you need them, or do your best to reduce the work in them as much as possible. However, timers in third-party scripts are a different story. You often don&#39;t have control over what a third-party script does, and fixing performance issues in third-party code often involves working with stakeholders to determine whether a given third-party script is necessary, and if so, establish contact with a third-party script vendor to determine what can done to fix performance issues they may cause on your website.&lt;/p&gt;
&lt;h3 id=&quot;avoid-long-tasks&quot;&gt;Avoid long tasks &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-input-delay/#avoid-long-tasks&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One way to mitigate long input delays is to avoid long tasks. When you have excessive main thread work that blocks the main thread during interactions, that will add to the input delay for them before the long tasks have had a chance to finish.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A visualization of how long tasks extend input delay. At top, an interaction occurs shortly after a single long task runs, causing significant input delay that causes event callbacks to run much later than they should. At bottom, an interaction occurs at roughly the same time, but the long task is broken up into several smaller ones by yielding, allowing the interaction&amp;#x27;s event callbacks to run much sooner.&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/jL3OLOhcWUQDnR4XjewLBx4e3PC3/PRO5n8DxflhLaM5PdoZv.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/PRO5n8DxflhLaM5PdoZv.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/PRO5n8DxflhLaM5PdoZv.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/PRO5n8DxflhLaM5PdoZv.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/PRO5n8DxflhLaM5PdoZv.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/PRO5n8DxflhLaM5PdoZv.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/PRO5n8DxflhLaM5PdoZv.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/PRO5n8DxflhLaM5PdoZv.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/PRO5n8DxflhLaM5PdoZv.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/PRO5n8DxflhLaM5PdoZv.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/PRO5n8DxflhLaM5PdoZv.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/PRO5n8DxflhLaM5PdoZv.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/PRO5n8DxflhLaM5PdoZv.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/PRO5n8DxflhLaM5PdoZv.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/PRO5n8DxflhLaM5PdoZv.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/PRO5n8DxflhLaM5PdoZv.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/PRO5n8DxflhLaM5PdoZv.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/PRO5n8DxflhLaM5PdoZv.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    A visualization of what happens to interactions when tasks are too long and the browser can&#39;t respond quickly enough to interactions, versus when longer tasks are broken up into smaller tasks.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Besides minimizing the amount of work you do in a task—and you should &lt;em&gt;always&lt;/em&gt; strive to do as little work as possible on the main thread—you can improve responsiveness to user input by &lt;a href=&quot;https://web.dev/optimize-long-tasks/&quot;&gt;breaking up long tasks&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; Yielding isn&#39;t foolproof, as tasks from third-party JavaScript can still make their way in between first-party tasks after yielding. This can still contribute to input delay for future interactions, and you&#39;ll need to be mindful of third-party tasks that may run on an interval, as described in the previous section. This is a problem that &lt;a href=&quot;https://github.com/WICG/scheduling-apis/blob/main/explainers/yield-and-continuation.md&quot;&gt;the proposed &lt;code&gt;scheduler.yield&lt;/code&gt; API&lt;/a&gt; is looking to solve. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;be-mindful-of-interaction-overlap&quot;&gt;Be mindful of interaction overlap &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-input-delay/#be-mindful-of-interaction-overlap&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A particularly challenging part of optimizing INP can be if you have interactions that overlap. Interaction overlap means that after you&#39;ve interacted with one element, you make another interaction with the page before the initial interaction has had a chance to render the next frame.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A depiction of when tasks can overlap to produce long input delays. In this depiction, a click interaction overlaps with a keydown interaction to increase the input delay for the keydown interaction.&quot; decoding=&quot;async&quot; height=&quot;307&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ohYy7phsKRbKCeth0iUu.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ohYy7phsKRbKCeth0iUu.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ohYy7phsKRbKCeth0iUu.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ohYy7phsKRbKCeth0iUu.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ohYy7phsKRbKCeth0iUu.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ohYy7phsKRbKCeth0iUu.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ohYy7phsKRbKCeth0iUu.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ohYy7phsKRbKCeth0iUu.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ohYy7phsKRbKCeth0iUu.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ohYy7phsKRbKCeth0iUu.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ohYy7phsKRbKCeth0iUu.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ohYy7phsKRbKCeth0iUu.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ohYy7phsKRbKCeth0iUu.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ohYy7phsKRbKCeth0iUu.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ohYy7phsKRbKCeth0iUu.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ohYy7phsKRbKCeth0iUu.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ohYy7phsKRbKCeth0iUu.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ohYy7phsKRbKCeth0iUu.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    A visualization of two concurrent interactions in the performance profiler in Chrome&#39;s DevTools. The rendering work in the initial click interaction causes an input delay for the subsequent keyboard interaction.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Sources of interaction overlap may be as simple as users making many interactions in a short period of time. This can occur when users type in form fields, where many keyboard interactions can occur in a very short period of time. If the work on a key event is especially expensive—such as in the common case of autocomplete fields where network requests are made to a back end—you have a couple of options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Consider &lt;a href=&quot;https://web.dev/debounce-your-input-handlers/&quot;&gt;debouncing&lt;/a&gt; inputs to limit the amount of times an event callback executes in a given period of time.&lt;/li&gt;
&lt;li&gt;Use &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/AbortController/abort&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;AbortController&lt;/code&gt;&lt;/a&gt; to cancel outgoing &lt;code&gt;fetch&lt;/code&gt; requests so the main thread doesn&#39;t become congested handling &lt;code&gt;fetch&lt;/code&gt; callbacks. Note: an &lt;code&gt;AbortController&lt;/code&gt; instance&#39;s &lt;code&gt;signal&lt;/code&gt; property can also be used to &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_event&quot; rel=&quot;noopener&quot;&gt;abort events&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Another source of increased input delay due to overlapping interactions can be expensive animations. In particular, animations in JavaScript may fire many &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; calls, which may get in the way of user interactions. To get around this, use CSS animations whenever possible to avoid queueing potentially expensive animation frames—but if you do this, make sure you &lt;a href=&quot;https://developer.chrome.com/en/docs/lighthouse/performance/non-composited-animations/&quot; rel=&quot;noopener&quot;&gt;avoid non-composited animations&lt;/a&gt; so that animations run mainly on the GPU and compositor threads, and not on the main thread.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-input-delay/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While input delays may not represent the majority of the time your interactions take to run, it&#39;s important to understand that every part of an interaction takes up an amount of time that you can reduce. If you&#39;re &lt;a href=&quot;https://web.dev/diagnose-slow-interactions-in-the-lab/#how-to-identify-long-input-delays&quot;&gt;observing long input delay&lt;/a&gt;, then you have opportunities to reduce it. Avoiding recurring timer callbacks, breaking up long tasks, and being aware of potential interaction overlap can all help you to reduce input delay, leading to faster interactivity for your website&#39;s users.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hero image from &lt;a href=&quot;https://unsplash.com/&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;, by &lt;a href=&quot;https://unsplash.com/@introspectivedsgn&quot; rel=&quot;noopener&quot;&gt;Erik Mclean&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Script evaluation and long tasks</title>
    <link href="https://web.dev/script-evaluation-and-long-tasks/"/>
    <updated>2023-05-09T00:00:00Z</updated>
    <id>https://web.dev/script-evaluation-and-long-tasks/</id>
    <content type="html" mode="escaped">&lt;p&gt;When it comes to optimizing &lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt;, most of the advice you&#39;ll encounter is to optimize interactions themselves. For example, in the &lt;a href=&quot;https://web.dev/optimize-long-tasks/&quot;&gt;optimize long tasks guide&lt;/a&gt;, techniques such as yielding with &lt;code&gt;setTimeout&lt;/code&gt;, &lt;code&gt;isInputPending&lt;/code&gt;, and so forth are discussed. These techniques are beneficial, as they allow the main thread some breathing room by avoiding long tasks, which can allow more opportunities for interactions and other activity to run sooner, rather than if they had to wait for a single long task.&lt;/p&gt;
&lt;p&gt;However, what about the long tasks that come from loading scripts themselves? These tasks can interfere with user interactions and affect a page&#39;s INP during load. This guide will explore how browsers handle tasks kicked off by script evaluation, and look into what you may be able to do to break up script evaluation work so that your main thread can be more responsive to user input while the page is loading.&lt;/p&gt;
&lt;h2 id=&quot;what-is-script-evaluation&quot;&gt;What is script evaluation? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/script-evaluation-and-long-tasks/#what-is-script-evaluation&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you&#39;ve profiled an application that ships a lot of JavaScript, you may have seen long tasks where the culprit is labeled &lt;strong&gt;Evaluate Script&lt;/strong&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Script evaluation work as visualized in the performance profiler of Chrome DevTools. The work causes a long task during startup, which blocks the main thread&amp;#x27;s ability to respond to user interactions.&quot; decoding=&quot;async&quot; height=&quot;107&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 697px) 697px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WZyF71IiPWyG8vwKQcdP.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WZyF71IiPWyG8vwKQcdP.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WZyF71IiPWyG8vwKQcdP.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WZyF71IiPWyG8vwKQcdP.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WZyF71IiPWyG8vwKQcdP.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WZyF71IiPWyG8vwKQcdP.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WZyF71IiPWyG8vwKQcdP.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WZyF71IiPWyG8vwKQcdP.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WZyF71IiPWyG8vwKQcdP.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WZyF71IiPWyG8vwKQcdP.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WZyF71IiPWyG8vwKQcdP.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WZyF71IiPWyG8vwKQcdP.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WZyF71IiPWyG8vwKQcdP.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WZyF71IiPWyG8vwKQcdP.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WZyF71IiPWyG8vwKQcdP.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WZyF71IiPWyG8vwKQcdP.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WZyF71IiPWyG8vwKQcdP.png?auto=format&amp;w=1394 1394w&quot; width=&quot;697&quot; /&gt;
  &lt;figcaption&gt;
    Script evaluation work as shown in the performance profiler in Chrome DevTools. In this case, the work is enough to cause a long task that blocks the main thread from taking on other work—including tasks that drive user interactions.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Script evaluation is a necessary part of executing JavaScript in the browser, as JavaScript is compiled &lt;a href=&quot;https://en.wikipedia.org/wiki/Just-in-time_compilation&quot; rel=&quot;noopener&quot;&gt;just-in-time before execution&lt;/a&gt;. When a script is evaluated, it is first parsed for errors. If the parser doesn&#39;t find errors, the script is then compiled into &lt;a href=&quot;https://en.wikipedia.org/wiki/Bytecode&quot; rel=&quot;noopener&quot;&gt;bytecode&lt;/a&gt;, and then can continue onto execution.&lt;/p&gt;
&lt;p&gt;Though necessary, script evaluation can be problematic, as users may try to interact with a page shortly after it initially renders. However, just because a page has &lt;em&gt;rendered&lt;/em&gt; doesn&#39;t mean that the page has finished &lt;em&gt;loading&lt;/em&gt;. Interactions that take place during load can be delayed because the page is busy evaluating scripts. While there&#39;s no guarantee that the desired interaction can take place at this point in time—as a script responsible for it may not have loaded yet—there could be interactions dependent on JavaScript that &lt;em&gt;are&lt;/em&gt; ready, or the interactivity doesn&#39;t depend on JavaScript at all.&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; One metric that might give you insight into whether you have excessive script evaluation occurring during page load is &lt;a href=&quot;https://web.dev/tbt/&quot;&gt;Total Blocking Time (TBT)&lt;/a&gt;, as it is a &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/#types-of-metrics&quot;&gt;load responsiveness&lt;/a&gt; metric. &lt;a href=&quot;https://almanac.httparchive.org/en/2022/performance#inp-and-tbt&quot;&gt;Because TBT correlates well with INP&lt;/a&gt;, a page with a high TBT is a reasonable indicator that there may be high INP values during load that may be tied to script evaluation work. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;the-relationship-between-scripts-and-the-tasks-that-evaluate-them&quot;&gt;The relationship between scripts and the tasks that evaluate them &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/script-evaluation-and-long-tasks/#the-relationship-between-scripts-and-the-tasks-that-evaluate-them&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;How tasks responsible for script evaluation are kicked off depends on whether the script you&#39;re loading is loaded via a regular &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; element, or if a script is a module loaded with the &lt;code&gt;type=module&lt;/code&gt;. Since browsers have the tendency to handle things differently, how the major browser engines handle script evaluation will be touched upon where script evaluation behaviors across them vary.&lt;/p&gt;
&lt;h3 id=&quot;loading-scripts-with-the-lessscriptgreater-element&quot;&gt;Loading scripts with the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; element &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/script-evaluation-and-long-tasks/#loading-scripts-with-the-lessscriptgreater-element&quot;&gt;#&lt;/a&gt;&lt;/h3&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; This section deals with loading scripts using the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; element &lt;em&gt;without&lt;/em&gt; the &lt;code&gt;type=module&lt;/code&gt; attribute. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The number of tasks dispatched to evaluate scripts generally has a direct relationship with the number of &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; elements on a page. Each &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; element kicks off a task to evaluate the requested script so it can be parsed, compiled, and executed. &lt;strong&gt;This is the case for Chromium-based browsers, Safari, &lt;em&gt;and&lt;/em&gt; Firefox.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Why does this matter? Let&#39;s say you&#39;re using a bundler to manage your production scripts, and you&#39;ve configured it to bundle everything your page needs to run into a single script. If this is the case for your website, you can expect that there will be a single task dispatched to evaluate that script. Is this a bad thing? Not necessarily—unless that script is &lt;em&gt;huge&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;You can break up script evaluation work by avoiding loading large chunks of JavaScript, and load more individual, smaller scripts using additional &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; elements.&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; As devices vary in their capability, it&#39;s very difficult to define a set limit for how large individual scripts should be. To achieve a decent balance between compression efficiency, download time, and script evaluation time, a limit of 100 kilobytes per individual script is a good target. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;While you should always strive to load as little JavaScript as possible during page load, splitting up your scripts ensures that, instead of one large task that may block the main thread, you have a greater number of smaller tasks that won&#39;t block the main thread at all—or at least less than what you started with.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Multiple tasks involving script evaluation as visualized in the performance profiler of Chrome DevTools. Because multiple smaller scripts are loaded instead of fewer larger scripts, tasks are less likely to become long tasks, allowing the main thread to respond to user input more quickly.&quot; decoding=&quot;async&quot; height=&quot;174&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/dajGS6urKufQoweVhJQh.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/dajGS6urKufQoweVhJQh.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/dajGS6urKufQoweVhJQh.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/dajGS6urKufQoweVhJQh.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/dajGS6urKufQoweVhJQh.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/dajGS6urKufQoweVhJQh.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/dajGS6urKufQoweVhJQh.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/dajGS6urKufQoweVhJQh.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/dajGS6urKufQoweVhJQh.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/dajGS6urKufQoweVhJQh.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/dajGS6urKufQoweVhJQh.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/dajGS6urKufQoweVhJQh.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/dajGS6urKufQoweVhJQh.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/dajGS6urKufQoweVhJQh.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/dajGS6urKufQoweVhJQh.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/dajGS6urKufQoweVhJQh.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/dajGS6urKufQoweVhJQh.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/dajGS6urKufQoweVhJQh.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Multiple tasks spawned to evaluate scripts as a result of multiple &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; elements present in the page&#39;s HTML. This is preferable to sending one large script bundle to users, which is more likely to block the main thread.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You can think of breaking up tasks for script evaluation as being somewhat similar to &lt;a href=&quot;https://web.dev/optimize-long-tasks/#use-asyncawait-to-create-yield-points&quot;&gt;yielding during event callbacks that run during an interaction&lt;/a&gt;. However, with script evaluation, the yielding mechanism breaks up the JavaScript you load into multiple smaller scripts, rather than a smaller number of larger scripts than are more likely to block the main thread.&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; Currently, Chromium-based browsers will execute all loaded scripts with the &lt;code&gt;defer&lt;/code&gt; attribute in the same task as the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Window/DOMContentLoaded_event&quot;&gt;&lt;code&gt;DOMContentLoaded&lt;/code&gt; event&lt;/a&gt;. While this minimizes overall layout work, the cost is the increased likelihood of a longer task, which may cause other performance problems. A potential solution is &lt;a href=&quot;https://github.com/whatwg/html/issues/6230&quot;&gt;currently being explored&lt;/a&gt;. This behavior also takes place with scripts loaded using the &lt;code&gt;type=module&lt;/code&gt; attribute, as such scripts are deferred by default. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;loading-scripts-with-the-lessscriptgreater-element-and-the-type=module-attribute&quot;&gt;Loading scripts with the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; element and the &lt;code&gt;type=module&lt;/code&gt; attribute &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/script-evaluation-and-long-tasks/#loading-scripts-with-the-lessscriptgreater-element-and-the-type=module-attribute&quot;&gt;#&lt;/a&gt;&lt;/h3&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; If you&#39;re not bundling ES modules and loading them using the &lt;code&gt;type=module&lt;/code&gt; attribute, you could actually end up slowing down page startup. For more information, read the &lt;a href=&quot;https://web.dev/script-evaluation-and-long-tasks/#trade-offs-and-considerations&quot;&gt;trade-offs and considerations section&lt;/a&gt; later on in this guide. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;It&#39;s now possible to load ES modules natively in the browser with the &lt;a href=&quot;https://web.dev/serve-modern-code-to-modern-browsers/#use-lessscript-type=modulegreater&quot;&gt;&lt;code&gt;type=module&lt;/code&gt; attribute&lt;/a&gt; on the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; element. This approach to script loading carries some developer experience benefits, such as not having to transform code for production use—especially when used in combination with &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/script/type/importmap&quot; rel=&quot;noopener&quot;&gt;import maps&lt;/a&gt;. However, loading scripts in this way schedules tasks that differ from browser to browser.&lt;/p&gt;
&lt;h4 id=&quot;chromium-based-browsers&quot;&gt;Chromium-based browsers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/script-evaluation-and-long-tasks/#chromium-based-browsers&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;In browsers such as Chrome—or those derived from it—loading ES modules using the &lt;code&gt;type=module&lt;/code&gt; attribute produces different sorts of tasks than you&#39;d normally see when not using &lt;code&gt;type=module&lt;/code&gt;. For example, a task for each module script will run that involves activity labeled as &lt;strong&gt;Compile module&lt;/strong&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Module compilation work in multiple tasks as visualized in Chrome DevTools.&quot; decoding=&quot;async&quot; height=&quot;140&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aEGTjOqtruKet5I6sCLM.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aEGTjOqtruKet5I6sCLM.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aEGTjOqtruKet5I6sCLM.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aEGTjOqtruKet5I6sCLM.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aEGTjOqtruKet5I6sCLM.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aEGTjOqtruKet5I6sCLM.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aEGTjOqtruKet5I6sCLM.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aEGTjOqtruKet5I6sCLM.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aEGTjOqtruKet5I6sCLM.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aEGTjOqtruKet5I6sCLM.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aEGTjOqtruKet5I6sCLM.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aEGTjOqtruKet5I6sCLM.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aEGTjOqtruKet5I6sCLM.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aEGTjOqtruKet5I6sCLM.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aEGTjOqtruKet5I6sCLM.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aEGTjOqtruKet5I6sCLM.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aEGTjOqtruKet5I6sCLM.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/aEGTjOqtruKet5I6sCLM.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Module loading behavior in Chromium-based browsers. Each module script will spawn a &lt;strong&gt;Compile module&lt;/strong&gt; call to compile their contents prior to evaluation.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Once the modules have compiled, any code that subsequently runs in them will kick off activity labeled as &lt;strong&gt;Evaluate module&lt;/strong&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Just-in-time evaluation of a module as visualized in the performance panel of Chrome DevTools.&quot; decoding=&quot;async&quot; height=&quot;184&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/CQpGnJmXh9JSUIMuBpeu.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/CQpGnJmXh9JSUIMuBpeu.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/CQpGnJmXh9JSUIMuBpeu.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/CQpGnJmXh9JSUIMuBpeu.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/CQpGnJmXh9JSUIMuBpeu.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/CQpGnJmXh9JSUIMuBpeu.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/CQpGnJmXh9JSUIMuBpeu.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/CQpGnJmXh9JSUIMuBpeu.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/CQpGnJmXh9JSUIMuBpeu.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/CQpGnJmXh9JSUIMuBpeu.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/CQpGnJmXh9JSUIMuBpeu.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/CQpGnJmXh9JSUIMuBpeu.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/CQpGnJmXh9JSUIMuBpeu.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/CQpGnJmXh9JSUIMuBpeu.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/CQpGnJmXh9JSUIMuBpeu.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/CQpGnJmXh9JSUIMuBpeu.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/CQpGnJmXh9JSUIMuBpeu.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/CQpGnJmXh9JSUIMuBpeu.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    When code in a module runs, that module will be evaluated just-in-time.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The effect here—in Chrome and related browsers, at least—is that the compilation steps are broken up when using ES modules. This is a clear win in terms of managing long tasks; however, the resulting module evaluation work that results still means you&#39;re incurring some unavoidable cost. While you should strive to ship as little JavaScript as possible, using ES modules—regardless of the browser—provides the following benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All module code is automatically run in &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Strict_mode&quot; rel=&quot;noopener&quot;&gt;strict mode&lt;/a&gt;, which allows potential optimizations by JavaScript engines that couldn&#39;t otherwise be made in a non-strict context.&lt;/li&gt;
&lt;li&gt;Scripts loaded using &lt;code&gt;type=module&lt;/code&gt; are treated as if they were &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/script#attr-defer&quot; rel=&quot;noopener&quot;&gt;deferred&lt;/a&gt; by default. It&#39;s possible to use the &lt;code&gt;async&lt;/code&gt; attribute on scripts loaded with &lt;code&gt;type=module&lt;/code&gt; to change this behavior.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;safari-and-firefox&quot;&gt;Safari and Firefox &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/script-evaluation-and-long-tasks/#safari-and-firefox&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When modules are loaded in Safari and Firefox, each of them is evaluated in a separate task. This means you could theoretically load a single top-level module consisting of only &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/import&quot; rel=&quot;noopener&quot;&gt;static &lt;code&gt;import&lt;/code&gt;&lt;/a&gt; statements to other modules, and every module loaded will incur a separate network request and task to evaluate it.&lt;/p&gt;
&lt;h3 id=&quot;loading-scripts-with-dynamic-import&quot;&gt;Loading scripts with dynamic &lt;code&gt;import()&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/script-evaluation-and-long-tasks/#loading-scripts-with-dynamic-import&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/import&quot; rel=&quot;noopener&quot;&gt;Dynamic &lt;code&gt;import()&lt;/code&gt;&lt;/a&gt; is another method for loading scripts. Unlike static &lt;code&gt;import&lt;/code&gt; statements that are required to be at the top of an ES module, a dynamic &lt;code&gt;import()&lt;/code&gt; call can appear anywhere in a script to load a chunk of JavaScript on demand. This technique is called &lt;a href=&quot;https://web.dev/reduce-javascript-payloads-with-code-splitting/&quot;&gt;code splitting&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Dynamic &lt;code&gt;import()&lt;/code&gt; has two advantages when it comes to improving INP:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Modules which are deferred to load later reduce main thread contention during startup by reducing the amount of JavaScript loaded at that time. This frees up the main thread so it can be more responsive to user interactions.&lt;/li&gt;
&lt;li&gt;When dynamic &lt;code&gt;import()&lt;/code&gt; calls are made, each call will effectively separate the compilation and evaluation of each module to its own task. Of course, a dynamic &lt;code&gt;import()&lt;/code&gt; that loads a very large module will kick off a rather large script evaluation task, and that can interfere with the ability of the main thread to respond to user input if the interaction occurs at the same time as the dynamic &lt;code&gt;import()&lt;/code&gt; call. &lt;strong&gt;Therefore, it&#39;s still very important that you load as little JavaScript as possible.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Dynamic &lt;code&gt;import()&lt;/code&gt; calls behave similarly in all major browser engines: the script evaluation tasks that result will be the same as the amount of modules that are dynamically imported.&lt;/p&gt;
&lt;h3 id=&quot;loading-scripts-in-a-web-worker&quot;&gt;Loading scripts in a web worker &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/script-evaluation-and-long-tasks/#loading-scripts-in-a-web-worker&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Web_Workers_API&quot; rel=&quot;noopener&quot;&gt;Web workers&lt;/a&gt; are a special JavaScript use case. Web workers are registered on the main thread, and the code within the worker then runs on its own thread. This is hugely beneficial in the sense that—while the code that registers the web worker runs on the main thread—the code within the web worker doesn&#39;t. This reduces main thread congestion, and can help keep the main thread more responsive to user interactions.&lt;/p&gt;
&lt;p&gt;In addition to reducing main thread work, web workers &lt;em&gt;themselves&lt;/em&gt; can load external scripts to be used in the worker context, either through &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/WorkerGlobalScope/importScripts&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;importScripts&lt;/code&gt;&lt;/a&gt; or static &lt;code&gt;import&lt;/code&gt; statements in browsers that support &lt;a href=&quot;https://web.dev/module-workers/&quot;&gt;module workers&lt;/a&gt;. The result is that any script requested by a web worker is evaluated off the main thread.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-good-bg color-state-good-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; height=&quot;24&quot; width=&quot;24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Check&quot;&gt;   &lt;path d=&quot;M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Objective&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Read to learn more:&lt;/strong&gt; &lt;a href=&quot;https://web.dev/off-main-thread/&quot;&gt;Use web workers to run JavaScript off the browser&#39;s main thread&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;trade-offs-and-considerations&quot;&gt;Trade-offs and considerations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/script-evaluation-and-long-tasks/#trade-offs-and-considerations&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While breaking up your scripts into separate, smaller files helps limit long tasks as opposed to loading fewer, much larger files, it&#39;s important to take some things into account when deciding how to break scripts up.&lt;/p&gt;
&lt;h3 id=&quot;compression-efficiency&quot;&gt;Compression efficiency &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/script-evaluation-and-long-tasks/#compression-efficiency&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/reduce-network-payloads-using-text-compression/&quot;&gt;Compression&lt;/a&gt; is a factor when it comes to breaking up scripts. When scripts are smaller, compression becomes somewhat less efficient. Larger scripts will benefit much more from compression. While increasing compression efficiency helps to keep load times for scripts as low as possible, it&#39;s a bit of a balancing act to ensure that you&#39;re breaking up scripts into enough smaller chunks to facilitate better interactivity during startup.&lt;/p&gt;
&lt;p&gt;Bundlers are ideal tools for managing the output size for the scripts your website depends on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Where webpack is concerned, its &lt;code&gt;SplitChunksPlugin&lt;/code&gt; plugin can help. Consult the &lt;a href=&quot;https://webpack.js.org/plugins/split-chunks-plugin/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;SplitChunksPlugin&lt;/code&gt; documentation&lt;/a&gt; for options you can set to help manage asset sizes.&lt;/li&gt;
&lt;li&gt;For other bundlers such as &lt;a href=&quot;https://rollupjs.org/&quot; rel=&quot;noopener&quot;&gt;Rollup&lt;/a&gt; and &lt;a href=&quot;https://esbuild.github.io/&quot; rel=&quot;noopener&quot;&gt;esbuild&lt;/a&gt;, you can manage script file sizes by using dynamic &lt;code&gt;import()&lt;/code&gt; calls in your code. These bundlers—as well as webpack—will automatically break off the dynamically imported asset into its own file, thus avoiding larger initial bundle sizes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;cache-invalidation&quot;&gt;Cache invalidation &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/script-evaluation-and-long-tasks/#cache-invalidation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Cache invalidation plays a big role in how fast a page loads on repeat visits. When you ship large, monolithic script bundles, you&#39;re at a disadvantage when it comes to browser caching. This is because when you update your first-party code—either through updating packages or shipping bug fixes—the entire bundle becomes invalidated and must be downloaded again.&lt;/p&gt;
&lt;p&gt;By breaking up your scripts, you&#39;re not just breaking up script evaluation work across smaller tasks, you&#39;re also increasing the likelihood that return visitors will grab more scripts from the browser cache instead of from the network. This translates into an overall faster page load.&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; In order for caching to be both efficient and to avoid stale resources from being served from the cache, be sure that your bundler is &lt;a href=&quot;https://bundlers.tooling.report/hashing/&quot;&gt;generating resources with a hash in the file name&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;nested-modules-and-loading-performance&quot;&gt;Nested modules and loading performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/script-evaluation-and-long-tasks/#nested-modules-and-loading-performance&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you&#39;re shipping ES modules in production and loading them with the &lt;code&gt;type=module&lt;/code&gt; attribute, you need to be aware of how module nesting can impact startup time. Module nesting refers to when an ES module statically imports another ES module that statically imports another ES module:&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;// a.js&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;b&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;./b.js&#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;// b.js&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;c&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;./c.js&#39;&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 your ES modules are not bundled together, the preceding code results in a network request chain: when &lt;code&gt;a.js&lt;/code&gt; is requested from a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; element, another network request is dispatched for &lt;code&gt;b.js&lt;/code&gt;, which then involves &lt;em&gt;another&lt;/em&gt; request for &lt;code&gt;c.js&lt;/code&gt;. One way to avoid this is to use a bundler—but be sure you&#39;re configuring your bundler to break up scripts to spread out script evaluation work.&lt;/p&gt;
&lt;p&gt;If you don&#39;t want to use a bundler, then another way to get around nested module calls is to use the &lt;a href=&quot;https://developer.chrome.com/blog/modulepreload/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;modulepreload&lt;/code&gt; resource hint&lt;/a&gt;, which will preload ES modules ahead of time to avoid network request chains. Beware, however: this hint is currently only supported in Chromium-based browsers.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/script-evaluation-and-long-tasks/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Optimizing evaluation of scripts in the browser is no doubt a tricky feat. The approach depends on your website&#39;s requirements and constraints. However, by splitting up scripts, you&#39;re spreading the work of script evaluation over numerous smaller tasks, and therefore giving the main thread the ability to handle user interactions more efficiently, rather than blocking the main thread.&lt;/p&gt;
&lt;p&gt;To recap, here are some things you can to do to break up large script evaluation tasks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When loading scripts using the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; element without the &lt;code&gt;type=module&lt;/code&gt; attribute, avoid loading scripts that are very large, as these will kick off resource-intensive script evaluation tasks that block the main thread. Spread out your scripts over more &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; elements to break up this work.&lt;/li&gt;
&lt;li&gt;Using the &lt;code&gt;type=module&lt;/code&gt; attribute to load ES modules natively in the browser will kick off individual tasks for evaluation for each separate module script.&lt;/li&gt;
&lt;li&gt;Reduce the size of your initial bundles by using dynamic &lt;code&gt;import()&lt;/code&gt; calls. This also works in bundlers, as bundlers will treat each dynamically imported module as a &amp;quot;split point,&amp;quot; resulting in a separate script being generated for each dynamically imported module.&lt;/li&gt;
&lt;li&gt;Be sure to weigh trade-offs such as compression efficiency and cache invalidation. Larger scripts will compress better, but are more likely to involve more expensive script evaluation work in fewer tasks, and result in browser cache invalidation, leading to overall lower caching efficiency.&lt;/li&gt;
&lt;li&gt;If using ES modules natively without bundling, use the &lt;code&gt;modulepreload&lt;/code&gt; resource hint to optimize the loading of them during startup.&lt;/li&gt;
&lt;li&gt;As always, ship as little JavaScript as possible.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&#39;s a balancing act for sure—but by breaking up scripts and reducing initial payloads via dynamic &lt;code&gt;import()&lt;/code&gt;, you can achieve better startup performance and better accommodate user interactions during that crucial startup period. This should help you score better on the INP metric, thus delivering a better user experience.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hero image from &lt;a href=&quot;https://unsplash.com/&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;, by &lt;a href=&quot;https://unsplash.com/@markusspiske&quot; rel=&quot;noopener&quot;&gt;Markus Spiske&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>Jeremy Wagner</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>Optimize Interaction to Next Paint</title>
    <link href="https://web.dev/optimize-inp/"/>
    <updated>2022-12-08T00:00:00Z</updated>
    <id>https://web.dev/optimize-inp/</id>
    <content type="html" mode="escaped">&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Interaction to Next Paint (INP) is a &lt;a href=&quot;https://web.dev/vitals/#pending&quot;&gt;pending&lt;/a&gt; Core Web Vital metric that will &lt;a href=&quot;https://web.dev/inp-cwv/&quot;&gt;replace First Input Delay (FID)&lt;/a&gt; in March 2024. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt; is a &lt;a href=&quot;https://web.dev/vitals/#pending&quot;&gt;pending&lt;/a&gt; Core Web Vital metric that assesses a page&#39;s overall responsiveness to user interactions by observing the latency of all &lt;a href=&quot;https://web.dev/inp/#whats-in-an-interaction&quot;&gt;qualifying interactions&lt;/a&gt; that occur throughout the lifespan of a user&#39;s visit to a page. The final INP value is the longest interaction observed (sometimes ignoring outliers).&lt;/p&gt;
&lt;p&gt;To provide a good user experience, websites should strive to have an Interaction to Next Paint of &lt;strong&gt;200 milliseconds or less&lt;/strong&gt;. 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 of page loads&lt;/strong&gt;, segmented across mobile and desktop devices.&lt;/p&gt;
&lt;style&gt;
  .inp-mobile {
    display: inline;
  }

  .inp-desktop {
    display: none;
  }

  @media screen and (min-width: 640px) {
    .inp-mobile {
      display: none;
    }

    .inp-desktop {
      display: inline;
    }
  }
&lt;/style&gt;
&lt;figure&gt;
  &lt;svg title=&quot;A diagram of the INP thresholds. An INP at or below 200 milliseconds is considered good. Between 200 and 500 milliseconds suggests a page&#39;s responsiveness needs improvement. Anything over 500 milliseconds means that a page&#39;s responsiveness is poor.&quot; class=&quot;inp-mobile&quot; version=&quot;1.1&quot; id=&quot;Layer_1&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; x=&quot;0&quot; y=&quot;0&quot; viewBox=&quot;0 0 296.6 220.2&quot; style=&quot;enable-background:new 0 0 296.6 220.2&quot; xml:space=&quot;preserve&quot;&gt;&lt;style&gt;.st0{fill:#2979FF} .st1{fill-rule:evenodd;clip-rule:evenodd;fill:#0CCE6B} .st2{fill:#191919} .st3{fill-rule:evenodd;clip-rule:evenodd;fill:#FFA400} .st4{fill-rule:evenodd;clip-rule:evenodd;fill:#FF4E42} @media screen and (prefers-color-scheme: light){.st2{fill:#191919}} [data-user-theme=light] .st2 {fill:#191919} @media screen and (prefers-color-scheme: dark){.st2{fill:#fff}} [data-user-theme=dark] .st2{fill:#fff}&lt;/style&gt;&lt;path class=&quot;st0&quot; d=&quot;M83.3 63V0h11.9v63H83.3zm26.3 0V0h13.8l25.3 42.2h.7l-.7-12.1V0h11.8v63H148l-26.8-44.6h-.7l.7 12.1V63h-11.6zm65.4 0V0h22.2c4.1 0 7.7.8 10.9 2.5s5.8 4 7.7 7 2.9 6.4 2.9 10.4c0 3.9-1 7.4-2.9 10.4s-4.5 5.4-7.7 7c-3.2 1.7-6.9 2.5-10.9 2.5h-15.6V28.6h15.9c2.1 0 3.8-.4 5.2-1.2s2.5-1.9 3.2-3.2c.7-1.3 1.1-2.8 1.1-4.3s-.4-2.9-1.1-4.2c-.7-1.3-1.8-2.3-3.2-3.2s-3.1-1.2-5.2-1.2h-10.7V63H175z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st1&quot; d=&quot;M0 137.1h96v38.4H0v-38.4z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st2&quot; d=&quot;M34 161c-.6 0-1.2-.1-1.8-.3-.5-.2-1-.5-1.4-.9-.4-.4-.7-.9-1-1.4-.2-.6-.3-1.1-.3-1.8 0-.6.1-1.2.3-1.8.2-.6.6-1 1-1.4.4-.4.9-.7 1.4-.9.5-.2 1.1-.3 1.8-.3s1.3.1 1.8.3c.6.2 1 .6 1.4 1l-.9.9c-.2-.2-.4-.4-.6-.5-.2-.1-.5-.3-.8-.3-.3-.1-.6-.1-.9-.1-.4 0-.8.1-1.2.2-.4.1-.7.4-1 .6-.3.3-.5.6-.7 1-.2.4-.2.8-.2 1.3s.1.9.2 1.3c.2.4.4.7.7 1 .3.3.6.5 1 .7.4.1.8.2 1.2.2.4 0 .8-.1 1.1-.2.3-.1.6-.3.9-.5.3-.2.5-.5.6-.8.2-.3.3-.6.3-1H34v-1.2h4.2v.7c0 .6-.1 1.2-.3 1.7-.2.5-.5.9-.9 1.3s-.8.6-1.3.8c-.5.3-1.1.4-1.7.4zm9.5 0c-.6 0-1.2-.1-1.8-.3-.5-.2-1-.5-1.4-1-.4-.4-.7-.9-.9-1.4-.2-.5-.3-1.1-.3-1.8 0-.6.1-1.2.3-1.8.2-.5.5-1 .9-1.4.4-.4.9-.7 1.4-1 .5-.2 1.1-.3 1.8-.3.6 0 1.2.1 1.8.3.5.2 1 .6 1.4 1 .4.4.7.9.9 1.4.2.5.3 1.1.3 1.8 0 .6-.1 1.2-.3 1.8-.2.5-.5 1-.9 1.4-.4.4-.9.7-1.4 1-.5.2-1.1.3-1.8.3zm0-1.2c.6 0 1.1-.1 1.6-.4.5-.3.8-.7 1.1-1.1.3-.5.4-1.1.4-1.7 0-.6-.1-1.2-.4-1.7-.3-.5-.7-.9-1.1-1.1-.5-.3-1-.4-1.6-.4-.6 0-1.1.1-1.6.4-.5.3-.8.7-1.1 1.1-.3.5-.4 1-.4 1.7 0 .6.1 1.2.4 1.7.3.5.7.9 1.1 1.1.5.3 1 .4 1.6.4zm9.9 1.2c-.6 0-1.2-.1-1.8-.3-.5-.2-1-.5-1.4-1-.4-.4-.7-.9-.9-1.4-.2-.5-.3-1.1-.3-1.8 0-.6.1-1.2.3-1.8.2-.5.5-1 .9-1.4.4-.4.9-.7 1.4-1 .5-.2 1.1-.3 1.8-.3.6 0 1.2.1 1.8.3.5.2 1 .6 1.4 1 .4.4.7.9.9 1.4.2.5.3 1.1.3 1.8 0 .6-.1 1.2-.3 1.8-.2.5-.5 1-.9 1.4-.4.4-.9.7-1.4 1-.6.2-1.2.3-1.8.3zm0-1.2c.6 0 1.1-.1 1.6-.4.5-.3.8-.7 1.1-1.1.3-.5.4-1.1.4-1.7 0-.6-.1-1.2-.4-1.7-.3-.5-.7-.9-1.1-1.1-.5-.3-1-.4-1.6-.4-.6 0-1.1.1-1.6.4-.5.3-.8.7-1.1 1.1-.3.5-.4 1-.4 1.7 0 .6.1 1.2.4 1.7.3.5.7.9 1.1 1.1.5.3 1 .4 1.6.4zm6 1.1v-8.6h2.8c.9 0 1.7.2 2.3.5.7.4 1.2.9 1.5 1.5.4.6.5 1.4.5 2.2 0 .8-.2 1.6-.5 2.2-.4.6-.9 1.2-1.5 1.5-.6.4-1.4.5-2.3.5h-2.8zm1.3-1.3h1.4c.6 0 1.2-.1 1.7-.4.5-.3.8-.6 1.1-1 .2-.5.4-1 .4-1.6 0-.6-.1-1.2-.4-1.6-.2-.5-.6-.8-1.1-1-.5-.3-1-.4-1.7-.4h-1.4v6z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st3&quot; d=&quot;M96 137.1h105.6v38.4H96v-38.4z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st2&quot; d=&quot;M130.8 153.9v-8.6h1.6l3.9 6.3h.1l-.1-1.7v-4.7h1.3v8.6h-1.4l-4.1-6.6h-.1l.1 1.7v5h-1.3zm8.7 0v-8.6h5.2v1.2h-3.9v6.1h3.9v1.2h-5.2zm.7-3.7V149h4.1v1.2h-4.1zm6.2 3.7v-8.6h5.2v1.2h-3.9v6.1h3.9v1.2h-5.2zm.7-3.7V149h4.1v1.2h-4.1zm6.1 3.7v-8.6h2.8c.9 0 1.7.2 2.3.5.7.4 1.2.9 1.5 1.5.4.6.5 1.4.5 2.2s-.2 1.6-.5 2.2c-.4.6-.9 1.2-1.5 1.5-.6.4-1.4.5-2.3.5h-2.8zm1.3-1.3h1.4c.6 0 1.2-.1 1.7-.4.5-.2.8-.6 1.1-1 .2-.5.4-1 .4-1.6 0-.6-.1-1.2-.4-1.6-.2-.5-.6-.8-1.1-1-.5-.2-1-.4-1.7-.4h-1.4v6zm9.9 1.4c-.5 0-.9-.1-1.3-.3-.4-.2-.8-.4-1.1-.8-.3-.4-.5-.8-.7-1.3l1.2-.5c.1.5.3.9.7 1.2.3.3.7.5 1.2.5.3 0 .5 0 .8-.1.2-.1.4-.2.6-.4s.2-.4.2-.7c0-.3-.1-.5-.2-.7-.1-.2-.3-.3-.6-.5s-.6-.3-1-.5l-.5-.2c-.2-.1-.5-.2-.7-.3-.2-.1-.5-.3-.7-.5-.2-.2-.4-.4-.5-.7-.1-.3-.2-.6-.2-.9 0-.4.1-.8.3-1.2.2-.4.5-.6.9-.8.4-.2.9-.3 1.4-.3.6 0 1 .1 1.4.3.4.2.7.4.9.7.2.3.4.5.4.8l-1.2.5c0-.2-.1-.3-.2-.5s-.3-.3-.5-.4c-.2-.1-.4-.2-.8-.2-.2 0-.5.1-.7.2-.2.1-.4.2-.5.4-.1.2-.2.3-.2.6s.1.6.4.8c.3.2.6.4 1.1.5l.6.2c.3.1.6.2.8.3.3.1.5.3.7.5.2.2.4.4.5.7.1.3.2.6.2 1s-.1.8-.3 1.2c-.2.3-.4.6-.7.8-.3.2-.6.3-.9.4s-.5.2-.8.2zM107 167.9v-8.6h1.3v8.6H107zm3.2 0v-8.6h1.8l2.5 6.5h.1l2.5-6.5h1.8v8.6h-1.3V163l.1-1.5h-.1l-2.5 6.4h-1l-2.5-6.4h-.1l.1 1.5v4.9h-1.4zm10.6 0v-8.6h3c.5 0 1 .1 1.4.3.4.2.8.5 1 .9.3.4.4.9.4 1.4 0 .5-.1 1-.4 1.4-.2.4-.6.7-1 .9-.4.2-.9.3-1.4.3h-2.2v-1.2h2.3c.3 0 .6-.1.8-.2.2-.1.4-.3.5-.5.1-.2.2-.4.2-.7 0-.2-.1-.4-.2-.7-.1-.2-.3-.4-.5-.5-.2-.1-.5-.2-.8-.2h-1.7v7.3h-1.4zm7.2 0v-8.6h3c.5 0 1 .1 1.4.3.4.2.7.5 1 .9.2.4.4.8.4 1.4 0 .3-.1.7-.2 1s-.3.6-.6.8c-.3.2-.6.4-1 .6-.4.1-.8.2-1.2.2h-2.1v-1.2h2.3c.3 0 .5-.1.7-.2.2-.1.4-.3.5-.5.1-.2.2-.5.2-.7 0-.2-.1-.5-.2-.7-.1-.2-.3-.4-.5-.5-.2-.1-.5-.2-.8-.2h-1.7v7.4H128zm2-4h1.5l2.7 3.9v.1h-1.5l-2.7-4zm9.3 4.1c-.6 0-1.2-.1-1.8-.3-.5-.2-1-.5-1.4-1-.4-.4-.7-.9-.9-1.4-.2-.5-.3-1.1-.3-1.8s.1-1.2.3-1.8c.2-.5.5-1 .9-1.4.4-.4.9-.7 1.4-1 .5-.2 1.1-.3 1.8-.3.6 0 1.2.1 1.8.3.5.2 1 .6 1.4 1 .4.4.7.9.9 1.4.2.5.3 1.1.3 1.8s-.1 1.2-.3 1.8c-.2.5-.5 1-.9 1.4-.4.4-.9.7-1.4 1-.6.2-1.2.3-1.8.3zm0-1.2c.6 0 1.1-.1 1.6-.4.5-.3.8-.7 1.1-1.1.3-.5.4-1.1.4-1.7 0-.6-.1-1.2-.4-1.7-.3-.5-.7-.9-1.1-1.1-.5-.3-1-.4-1.6-.4-.6 0-1.1.1-1.6.4-.5.3-.8.7-1.1 1.1-.3.5-.4 1-.4 1.7 0 .6.1 1.2.4 1.7.3.5.7.9 1.1 1.1.5.3 1 .4 1.6.4zm7.9 1.1-3-8.6h1.4l1.9 5.7.3 1h.1l.3-1 2-5.7h1.4l-3.1 8.6h-1.3zm5.6 0v-8.6h5.2v1.2h-3.9v6.1h3.9v1.2h-5.2zm.7-3.7V163h4.1v1.2h-4.1zm6.2 3.7v-8.6h1.8l2.5 6.5h.1l2.5-6.5h1.8v8.6H167V163l.1-1.5h-.1l-2.5 6.4h-1l-2.5-6.4h-.1l.1 1.5v4.9h-1.3zm10.6 0v-8.6h5.2v1.2h-3.9v6.1h3.9v1.2h-5.2zm.7-3.7V163h4.1v1.2H171zm6.1 3.7v-8.6h1.6l3.9 6.3h.1l-.1-1.7v-4.7h1.3v8.6h-1.4l-4.1-6.6h-.1l.1 1.7v5h-1.3zm10.5 0V160h1.3v7.9h-1.3zm-2.4-7.4v-1.2h6.1v1.2h-6.1z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st4&quot; d=&quot;M200.6 137.1h96v38.4h-96v-38.4z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st2&quot; d=&quot;M232.5 160.9v-8.6h3c.5 0 1 .1 1.4.3.4.2.8.5 1 .9.3.4.4.9.4 1.4 0 .5-.1 1-.4 1.4-.2.4-.6.7-1 .9-.4.2-.9.3-1.4.3h-2.2v-1.2h2.3c.3 0 .6-.1.8-.2.2-.1.4-.3.5-.5.1-.2.2-.4.2-.7 0-.2-.1-.4-.2-.7-.1-.2-.3-.4-.5-.5-.2-.1-.5-.2-.8-.2h-1.7v7.3h-1.4zm11 .1c-.6 0-1.2-.1-1.8-.3-.5-.2-1-.5-1.4-1-.4-.4-.7-.9-.9-1.4-.2-.5-.3-1.1-.3-1.8s.1-1.2.3-1.8c.2-.5.5-1 .9-1.4.4-.4.9-.7 1.4-1 .5-.2 1.1-.3 1.8-.3.6 0 1.2.1 1.8.3.5.2 1 .6 1.4 1 .4.4.7.9.9 1.4.2.5.3 1.1.3 1.8s-.1 1.2-.3 1.8c-.2.5-.5 1-.9 1.4-.4.4-.9.7-1.4 1-.5.2-1.1.3-1.8.3zm0-1.2c.6 0 1.1-.1 1.6-.4.5-.3.8-.7 1.1-1.1.3-.5.4-1.1.4-1.7 0-.6-.1-1.2-.4-1.7-.3-.5-.7-.9-1.1-1.1-.5-.3-1-.4-1.6-.4-.6 0-1.1.1-1.6.4-.5.3-.8.7-1.1 1.1-.3.5-.4 1-.4 1.7 0 .6.1 1.2.4 1.7.3.5.7.9 1.1 1.1.6.3 1.1.4 1.6.4zm9.9 1.2c-.6 0-1.2-.1-1.8-.3-.5-.2-1-.5-1.4-1-.4-.4-.7-.9-.9-1.4-.2-.5-.3-1.1-.3-1.8s.1-1.2.3-1.8c.2-.5.5-1 .9-1.4.4-.4.9-.7 1.4-1 .5-.2 1.1-.3 1.8-.3.6 0 1.2.1 1.8.3.5.2 1 .6 1.4 1 .4.4.7.9.9 1.4.2.5.3 1.1.3 1.8s-.1 1.2-.3 1.8c-.2.5-.5 1-.9 1.4-.4.4-.9.7-1.4 1-.6.2-1.1.3-1.8.3zm0-1.2c.6 0 1.1-.1 1.6-.4.5-.3.8-.7 1.1-1.1.3-.5.4-1.1.4-1.7 0-.6-.1-1.2-.4-1.7-.3-.5-.7-.9-1.1-1.1-.5-.3-1-.4-1.6-.4-.6 0-1.1.1-1.6.4-.5.3-.8.7-1.1 1.1-.3.5-.4 1-.4 1.7 0 .6.1 1.2.4 1.7.3.5.7.9 1.1 1.1.5.3 1 .4 1.6.4zm6 1.1v-8.6h3c.5 0 1 .1 1.4.3.4.2.7.5 1 .9.2.4.4.8.4 1.4 0 .3-.1.7-.2 1s-.3.6-.6.8c-.3.2-.6.4-1 .6-.4.1-.8.2-1.2.2h-2.1v-1.2h2.3c.3 0 .5-.1.7-.2.2-.1.4-.3.5-.5.1-.2.2-.5.2-.7 0-.2-.1-.5-.2-.7-.1-.2-.3-.4-.5-.5-.2-.1-.5-.2-.8-.2h-1.7v7.4h-1.2zm2-4h1.5l2.7 3.9v.1H264l-2.6-4zM178.1 219.9c-.5 0-1-.1-1.5-.3-.5-.2-1-.5-1.4-1-.4-.4-.7-1-.8-1.7l1.5-.6c.1.6.4 1 .8 1.4.4.4.9.5 1.4.5.6 0 1.1-.2 1.5-.6.4-.4.6-.9.6-1.5s-.2-1.1-.6-1.5c-.4-.4-.9-.6-1.5-.6-.4 0-.7.1-1 .2-.3.1-.5.4-.7.6l-1.7-.8.7-5.5h5.9v1.6h-4.5l-.4 2.9h.1c.2-.2.5-.3.8-.5.3-.1.7-.2 1.2-.2.6 0 1.2.2 1.7.5s1 .7 1.3 1.3c.3.5.5 1.2.5 1.9s-.2 1.3-.5 1.9c-.3.6-.8 1-1.3 1.3-.7.5-1.4.7-2.1.7zm9.9 0c-.7 0-1.3-.1-1.9-.4-.5-.3-1-.7-1.4-1.2-.4-.5-.7-1.1-.9-1.8-.2-.7-.3-1.4-.3-2.2 0-.8.1-1.6.3-2.2.2-.7.5-1.3.9-1.8s.9-.9 1.4-1.2c.6-.3 1.2-.5 1.9-.5s1.3.2 1.9.5c.6.3 1 .7 1.4 1.2.4.5.7 1.1.9 1.8.2.7.3 1.4.3 2.2 0 .8-.1 1.6-.3 2.2-.2.7-.5 1.3-.9 1.8s-.9.9-1.4 1.2c-.6.2-1.2.4-1.9.4zm.1-1.7c.6 0 1-.2 1.5-.5.4-.4.7-.9 1-1.5.2-.6.3-1.3.3-2.1s-.1-1.5-.3-2.1c-.2-.6-.5-1.1-1-1.5-.4-.4-.9-.5-1.5-.5s-1.1.2-1.5.5c-.4.4-.7.8-.9 1.5-.2.6-.3 1.3-.3 2.1s.1 1.5.3 2.1c.2.6.5 1.1.9 1.5.4.3.9.5 1.5.5zm10.5 1.7c-.7 0-1.3-.1-1.9-.4-.5-.3-1-.7-1.4-1.2-.4-.5-.7-1.1-.9-1.8-.2-.7-.3-1.4-.3-2.2 0-.8.1-1.6.3-2.2.2-.7.5-1.3.9-1.8s.9-.9 1.4-1.2c.6-.3 1.2-.5 1.9-.5s1.3.2 1.9.5c.6.3 1 .7 1.4 1.2.4.5.7 1.1.9 1.8.2.7.3 1.4.3 2.2 0 .8-.1 1.6-.3 2.2-.2.7-.5 1.3-.9 1.8s-.9.9-1.4 1.2c-.6.2-1.2.4-1.9.4zm0-1.7c.6 0 1-.2 1.5-.5.4-.4.7-.9 1-1.5.2-.6.3-1.3.3-2.1s-.1-1.5-.3-2.1c-.2-.6-.5-1.1-1-1.5-.4-.4-.9-.5-1.5-.5s-1.1.2-1.5.5c-.4.4-.7.8-.9 1.5-.2.6-.3 1.3-.3 2.1s.1 1.5.3 2.1c.2.6.5 1.1.9 1.5.4.3.9.5 1.5.5zm10 1.4v-8.2h1.6v1.1h.1c.2-.3.4-.5.6-.7.3-.2.6-.4.9-.5.3-.1.7-.2 1-.2.6 0 1.1.1 1.5.4.4.3.7.7.9 1.1.3-.4.6-.8 1.1-1.1.5-.3 1-.5 1.7-.5 1 0 1.7.3 2.1.9.5.6.7 1.4.7 2.3v5.2h-1.7v-4.9c0-.7-.1-1.1-.4-1.4-.3-.3-.7-.5-1.2-.5-.4 0-.7.1-1 .3-.3.2-.5.5-.7.9-.2.4-.2.8-.2 1.2v4.4h-1.7v-4.9c0-.7-.1-1.1-.4-1.4-.3-.3-.7-.5-1.2-.5-.4 0-.7.1-1 .3-.3.2-.5.5-.7.9-.2.4-.2.8-.2 1.2v4.4h-1.8zm17.2.3c-.6 0-1.2-.1-1.7-.3-.5-.2-.9-.5-1.2-.8-.3-.3-.5-.7-.7-1.1l1.5-.7c.2.4.5.8.8 1 .4.2.8.3 1.2.3.4 0 .8-.1 1.1-.2.3-.2.5-.4.5-.8 0-.2-.1-.4-.2-.6-.1-.1-.3-.3-.6-.4-.2-.1-.5-.2-.8-.2l-1-.2c-.4-.1-.8-.3-1.1-.5-.3-.2-.6-.5-.8-.8-.2-.3-.3-.7-.3-1.1 0-.5.1-.9.4-1.3.3-.4.7-.6 1.1-.8.5-.2 1-.3 1.6-.3.5 0 1 .1 1.4.2.4.1.8.3 1.1.6.3.3.6.6.8 1l-1.5.7c-.2-.4-.4-.6-.7-.8-.3-.1-.6-.2-1-.2s-.7.1-1 .2c-.3.2-.4.4-.4.6 0 .3.1.5.4.7.2.2.5.3.9.4l1.2.3c.8.2 1.4.5 1.8.9.4.4.6.9.6 1.5 0 .5-.2 1-.5 1.4-.3.4-.7.7-1.2.9-.6.2-1.1.4-1.7.4zM69 220v-1.6s.2-.1.4-.4c.2-.2.5-.5.8-.9l1.1-1.1 1-1c.3-.3.5-.6.7-.8.3-.3.5-.6.7-.8.2-.2.3-.5.4-.7.1-.2.1-.5.1-.8 0-.3-.1-.5-.2-.8-.1-.3-.3-.4-.6-.6-.3-.1-.6-.2-1-.2s-.7.1-1 .2c-.3.1-.5.3-.6.6-.1.2-.3.4-.3.7l-1.5-.6c.1-.3.2-.5.4-.8.2-.3.4-.5.7-.8.3-.3.6-.5 1-.6.4-.2.9-.3 1.4-.2.7 0 1.3.2 1.9.5.5.3.9.7 1.2 1.2.3.5.4 1 .4 1.6 0 .5-.1.9-.2 1.3-.2.4-.4.8-.7 1.2-.3.4-.5.7-.8 1l-.5.5c-.2.2-.4.5-.7.7l-.7.7-.6.6-.4.4h4.8v1.6H69zm13.2.2c-.7 0-1.3-.1-1.9-.4-.5-.3-1-.7-1.4-1.2-.4-.5-.7-1.1-.9-1.8-.2-.7-.3-1.4-.3-2.2 0-.8.1-1.6.3-2.2.2-.7.5-1.3.9-1.8s.9-.9 1.4-1.2c.6-.3 1.2-.5 1.9-.5s1.3.2 1.9.5c.6.3 1 .7 1.4 1.2.4.5.7 1.1.9 1.8.2.7.3 1.4.3 2.2 0 .8-.1 1.6-.3 2.2-.2.7-.5 1.3-.9 1.8s-.9.9-1.4 1.2c-.6.3-1.2.4-1.9.4zm0-1.6c.6 0 1-.2 1.5-.5.4-.4.7-.9 1-1.5.2-.6.3-1.3.3-2.1s-.1-1.5-.3-2.1c-.2-.6-.5-1.1-1-1.5-.4-.4-.9-.5-1.5-.5s-1.1.2-1.5.5c-.4.4-.7.8-.9 1.5-.2.6-.3 1.3-.3 2.1s.1 1.5.3 2.1c.2.6.5 1.1.9 1.5.4.3.9.5 1.5.5zm10.5 1.6c-.7 0-1.3-.1-1.9-.4-.5-.3-1-.7-1.4-1.2-.4-.5-.7-1.1-.9-1.8-.2-.7-.3-1.4-.3-2.2 0-.8.1-1.6.3-2.2.2-.7.5-1.3.9-1.8s.9-.9 1.4-1.2c.6-.3 1.2-.5 1.9-.5s1.3.2 1.9.5c.6.3 1 .7 1.4 1.2.4.5.7 1.1.9 1.8.2.7.3 1.4.3 2.2 0 .8-.1 1.6-.3 2.2-.2.7-.5 1.3-.9 1.8s-.9.9-1.4 1.2c-.6.3-1.2.4-1.9.4zm0-1.6c.6 0 1-.2 1.5-.5.4-.4.7-.9 1-1.5.2-.6.3-1.3.3-2.1s-.1-1.5-.3-2.1c-.2-.6-.5-1.1-1-1.5-.4-.4-.9-.5-1.5-.5s-1.1.2-1.5.5c-.4.4-.7.8-.9 1.5-.2.6-.3 1.3-.3 2.1s.1 1.5.3 2.1c.2.6.5 1.1.9 1.5.5.3 1 .5 1.5.5zm10 1.4v-8.2h1.6v1.1h.1c.2-.3.4-.5.6-.7.3-.2.6-.4.9-.5.3-.1.7-.2 1-.2.6 0 1.1.1 1.5.4.4.3.7.7.9 1.1.3-.4.6-.8 1.1-1.1.5-.3 1-.5 1.7-.5 1 0 1.7.3 2.1.9.5.6.7 1.4.7 2.3v5.2h-1.7v-4.9c0-.7-.1-1.1-.4-1.4-.3-.3-.7-.5-1.2-.5-.4 0-.7.1-1 .3-.3.2-.5.5-.7.9-.2.4-.2.8-.2 1.2v4.4H108v-4.9c0-.7-.1-1.1-.4-1.4-.3-.3-.7-.5-1.2-.5-.4 0-.7.1-1 .3-.3.2-.5.5-.7.9-.2.4-.2.8-.2 1.2v4.4h-1.8zm17.2.2c-.6 0-1.2-.1-1.7-.3-.5-.2-.9-.5-1.2-.8-.3-.3-.5-.7-.7-1.1l1.5-.7c.2.4.5.8.8 1 .4.2.8.3 1.2.3.4 0 .8-.1 1.1-.2.3-.2.5-.4.5-.8 0-.2-.1-.4-.2-.6-.1-.1-.3-.3-.6-.4-.2-.1-.5-.2-.8-.2l-1-.2c-.4-.1-.8-.3-1.1-.5-.3-.2-.6-.5-.8-.8-.2-.3-.3-.7-.3-1.1 0-.5.1-.9.4-1.3.3-.4.7-.6 1.1-.8.5-.2 1-.3 1.6-.3.5 0 1 .1 1.4.2.4.1.8.3 1.1.6.3.3.6.6.8 1l-1.5.7c-.2-.4-.4-.6-.7-.8-.3-.1-.6-.2-1-.2s-.7.1-1 .2c-.3.2-.4.4-.4.6 0 .3.1.5.4.7.2.2.5.3.9.4l1.2.3c.8.2 1.4.5 1.8.9.4.4.6.9.6 1.5 0 .5-.2 1-.5 1.4-.3.4-.7.7-1.2.9-.5.3-1.1.4-1.7.4zM42.4 96.3V82h1.7v14.3h-1.7zm4.6 0V86.1h1.6v1.5h.1c.3-.5.7-.9 1.3-1.3.6-.4 1.3-.5 2-.5 1.2 0 2.2.4 2.8 1.1.6.7 1 1.7 1 2.9v6.5h-1.7V90c0-1-.2-1.7-.7-2.1-.5-.4-1.1-.6-1.8-.6-.6 0-1.1.2-1.5.5-.4.3-.8.7-1 1.2-.2.5-.4 1-.4 1.6v5.7H47zm10-10.2h6v1.5h-6v-1.5zm1.8 7.5V83.3h1.7v10c0 .5.1.9.3 1.2s.6.4 1.1.4c.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.5-.6-.7-1.3-.7-2.2zm10.3 3c-1 0-1.9-.2-2.6-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.6-1.7-.6-2.8 0-1 .2-1.9.6-2.7s1-1.5 1.7-2 1.6-.8 2.6-.8 1.9.2 2.6.7c.7.4 1.3 1.1 1.7 1.8.4.8.6 1.7.6 2.7v.5H65V90h7c0-.3-.1-.6-.2-.9-.1-.3-.3-.6-.5-.9-.2-.3-.6-.5-.9-.7-.4-.2-.8-.3-1.4-.3-.7 0-1.2.2-1.7.5s-.8.8-1.1 1.4c-.3.6-.4 1.2-.4 2 0 .9.2 1.6.5 2.2.3.6.8 1 1.3 1.3.5.3 1.1.4 1.7.4.8 0 1.4-.2 1.8-.5.5-.4.9-.8 1.2-1.3l1.4.7c-.4.8-1 1.4-1.7 1.9-.9.6-1.8.8-2.9.8zm6.7-.3V86.1h1.6v1.6h.1c.1-.4.4-.7.7-1 .3-.3.7-.5 1.1-.7s.8-.2 1.2-.2h.7c.2 0 .3.1.5.2v1.8c-.2-.1-.5-.2-.7-.2-.2-.1-.5-.1-.7-.1-.5 0-1 .1-1.4.4-.4.3-.7.7-1 1.1-.2.5-.4 1-.4 1.5v5.7h-1.7zm10.4.3c-.8 0-1.4-.1-2-.4s-1-.7-1.3-1.2c-.3-.5-.5-1.1-.5-1.8 0-.8.2-1.4.6-1.9.4-.5.9-.9 1.5-1.2.7-.3 1.4-.4 2.2-.4.4 0 .9 0 1.2.1s.7.2 1 .3c.3.1.5.2.7.3v-.6c0-.8-.3-1.4-.8-1.8-.5-.5-1.2-.7-2-.7-.6 0-1.1.1-1.6.4-.5.2-.9.6-1.1 1l-1.3-1c.3-.4.6-.7 1-1 .4-.3.9-.5 1.4-.7.5-.2 1.1-.2 1.6-.2 1.4 0 2.5.4 3.2 1.1.8.7 1.2 1.7 1.2 3v6.5h-1.6v-1.5h-.1c-.2.3-.4.6-.7.8s-.7.5-1.1.7c-.5.2-1 .2-1.5.2zm.1-1.5c.6 0 1.1-.1 1.6-.4.5-.3.9-.7 1.2-1.2.3-.5.5-1 .5-1.6-.3-.2-.7-.4-1.2-.5s-1-.2-1.5-.2c-1 0-1.7.2-2.1.6-.4.4-.7.9-.7 1.5s.2 1 .6 1.4 1 .4 1.6.4zm11.8 1.5c-1 0-1.9-.2-2.7-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.7-1.7-.7-2.8 0-1 .2-2 .7-2.8.4-.8 1.1-1.5 1.8-1.9.8-.5 1.7-.7 2.7-.7 1.1 0 2.1.3 2.8.8.7.5 1.3 1.2 1.6 2l-1.5.6c-.2-.6-.6-1.1-1.1-1.4-.5-.3-1.1-.5-1.8-.5-.6 0-1.2.2-1.7.5s-.9.8-1.2 1.4c-.3.6-.5 1.3-.5 2 0 .8.2 1.4.5 2 .3.6.7 1 1.2 1.4.5.3 1.1.5 1.7.5.7 0 1.3-.2 1.9-.5s.9-.8 1.2-1.4l1.5.6c-.3.8-.9 1.5-1.6 2s-1.9.8-3 .8zm5.5-10.5h6v1.5h-6v-1.5zm1.7 7.5V83.3h1.7v10c0 .5.1.9.3 1.2s.6.4 1.1.4c.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8s-.7-1.3-.7-2.2zm6.5 2.7V86.1h1.7v10.2h-1.7zm.9-12c-.3 0-.6-.1-.9-.4s-.4-.5-.4-.9c0-.3.1-.6.4-.9.2-.2.5-.4.9-.4.3 0 .6.1.9.4.2.2.4.5.4.9 0 .3-.1.6-.4.9-.3.2-.6.4-.9.4zm8.1 12.3c-1 0-1.9-.2-2.7-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.7-1.7-.7-2.8 0-1 .2-1.9.7-2.8.4-.8 1.1-1.5 1.8-2 .8-.5 1.7-.7 2.7-.7 1 0 1.9.2 2.7.7.8.5 1.4 1.1 1.9 2s.7 1.7.7 2.7c0 1-.2 1.9-.7 2.8-.4.8-1.1 1.5-1.9 1.9-.8.6-1.7.8-2.7.8zm0-1.5c.6 0 1.2-.2 1.7-.5s1-.8 1.3-1.3c.3-.6.5-1.3.5-2.1s-.2-1.5-.5-2.1-.8-1-1.3-1.3c-.5-.3-1.1-.5-1.7-.5-.6 0-1.2.2-1.7.5s-1 .7-1.3 1.3-.5 1.3-.5 2.1.2 1.5.5 2.1c.3.6.8 1 1.3 1.3.5.4 1.1.5 1.7.5zm7 1.2V86.1h1.6v1.5h.1c.3-.5.7-.9 1.3-1.3.6-.4 1.3-.5 2-.5 1.2 0 2.2.4 2.8 1.1.6.7 1 1.7 1 2.9v6.5h-1.7V90c0-1-.2-1.7-.7-2.1-.5-.4-1.1-.6-1.8-.6-.6 0-1.1.2-1.5.5-.4.3-.8.7-1 1.2-.2.5-.4 1-.4 1.6v5.7h-1.7zM143 86.1h6v1.5h-6v-1.5zm1.7 7.5V83.3h1.7v10c0 .5.1.9.3 1.2s.6.4 1.1.4c.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.4-.6-.7-1.3-.7-2.2zm10.5 3c-1 0-1.9-.2-2.7-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.7-1.7-.7-2.8 0-1 .2-1.9.7-2.8.4-.8 1.1-1.5 1.8-2 .8-.5 1.7-.7 2.7-.7 1 0 1.9.2 2.7.7.8.5 1.4 1.1 1.9 2 .4.8.7 1.7.7 2.7 0 1-.2 1.9-.7 2.8-.4.8-1.1 1.5-1.9 1.9-.8.6-1.7.8-2.7.8zm0-1.5c.6 0 1.2-.2 1.7-.5s1-.8 1.3-1.3c.3-.6.5-1.3.5-2.1s-.2-1.5-.5-2.1c-.3-.6-.8-1-1.3-1.3-.5-.3-1.1-.5-1.7-.5-.6 0-1.2.2-1.7.5s-1 .7-1.3 1.3c-.3.6-.5 1.3-.5 2.1s.2 1.5.5 2.1c.3.6.8 1 1.3 1.3.5.4 1.1.5 1.7.5zm12.2 1.2V82h2.1l7.2 11.4h.1l-.1-2.8V82h1.7v14.3h-1.8l-7.5-11.9h-.1l.1 2.8v9.2h-1.7zm18.2.3c-1 0-1.9-.2-2.6-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.6-1.7-.6-2.8 0-1 .2-1.9.6-2.7.4-.8 1-1.5 1.7-2s1.6-.8 2.6-.8 1.9.2 2.6.7c.7.4 1.3 1.1 1.7 1.8.4.8.6 1.7.6 2.7v.5h-8.8V90h7c0-.3-.1-.6-.2-.9-.1-.3-.3-.6-.5-.9-.2-.3-.6-.5-.9-.7-.4-.2-.8-.3-1.4-.3-.7 0-1.2.2-1.7.5s-.8.8-1.1 1.4c-.3.6-.4 1.2-.4 2 0 .9.2 1.6.5 2.2.3.6.8 1 1.3 1.3.5.3 1.1.4 1.7.4.8 0 1.4-.2 1.8-.5.5-.4.9-.8 1.2-1.3l1.4.7c-.4.8-1 1.4-1.7 1.9-1 .6-1.9.8-3 .8zm5.5-.3 4.1-5.9h.2l2.9-4.3h2l-4 5.6h-.1l-3.1 4.6h-2zm.1-10.2h1.9l3.2 4.5h.1l4 5.7h-2l-3-4.5h-.1l-4.1-5.7zm9.9 0h6v1.5h-6v-1.5zm1.8 7.5V83.3h1.7v10c0 .5.1.9.3 1.2.2.3.6.4 1.1.4.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.4-.6-.7-1.3-.7-2.2zm11.2 2.7V82h4.8c.8 0 1.5.2 2.2.5.7.4 1.2.8 1.6 1.5.4.6.6 1.4.6 2.2 0 .8-.2 1.6-.6 2.2-.4.6-.9 1.1-1.6 1.5-.7.4-1.4.5-2.2.5H215v-1.6h4c.6 0 1-.1 1.4-.4.4-.3.7-.6.9-1 .2-.4.3-.8.3-1.2s-.1-.8-.3-1.2c-.2-.4-.5-.7-.9-1-.4-.3-.9-.4-1.4-.4h-3.2v12.7h-1.7zm14.1.3c-.8 0-1.4-.1-2-.4s-1-.7-1.3-1.2-.5-1.1-.5-1.8c0-.8.2-1.4.6-1.9s.9-.9 1.5-1.2c.7-.3 1.4-.4 2.2-.4.4 0 .9 0 1.2.1s.7.2 1 .3c.3.1.5.2.7.3v-.6c0-.8-.3-1.4-.8-1.8-.5-.5-1.2-.7-2-.7-.6 0-1.1.1-1.6.4-.5.2-.9.6-1.1 1l-1.3-1c.3-.4.6-.7 1-1 .4-.3.9-.5 1.4-.7s1.1-.2 1.6-.2c1.4 0 2.5.4 3.2 1.1.8.7 1.2 1.7 1.2 3v6.5h-1.6v-1.5h-.1c-.2.3-.4.6-.7.8s-.7.5-1.1.7c-.6.2-1 .2-1.5.2zm.1-1.5c.6 0 1.1-.1 1.6-.4.5-.3.9-.7 1.2-1.2.3-.5.5-1 .5-1.6-.3-.2-.7-.4-1.2-.5s-1-.2-1.5-.2c-1 0-1.7.2-2.1.6-.4.4-.7.9-.7 1.5s.2 1 .6 1.4 1 .4 1.6.4zm7.4 1.2V86.1h1.7v10.2h-1.7zm.8-12c-.3 0-.6-.1-.9-.4s-.4-.5-.4-.9c0-.3.1-.6.4-.9.2-.2.5-.4.9-.4.3 0 .6.1.9.4.2.2.4.5.4.9 0 .3-.1.6-.4.9-.2.2-.5.4-.9.4zm3.5 12V86.1h1.6v1.5h.1c.3-.5.7-.9 1.3-1.3.6-.4 1.3-.5 2-.5 1.2 0 2.2.4 2.8 1.1.6.7 1 1.7 1 2.9v6.5h-1.7V90c0-1-.2-1.7-.7-2.1-.5-.4-1.1-.6-1.8-.6-.6 0-1.1.2-1.5.5-.4.3-.8.7-1 1.2-.2.5-.4 1-.4 1.6v5.7H240zm10.1-10.2h6v1.5h-6v-1.5zm1.7 7.5V83.3h1.7v10c0 .5.1.9.3 1.2s.6.4 1.1.4c.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8s-.7-1.3-.7-2.2zM96 172.8c-2.4 0-4.3 1.9-4.3 4.3 0 2 1.4 3.7 3.3 4.2v20.1h2v-20.1c1.9-.5 3.3-2.1 3.3-4.2 0-2.3-1.9-4.3-4.3-4.3zM201.6 172.6c-2.4 0-4.3 1.9-4.3 4.3 0 2 1.4 3.7 3.3 4.2v19.8h2v-19.8c1.9-.5 3.3-2.1 3.3-4.2 0-2.4-1.9-4.3-4.3-4.3z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
  &lt;svg title=&quot;A diagram of the INP thresholds. An INP at or below 200 milliseconds is considered good. Between 200 and 500 milliseconds suggests a page&#39;s responsiveness needs improvement. Anything over 500 milliseconds means that a page&#39;s responsiveness is poor.&quot; class=&quot;inp-desktop&quot; version=&quot;1.1&quot; id=&quot;Layer_1&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; x=&quot;0&quot; y=&quot;0&quot; viewBox=&quot;0 0 658.4 113.6&quot; style=&quot;enable-background:new 0 0 658.4 113.6&quot; xml:space=&quot;preserve&quot;&gt;&lt;style&gt;.st0{fill: #2979FF} v.st1{fill-rule: evenodd;clip-rule: evenodd;fill: #0CCE6B} .st2 {fill: #191919} .st3{fill-rule: evenodd;clip-rule: evenodd;fill: #FFA400} .st4{fill-rule: evenodd;clip-rule: evenodd;fill: #FF4E42} @media screen and (prefers-color-scheme: light){.st2{fill: #191919}} @media screen and (prefers-color-scheme: dark){.st2{fill: #fff}} [data-user-theme=dark] .st2{fill: #fff}&lt;/style&gt;&lt;path class=&quot;st0&quot; d=&quot;M30.2 68.7V0h13v68.7h-13zm28.7 0V0H74l27.7 46.1h.8l-.8-13.2V0h12.9v68.7H101L71.7 20.1h-.8l.8 13.2v35.4H58.9zm71.3 0V0h24.2c4.4 0 8.4.9 11.9 2.7 3.5 1.8 6.3 4.4 8.4 7.6 2.1 3.3 3.1 7 3.1 11.3 0 4.3-1 8.1-3.1 11.4-2.1 3.3-4.9 5.8-8.4 7.7-3.5 1.8-7.5 2.7-11.9 2.7h-17V31.2h17.4c2.2 0 4.1-.4 5.7-1.3 1.5-.9 2.7-2.1 3.5-3.5.8-1.4 1.2-3 1.2-4.7 0-1.7-.4-3.2-1.2-4.6-.8-1.4-1.9-2.6-3.5-3.5-1.5-.9-3.4-1.3-5.7-1.3h-11.6v56.5h-13z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st1&quot; d=&quot;M303.2 14.9h115.2v43.2H303.2V14.9z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st2&quot; d=&quot;M345.3 41.5c-.7 0-1.3-.1-1.9-.4-.6-.2-1.1-.6-1.6-1s-.8-1-1.1-1.6c-.3-.6-.4-1.3-.4-2s.1-1.4.4-2c.3-.6.6-1.1 1.1-1.6.5-.5 1-.8 1.6-1 .6-.2 1.2-.4 1.9-.4s1.4.1 2 .4c.6.2 1.1.6 1.6 1.1l-1 1c-.2-.2-.4-.4-.7-.6-.3-.2-.5-.3-.9-.4-.3-.1-.6-.1-1-.1-.5 0-.9.1-1.3.3-.4.2-.8.4-1.1.7-.3.3-.6.7-.8 1.1-.2.4-.3.9-.3 1.5s.1 1 .3 1.5c.2.4.5.8.8 1.1.3.3.7.6 1.1.7.4.2.9.2 1.3.2s.8-.1 1.2-.2c.4-.1.7-.3 1-.5.3-.2.5-.5.7-.8.2-.3.3-.7.3-1.1h-3.3v-1.3h4.6v.8c0 .7-.1 1.3-.4 1.9-.2.6-.6 1-1 1.5-.4.4-.9.7-1.5.9-.3.2-.9.3-1.6.3zm10.5 0c-.7 0-1.4-.1-2-.4-.6-.3-1.1-.6-1.6-1.1-.4-.5-.8-1-1-1.6-.2-.6-.4-1.2-.4-1.9s.1-1.3.4-1.9c.2-.6.6-1.1 1-1.6s1-.8 1.6-1.1c.6-.3 1.3-.4 2-.4s1.4.1 2 .4c.6.2 1.1.6 1.6 1.1.5.5.8 1 1 1.6.2.6.4 1.2.4 1.9s-.1 1.3-.4 1.9c-.2.6-.6 1.1-1 1.6s-1 .8-1.6 1.1-1.2.4-2 .4zm0-1.4c.6 0 1.2-.2 1.8-.5.5-.3.9-.7 1.2-1.3s.5-1.2.5-1.9-.2-1.3-.5-1.9-.7-1-1.2-1.3c-.5-.3-1.1-.5-1.8-.5-.6 0-1.2.2-1.8.5-.5.3-.9.7-1.2 1.3s-.5 1.2-.5 1.9.2 1.3.5 1.9.7 1 1.2 1.3c.6.4 1.2.5 1.8.5zm11 1.4c-.7 0-1.4-.1-2-.4-.6-.3-1.1-.6-1.6-1.1-.4-.5-.8-1-1-1.6-.2-.6-.4-1.2-.4-1.9s.1-1.3.4-1.9c.2-.6.6-1.1 1-1.6s1-.8 1.6-1.1c.6-.3 1.3-.4 2-.4s1.4.1 2 .4c.6.2 1.1.6 1.6 1.1.5.5.8 1 1 1.6.2.6.4 1.2.4 1.9s-.1 1.3-.4 1.9c-.2.6-.6 1.1-1 1.6s-1 .8-1.6 1.1-1.3.4-2 .4zm0-1.4c.6 0 1.2-.2 1.8-.5.5-.3.9-.7 1.2-1.3s.5-1.2.5-1.9-.2-1.3-.5-1.9-.7-1-1.2-1.3c-.5-.3-1.1-.5-1.8-.5-.6 0-1.2.2-1.8.5-.5.3-.9.7-1.2 1.3s-.5 1.2-.5 1.9.2 1.3.5 1.9.7 1 1.2 1.3c.6.4 1.2.5 1.8.5zm6.6 1.2v-9.5h3c1 0 1.9.2 2.6.6.7.4 1.3 1 1.7 1.7s.6 1.5.6 2.5c0 .9-.2 1.8-.6 2.5s-1 1.3-1.7 1.7c-.7.4-1.6.6-2.6.6h-3zm1.5-1.4h1.5c.7 0 1.3-.1 1.8-.4.5-.3.9-.7 1.2-1.2.3-.5.4-1.1.4-1.8s-.1-1.3-.4-1.8c-.3-.5-.7-.9-1.2-1.2-.5-.3-1.1-.4-1.8-.4h-1.5v6.8z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st3&quot; d=&quot;M418.4 14.9h124.8v43.2H418.4V14.9z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st2&quot; d=&quot;M460.8 33.3v-9.5h1.7l4.3 7h.1l-.1-1.8v-5.2h1.5v9.5h-1.5l-4.5-7.4h-.1l.1 1.8v5.5h-1.5zm9.7 0v-9.5h5.8v1.4H472V32h4.3v1.4h-5.8zm.7-4.1v-1.4h4.6v1.4h-4.6zm6.9 4.1v-9.5h5.8v1.4h-4.3V32h4.3v1.4h-5.8zm.8-4.1v-1.4h4.6v1.4h-4.6zm6.8 4.1v-9.5h3c1 0 1.9.2 2.6.6.7.4 1.3 1 1.7 1.7s.6 1.5.6 2.5c0 .9-.2 1.8-.6 2.5s-1 1.3-1.7 1.7c-.7.4-1.6.6-2.6.6h-3zm1.5-1.4h1.5c.7 0 1.3-.1 1.8-.4.5-.3.9-.7 1.2-1.2.3-.5.4-1.1.4-1.8s-.1-1.3-.4-1.8c-.3-.5-.7-.9-1.2-1.2-.5-.3-1.1-.4-1.8-.4h-1.5v6.8zm10.9 1.6c-.5 0-1-.1-1.5-.3-.5-.2-.9-.5-1.2-.9-.3-.4-.6-.9-.8-1.5l1.4-.6c.1.5.4.9.8 1.3.4.3.8.5 1.3.5.3 0 .6-.1.8-.2.3-.1.5-.3.6-.5.2-.2.2-.5.2-.8 0-.3-.1-.5-.2-.7-.1-.2-.3-.4-.6-.5-.3-.2-.7-.3-1.1-.5l-.6-.2c-.3-.1-.5-.2-.8-.4-.3-.1-.5-.3-.7-.5-.2-.2-.4-.5-.5-.7-.1-.3-.2-.6-.2-1 0-.5.1-.9.4-1.3.2-.4.6-.7 1-.9.4-.2 1-.4 1.6-.4.6 0 1.1.1 1.5.3.4.2.7.5 1 .8.2.3.4.6.5.9l-1.3.6c-.1-.2-.2-.4-.3-.5-.1-.2-.3-.3-.5-.4-.2-.1-.5-.2-.8-.2-.3 0-.5.1-.8.2-.2.1-.4.2-.6.4-.1.2-.2.4-.2.6 0 .3.1.6.4.8s.7.4 1.2.6l.6.2c.3.1.6.2.9.4.3.1.6.3.8.6.2.2.4.5.6.8.1.3.2.7.2 1.2s-.1.9-.3 1.3c-.2.4-.4.6-.8.9-.3.2-.7.4-1 .5-.3 0-.7.1-1 .1zM434.4 49.3v-9.5h1.5v9.5h-1.5zm3.6 0v-9.5h2l2.8 7.3h.1l2.8-7.3h2v9.5h-1.4v-5.4l.1-1.7h-.1l-2.8 7.1h-1.2l-2.8-7.1h-.1l.1 1.7v5.4H438zm11.7 0v-9.5h3.3c.6 0 1.1.1 1.6.4.5.2.9.6 1.1 1 .3.4.4.9.4 1.5s-.1 1.1-.4 1.5c-.3.4-.7.8-1.1 1-.5.2-1 .4-1.6.4h-2.5v-1.4h2.5c.4 0 .7-.1.9-.2.2-.1.4-.3.5-.6.1-.2.2-.5.2-.8 0-.3-.1-.5-.2-.7-.1-.2-.3-.4-.5-.6-.2-.2-.5-.2-.9-.2h-1.8v8.2h-1.5zm8 0v-9.5h3.3c.6 0 1.1.1 1.6.4.5.2.8.6 1.1 1s.4.9.4 1.5c0 .4-.1.8-.2 1.1s-.4.7-.7.9c-.3.3-.6.5-1 .6-.4.1-.9.2-1.4.2h-2.3v-1.3h2.5c.3 0 .6-.1.8-.2.2-.1.4-.3.6-.6.2-.2.2-.5.2-.8 0-.3-.1-.5-.2-.7-.1-.2-.3-.4-.5-.6-.2-.1-.5-.2-.9-.2h-1.9v8.2h-1.4zm2.2-4.4h1.7l3 4.3v.1h-1.7l-3-4.4zm10.3 4.6c-.7 0-1.4-.1-2-.4-.6-.3-1.1-.6-1.6-1.1-.4-.5-.8-1-1-1.6-.2-.6-.4-1.2-.4-1.9s.1-1.3.4-1.9c.2-.6.6-1.1 1-1.6s1-.8 1.6-1.1c.6-.3 1.3-.4 2-.4s1.4.1 2 .4c.6.2 1.1.6 1.6 1.1.5.5.8 1 1 1.6.2.6.4 1.2.4 1.9s-.1 1.3-.4 1.9c-.2.6-.6 1.1-1 1.6s-1 .8-1.6 1.1c-.6.3-1.2.4-2 .4zm0-1.4c.6 0 1.2-.2 1.8-.5.5-.3.9-.7 1.2-1.3.3-.5.5-1.2.5-1.9s-.2-1.3-.5-1.9c-.3-.5-.7-1-1.2-1.3-.5-.3-1.1-.5-1.8-.5s-1.2.2-1.8.5c-.5.3-.9.7-1.2 1.3-.3.5-.5 1.2-.5 1.9s.2 1.3.5 1.9c.3.5.7 1 1.2 1.3.6.4 1.2.5 1.8.5zm8.8 1.2-3.4-9.5h1.6l2.2 6.3.3 1.1h.1l.4-1.1 2.2-6.3h1.6l-3.5 9.5H479zm6.2 0v-9.5h5.8v1.4h-4.3V48h4.3v1.4h-5.8zm.8-4.1v-1.4h4.6v1.4H486zm6.8 4.1v-9.5h2l2.8 7.3h.1l2.8-7.3h2v9.5H501v-5.4l.1-1.7h-.1l-2.8 7.1H497l-2.8-7.1h-.1l.1 1.7v5.4h-1.4zm11.8 0v-9.5h5.8v1.4h-4.3V48h4.3v1.4h-5.8zm.8-4.1v-1.4h4.6v1.4h-4.6zm6.8 4.1v-9.5h1.7l4.3 7h.1l-.1-1.8v-5.2h1.5v9.5h-1.5l-4.5-7.4h-.1l.1 1.8v5.5h-1.5zm11.7 0v-8.8h1.5v8.8h-1.5zm-2.7-8.2v-1.4h6.8v1.4h-6.8z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st4&quot; d=&quot;M543.2 14.9h115.2v43.2H543.2V14.9z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st2&quot; d=&quot;M582.9 41.3v-9.5h3.3c.6 0 1.1.1 1.6.4.5.2.9.6 1.1 1 .3.4.4.9.4 1.5s-.1 1.1-.4 1.5c-.3.4-.7.8-1.1 1-.5.2-1 .4-1.6.4h-2.5v-1.4h2.5c.4 0 .7-.1.9-.2.2-.1.4-.3.5-.6.1-.2.2-.5.2-.8 0-.3-.1-.5-.2-.7-.1-.2-.3-.4-.5-.6-.2-.2-.5-.2-.9-.2h-1.8v8.2h-1.5zm12.3.2c-.7 0-1.4-.1-2-.4-.6-.3-1.1-.6-1.6-1.1-.4-.5-.8-1-1-1.6-.2-.6-.4-1.2-.4-1.9s.1-1.3.4-1.9c.2-.6.6-1.1 1-1.6s1-.8 1.6-1.1c.6-.3 1.3-.4 2-.4s1.4.1 2 .4c.6.2 1.1.6 1.6 1.1.5.5.8 1 1 1.6.2.6.4 1.2.4 1.9s-.1 1.3-.4 1.9c-.2.6-.6 1.1-1 1.6s-1 .8-1.6 1.1c-.7.3-1.3.4-2 .4zm0-1.4c.6 0 1.2-.2 1.8-.5.5-.3.9-.7 1.2-1.3.3-.5.5-1.2.5-1.9s-.2-1.3-.5-1.9c-.3-.5-.7-1-1.2-1.3-.5-.3-1.1-.5-1.8-.5-.6 0-1.2.2-1.8.5-.5.3-.9.7-1.2 1.3-.3.5-.5 1.2-.5 1.9s.2 1.3.5 1.9c.3.5.7 1 1.2 1.3.6.4 1.1.5 1.8.5zm10.9 1.4c-.7 0-1.4-.1-2-.4-.6-.3-1.1-.6-1.6-1.1-.4-.5-.8-1-1-1.6-.2-.6-.4-1.2-.4-1.9s.1-1.3.4-1.9c.2-.6.6-1.1 1-1.6s1-.8 1.6-1.1c.6-.3 1.3-.4 2-.4s1.4.1 2 .4c.6.2 1.1.6 1.6 1.1.5.5.8 1 1 1.6.2.6.4 1.2.4 1.9s-.1 1.3-.4 1.9c-.2.6-.6 1.1-1 1.6s-1 .8-1.6 1.1c-.6.3-1.3.4-2 .4zm0-1.4c.6 0 1.2-.2 1.8-.5.5-.3.9-.7 1.2-1.3.3-.5.5-1.2.5-1.9s-.2-1.3-.5-1.9c-.3-.5-.7-1-1.2-1.3-.5-.3-1.1-.5-1.8-.5s-1.2.2-1.8.5c-.5.3-.9.7-1.2 1.3-.3.5-.5 1.2-.5 1.9s.2 1.3.5 1.9c.3.5.7 1 1.2 1.3.6.4 1.2.5 1.8.5zm6.7 1.2v-9.5h3.3c.6 0 1.1.1 1.6.4.5.2.8.6 1.1 1s.4.9.4 1.5c0 .4-.1.8-.2 1.1s-.4.7-.7.9c-.3.3-.6.5-1 .6-.4.1-.9.2-1.4.2h-2.3v-1.3h2.5c.3 0 .6-.1.8-.2.2-.1.4-.3.6-.6.2-.2.2-.5.2-.8 0-.3-.1-.5-.2-.7-.1-.2-.3-.4-.5-.6-.2-.1-.5-.2-.9-.2h-1.9v8.2h-1.4zm2.2-4.4h1.7l3 4.3v.1H618l-3-4.4zM517.7 102.9c-.6 0-1.1-.1-1.7-.3-.6-.2-1-.6-1.5-1-.4-.5-.7-1.1-.9-1.8l1.7-.7c.2.6.4 1.1.8 1.5.4.4.9.6 1.6.6.7 0 1.2-.2 1.6-.6.4-.4.7-1 .7-1.6 0-.7-.2-1.2-.6-1.6-.4-.4-1-.7-1.6-.7-.4 0-.8.1-1.1.2-.3.2-.6.4-.8.7l-1.8-.8.7-6h6.4v1.7h-4.9l-.4 3.2h.1c.3-.2.6-.4.9-.5.4-.1.8-.2 1.3-.2.7 0 1.3.2 1.9.5.6.3 1 .8 1.4 1.4.4.6.5 1.3.5 2 0 .8-.2 1.5-.5 2.1-.4.6-.8 1.1-1.5 1.4-.7.3-1.5.5-2.3.5zm10.8 0c-.7 0-1.4-.2-2-.5-.6-.3-1.1-.8-1.5-1.3-.4-.6-.8-1.2-1-2-.2-.8-.3-1.6-.3-2.4 0-.9.1-1.7.3-2.5.2-.8.6-1.4 1-2 .4-.6.9-1 1.5-1.3.6-.3 1.3-.5 2-.5.8 0 1.4.2 2 .5.6.3 1.1.8 1.5 1.3.4.6.8 1.2 1 2 .2.8.3 1.6.3 2.5 0 .9-.1 1.7-.3 2.4-.2.8-.5 1.4-1 2-.4.6-.9 1-1.5 1.3-.6.3-1.2.5-2 .5zm0-1.8c.6 0 1.1-.2 1.6-.6.4-.4.8-.9 1-1.6.2-.7.4-1.4.4-2.2 0-.8-.1-1.6-.4-2.3-.2-.7-.6-1.2-1-1.6-.4-.4-1-.6-1.6-.6-.6 0-1.2.2-1.6.6-.4.4-.8.9-1 1.6-.2.7-.4 1.4-.4 2.3 0 .8.1 1.6.4 2.2.2.7.6 1.2 1 1.6.5.4 1 .6 1.6.6zm11.4 1.8c-.7 0-1.4-.2-2-.5-.6-.3-1.1-.8-1.5-1.3-.4-.6-.8-1.2-1-2-.2-.8-.3-1.6-.3-2.4 0-.9.1-1.7.3-2.5.2-.8.6-1.4 1-2 .4-.6.9-1 1.5-1.3.6-.3 1.3-.5 2-.5.8 0 1.4.2 2 .5.6.3 1.1.8 1.5 1.3.4.6.8 1.2 1 2 .2.8.3 1.6.3 2.5 0 .9-.1 1.7-.3 2.4-.2.8-.5 1.4-1 2-.4.6-.9 1-1.5 1.3-.5.3-1.2.5-2 .5zm.1-1.8c.6 0 1.1-.2 1.6-.6.4-.4.8-.9 1-1.6.2-.7.4-1.4.4-2.2 0-.8-.1-1.6-.4-2.3-.2-.7-.6-1.2-1-1.6-.4-.4-1-.6-1.6-.6-.6 0-1.2.2-1.6.6-.4.4-.8.9-1 1.6-.2.7-.4 1.4-.4 2.3 0 .8.1 1.6.4 2.2.2.7.6 1.2 1 1.6.4.4.9.6 1.6.6zm10.8 1.5v-8.8h1.8V95h.1c.2-.3.4-.5.7-.8s.6-.4.9-.5c.4-.1.7-.2 1.1-.2.7 0 1.2.2 1.7.5s.8.7 1 1.2c.3-.5.7-.8 1.2-1.2.5-.3 1.1-.5 1.8-.5 1 0 1.8.3 2.3 1 .5.6.8 1.5.8 2.5v5.7h-1.9v-5.4c0-.7-.2-1.2-.5-1.5-.3-.3-.7-.5-1.3-.5-.4 0-.8.1-1.1.4-.3.2-.6.6-.8 1s-.3.9-.3 1.3v4.7h-1.9v-5.4c0-.7-.2-1.2-.5-1.5-.3-.3-.8-.5-1.3-.5-.4 0-.8.1-1.1.4-.3.2-.6.6-.7 1s-.3.9-.3 1.3v4.7h-1.7zm18.6.3c-.7 0-1.3-.1-1.8-.3-.5-.2-.9-.5-1.3-.9-.3-.4-.6-.8-.7-1.2l1.7-.7c.2.5.5.8.9 1.1.4.2.8.4 1.3.4s.9-.1 1.2-.2c.3-.2.5-.4.5-.8 0-.2-.1-.5-.2-.6s-.4-.3-.6-.4l-.9-.3-1.1-.2c-.4-.1-.8-.3-1.2-.5-.4-.2-.7-.5-.9-.9-.2-.4-.3-.8-.3-1.2 0-.5.2-1 .5-1.4.3-.4.7-.7 1.2-.9.5-.2 1.1-.3 1.7-.3.6 0 1.1.1 1.5.2.5.1.9.3 1.2.6.3.3.6.6.8 1l-1.6.7c-.2-.4-.5-.7-.8-.8-.3-.2-.7-.2-1.1-.2-.4 0-.8.1-1.1.3-.3.2-.4.4-.4.7 0 .3.1.5.4.7.3.2.6.3 1 .4l1.3.3c.9.2 1.5.6 2 1 .4.4.7 1 .7 1.6 0 .6-.2 1.1-.5 1.5-.3.4-.8.7-1.3 1-.8.2-1.5.3-2.1.3zM389.2 102.6v-1.8l.4-.4.9-.9c.4-.4.7-.8 1.1-1.2l1.1-1.1.8-.8c.3-.3.6-.6.8-.9.2-.3.3-.5.4-.8.1-.2.1-.5.1-.8 0-.3-.1-.6-.2-.8-.1-.3-.4-.5-.7-.6-.3-.2-.6-.2-1.1-.2-.4 0-.7.1-1 .2-.3.2-.5.4-.7.6-.2.2-.3.5-.3.7l-1.7-.7c.1-.3.2-.6.4-.9.2-.3.4-.6.8-.9.3-.3.7-.5 1.1-.7.4-.2.9-.3 1.5-.3.8 0 1.4.2 2 .5.6.3 1 .7 1.3 1.2.3.5.5 1.1.5 1.7 0 .5-.1 1-.3 1.5-.2.5-.4.9-.7 1.3-.3.4-.6.7-.9 1-.2.1-.4.3-.6.6-.2.2-.5.5-.7.8l-.8.8-.7.7-.4.4h5.2v1.7h-7.6zm14.2.3c-.7 0-1.4-.2-2-.5-.6-.3-1.1-.8-1.5-1.3-.4-.6-.8-1.2-1-2-.2-.8-.3-1.6-.3-2.4 0-.9.1-1.7.3-2.5.2-.8.6-1.4 1-2 .4-.6.9-1 1.5-1.3.6-.3 1.3-.5 2-.5.8 0 1.4.2 2 .5.6.3 1.1.8 1.5 1.3.4.6.8 1.2 1 2 .2.8.3 1.6.3 2.5 0 .9-.1 1.7-.3 2.4-.2.8-.5 1.4-1 2-.4.6-.9 1-1.5 1.3-.5.3-1.2.5-2 .5zm0-1.8c.6 0 1.1-.2 1.6-.6.4-.4.8-.9 1-1.6.2-.7.4-1.4.4-2.2 0-.8-.1-1.6-.4-2.3-.2-.7-.6-1.2-1-1.6-.4-.4-1-.6-1.6-.6-.6 0-1.2.2-1.6.6-.4.4-.8.9-1 1.6-.2.7-.4 1.4-.4 2.3 0 .8.1 1.6.4 2.2.2.7.6 1.2 1 1.6.5.4 1 .6 1.6.6zm11.5 1.8c-.7 0-1.4-.2-2-.5-.6-.3-1.1-.8-1.5-1.3-.4-.6-.8-1.2-1-2-.2-.8-.3-1.6-.3-2.4 0-.9.1-1.7.3-2.5.2-.8.6-1.4 1-2 .4-.6.9-1 1.5-1.3.6-.3 1.3-.5 2-.5.8 0 1.4.2 2 .5.6.3 1.1.8 1.5 1.3.4.6.8 1.2 1 2 .2.8.3 1.6.3 2.5 0 .9-.1 1.7-.3 2.4-.2.8-.5 1.4-1 2-.4.6-.9 1-1.5 1.3-.6.3-1.3.5-2 .5zm0-1.8c.6 0 1.1-.2 1.6-.6.4-.4.8-.9 1-1.6.2-.7.4-1.4.4-2.2 0-.8-.1-1.6-.4-2.3-.2-.7-.6-1.2-1-1.6-.4-.4-1-.6-1.6-.6-.6 0-1.2.2-1.6.6-.4.4-.8.9-1 1.6-.2.7-.4 1.4-.4 2.3 0 .8.1 1.6.4 2.2.2.7.6 1.2 1 1.6.4.4.9.6 1.6.6zm10.8 1.5v-8.8h1.8V95h.1c.2-.3.4-.5.7-.8s.6-.4.9-.5c.4-.1.7-.2 1.1-.2.7 0 1.2.2 1.7.5s.8.7 1 1.2c.3-.5.7-.8 1.2-1.2.5-.3 1.1-.5 1.8-.5 1 0 1.8.3 2.3 1 .5.6.8 1.5.8 2.5v5.7h-1.9v-5.4c0-.7-.2-1.2-.5-1.5-.3-.3-.7-.5-1.3-.5-.4 0-.8.1-1.1.4-.3.2-.6.6-.8 1s-.3.9-.3 1.3v4.7h-1.9v-5.4c0-.7-.2-1.2-.5-1.5-.3-.3-.8-.5-1.3-.5-.4 0-.8.1-1.1.4-.3.2-.6.6-.7 1s-.3.9-.3 1.3v4.7h-1.7zm18.6.3c-.7 0-1.3-.1-1.8-.3-.5-.2-.9-.5-1.3-.9-.3-.4-.6-.8-.7-1.2l1.7-.7c.2.5.5.8.9 1.1.4.2.8.4 1.3.4s.9-.1 1.2-.2c.3-.2.5-.4.5-.8 0-.2-.1-.5-.2-.6-.2-.2-.4-.3-.6-.4l-.9-.3-1.1-.2c-.4-.1-.8-.3-1.2-.5-.4-.2-.7-.5-.9-.9-.2-.4-.3-.8-.3-1.2 0-.5.2-1 .5-1.4.3-.4.7-.7 1.2-.9.5-.2 1.1-.3 1.7-.3.6 0 1.1.1 1.5.2.5.1.9.3 1.2.6.3.3.6.6.8 1l-1.6.7c-.2-.4-.5-.7-.8-.8-.3-.2-.7-.2-1.1-.2-.4 0-.8.1-1.1.3-.3.2-.4.4-.4.7 0 .3.1.5.4.7.3.2.6.3 1 .4l1.3.3c.9.2 1.5.6 2 1 .4.4.7 1 .7 1.6 0 .6-.2 1.1-.5 1.5-.3.4-.8.7-1.3 1-.8.2-1.4.3-2.1.3zM0 113.3V99h1.7v14.3H0zm4.6 0v-10.2h1.6v1.5h.1c.3-.5.7-.9 1.3-1.3.6-.4 1.3-.5 2-.5 1.2 0 2.2.4 2.8 1.1.6.7 1 1.7 1 2.9v6.5h-1.7V107c0-1-.2-1.7-.7-2.1-.5-.4-1.1-.6-1.8-.6-.6 0-1.1.2-1.5.5-.4.3-.8.7-1 1.2-.2.5-.4 1-.4 1.6v5.7H4.6zm10-10.2h6v1.5h-6v-1.5zm1.8 7.5v-10.4h1.7v10c0 .5.1.9.3 1.2.2.3.6.4 1.1.4.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.5-.5-.7-1.2-.7-2.1zm10.3 3c-1 0-1.9-.2-2.6-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.6-1.7-.6-2.8 0-1 .2-1.9.6-2.7.4-.8 1-1.5 1.7-2s1.6-.8 2.6-.8 1.9.2 2.6.7c.7.4 1.3 1.1 1.7 1.8.4.8.6 1.7.6 2.7v.5h-8.8V107h7c0-.3-.1-.6-.2-.9-.1-.3-.3-.6-.5-.9-.2-.3-.6-.5-.9-.7-.4-.2-.8-.3-1.4-.3-.7 0-1.2.2-1.7.5s-.8.8-1.1 1.4c-.3.6-.4 1.2-.4 2 0 .9.2 1.6.5 2.2.3.6.8 1 1.3 1.3.5.3 1.1.4 1.7.4.8 0 1.4-.2 1.8-.5.5-.4.9-.8 1.2-1.3l1.4.7c-.4.8-1 1.4-1.7 1.9-1 .5-1.9.8-3 .8zm6.7-.3v-10.2H35v1.6h.1c.1-.4.4-.7.7-1 .3-.3.7-.5 1.1-.7.4-.2.8-.2 1.2-.2h.7c.2 0 .3.1.5.2v1.8c-.2-.1-.5-.2-.7-.2-.2-.1-.5-.1-.7-.1-.5 0-1 .1-1.4.4-.4.3-.7.7-1 1.1-.2.5-.4 1-.4 1.5v5.7h-1.7zm10.4.3c-.8 0-1.4-.1-2-.4-.6-.3-1-.7-1.3-1.2-.3-.5-.5-1.1-.5-1.8 0-.8.2-1.4.6-1.9.4-.5.9-.9 1.5-1.2.7-.3 1.4-.4 2.2-.4.4 0 .9 0 1.2.1.4.1.7.2 1 .3.3.1.5.2.7.3v-.6c0-.8-.3-1.4-.8-1.8-.5-.5-1.2-.7-2-.7-.6 0-1.1.1-1.6.4-.5.2-.9.6-1.1 1l-1.3-1c.3-.4.6-.7 1-1 .4-.3.9-.5 1.4-.7.5-.2 1.1-.2 1.6-.2 1.4 0 2.5.4 3.2 1.1.8.7 1.2 1.7 1.2 3v6.5h-1.6v-1.5h-.1c-.2.3-.4.6-.7.8-.3.3-.7.5-1.1.7-.5.1-1 .2-1.5.2zm.1-1.5c.6 0 1.1-.1 1.6-.4.5-.3.9-.7 1.2-1.2.3-.5.5-1 .5-1.6-.3-.2-.7-.4-1.2-.5-.5-.1-1-.2-1.5-.2-1 0-1.7.2-2.1.6-.4.4-.7.9-.7 1.5s.2 1 .6 1.4c.4.2 1 .4 1.6.4zm11.7 1.5c-1 0-1.9-.2-2.7-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.7-1.7-.7-2.8 0-1 .2-2 .7-2.8.4-.8 1.1-1.5 1.8-1.9.8-.5 1.7-.7 2.7-.7 1.1 0 2.1.3 2.8.8.7.5 1.3 1.2 1.6 2l-1.5.6c-.2-.6-.6-1.1-1.1-1.4-.5-.3-1.1-.5-1.8-.5-.6 0-1.2.2-1.7.5s-.9.8-1.2 1.4c-.3.6-.5 1.3-.5 2 0 .8.2 1.4.5 2 .3.6.7 1 1.2 1.4.5.3 1.1.5 1.7.5.7 0 1.3-.2 1.9-.5.5-.3.9-.8 1.2-1.4l1.5.6c-.3.8-.9 1.5-1.6 2-.9.5-1.8.8-3 .8zm5.5-10.5h6v1.5h-6v-1.5zm1.8 7.5v-10.4h1.7v10c0 .5.1.9.3 1.2.2.3.6.4 1.1.4.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.4-.5-.7-1.2-.7-2.1zm6.5 2.7v-10.2h1.7v10.2h-1.7zm.9-12.1c-.3 0-.6-.1-.9-.4-.2-.2-.4-.5-.4-.9 0-.3.1-.6.4-.9.2-.2.5-.4.9-.4.3 0 .6.1.9.4.2.2.4.5.4.9 0 .3-.1.6-.4.9-.3.3-.6.4-.9.4zm8.1 12.4c-1 0-1.9-.2-2.7-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.7-1.7-.7-2.8 0-1 .2-1.9.7-2.8.4-.8 1.1-1.5 1.8-2 .8-.5 1.7-.7 2.7-.7 1 0 1.9.2 2.7.7.8.5 1.4 1.1 1.9 2 .4.8.7 1.7.7 2.7 0 1-.2 1.9-.7 2.8-.4.8-1.1 1.5-1.9 1.9-.8.5-1.7.8-2.7.8zm0-1.6c.6 0 1.2-.2 1.7-.5s1-.8 1.3-1.3c.3-.6.5-1.3.5-2.1s-.2-1.5-.5-2.1c-.3-.6-.8-1-1.3-1.3-.5-.3-1.1-.5-1.7-.5-.6 0-1.2.2-1.7.5s-1 .7-1.3 1.3c-.3.6-.5 1.3-.5 2.1s.2 1.5.5 2.1c.3.6.8 1 1.3 1.3.5.4 1.1.5 1.7.5zm7 1.3v-10.2H87v1.5h.1c.3-.5.7-.9 1.3-1.3.6-.4 1.3-.5 2-.5 1.2 0 2.2.4 2.8 1.1.6.7 1 1.7 1 2.9v6.5h-1.7V107c0-1-.2-1.7-.7-2.1-.5-.4-1.1-.6-1.8-.6-.6 0-1.1.2-1.5.5-.4.3-.8.7-1 1.2-.2.5-.4 1-.4 1.6v5.7h-1.7zm15.1-10.2h6v1.5h-6v-1.5zm1.8 7.5v-10.4h1.7v10c0 .5.1.9.3 1.2.2.3.6.4 1.1.4.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.4-.5-.7-1.2-.7-2.1zm10.5 3c-1 0-1.9-.2-2.7-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.7-1.7-.7-2.8 0-1 .2-1.9.7-2.8.4-.8 1.1-1.5 1.8-2 .8-.5 1.7-.7 2.7-.7 1 0 1.9.2 2.7.7.8.5 1.4 1.1 1.9 2 .4.8.7 1.7.7 2.7 0 1-.2 1.9-.7 2.8-.4.8-1.1 1.5-1.9 1.9-.8.5-1.7.8-2.7.8zm0-1.6c.6 0 1.2-.2 1.7-.5s1-.8 1.3-1.3c.3-.6.5-1.3.5-2.1s-.2-1.5-.5-2.1c-.3-.6-.8-1-1.3-1.3-.5-.3-1.1-.5-1.7-.5-.6 0-1.2.2-1.7.5s-1 .7-1.3 1.3c-.3.6-.5 1.3-.5 2.1s.2 1.5.5 2.1c.3.6.8 1 1.3 1.3.5.4 1.1.5 1.7.5zm12.2 1.3V99h2.1l7.2 11.4h.1l-.1-2.8V99h1.7v14.3h-1.8l-7.5-11.9h-.1l.1 2.8v9.2H125zm18.2.3c-1 0-1.9-.2-2.6-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.6-1.7-.6-2.8 0-1 .2-1.9.6-2.7.4-.8 1-1.5 1.7-2s1.6-.8 2.6-.8 1.9.2 2.6.7c.7.4 1.3 1.1 1.7 1.8.4.8.6 1.7.6 2.7v.5h-8.8V107h7c0-.3-.1-.6-.2-.9-.1-.3-.3-.6-.5-.9-.2-.3-.6-.5-.9-.7-.4-.2-.8-.3-1.4-.3-.7 0-1.2.2-1.7.5s-.8.8-1.1 1.4c-.3.6-.4 1.2-.4 2 0 .9.2 1.6.5 2.2.3.6.8 1 1.3 1.3.5.3 1.1.4 1.7.4.8 0 1.4-.2 1.8-.5.5-.4.9-.8 1.2-1.3l1.4.7c-.4.8-1 1.4-1.7 1.9-1 .5-1.9.8-3 .8zm5.5-.3 4.1-5.9h.2l2.9-4.3h2l-4 5.6h-.1l-3.1 4.6h-2zm.1-10.2h1.9l3.2 4.5h.1l4 5.7h-2l-3-4.5h-.1l-4.1-5.7zm9.9 0h6v1.5h-6v-1.5zm1.8 7.5v-10.4h1.7v10c0 .5.1.9.3 1.2.2.3.6.4 1.1.4.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.4-.5-.7-1.2-.7-2.1zm11.2 2.7V99h4.8c.8 0 1.5.2 2.2.5.7.4 1.2.8 1.6 1.5.4.6.6 1.4.6 2.2 0 .8-.2 1.6-.6 2.2-.4.6-.9 1.1-1.6 1.5-.7.4-1.4.5-2.2.5h-3.9v-1.6h4c.6 0 1-.1 1.4-.4.4-.3.7-.6.9-1 .2-.4.3-.8.3-1.2 0-.4-.1-.8-.3-1.2-.2-.4-.5-.7-.9-1-.4-.3-.9-.4-1.4-.4h-3.2v12.7h-1.7zm14 .3c-.8 0-1.4-.1-2-.4-.6-.3-1-.7-1.3-1.2-.3-.5-.5-1.1-.5-1.8 0-.8.2-1.4.6-1.9.4-.5.9-.9 1.5-1.2.7-.3 1.4-.4 2.2-.4.4 0 .9 0 1.2.1.4.1.7.2 1 .3.3.1.5.2.7.3v-.6c0-.8-.3-1.4-.8-1.8-.5-.5-1.2-.7-2-.7-.6 0-1.1.1-1.6.4-.5.2-.9.6-1.1 1l-1.3-1c.3-.4.6-.7 1-1 .4-.3.9-.5 1.4-.7.5-.2 1.1-.2 1.6-.2 1.4 0 2.5.4 3.2 1.1.8.7 1.2 1.7 1.2 3v6.5h-1.6v-1.5h-.1c-.2.3-.4.6-.7.8-.3.3-.7.5-1.1.7-.5.1-.9.2-1.5.2zm.2-1.5c.6 0 1.1-.1 1.6-.4.5-.3.9-.7 1.2-1.2.3-.5.5-1 .5-1.6-.3-.2-.7-.4-1.2-.5-.5-.1-1-.2-1.5-.2-1 0-1.7.2-2.1.6-.4.4-.7.9-.7 1.5s.2 1 .6 1.4c.4.2 1 .4 1.6.4zm7.4 1.2v-10.2h1.7v10.2h-1.7zm.8-12.1c-.3 0-.6-.1-.9-.4-.2-.2-.4-.5-.4-.9 0-.3.1-.6.4-.9.2-.2.5-.4.9-.4.3 0 .6.1.9.4.2.2.4.5.4.9 0 .3-.1.6-.4.9-.3.3-.5.4-.9.4zm3.5 12.1v-10.2h1.6v1.5h.1c.3-.5.7-.9 1.3-1.3.6-.4 1.3-.5 2-.5 1.2 0 2.2.4 2.8 1.1.6.7 1 1.7 1 2.9v6.5h-1.7V107c0-1-.2-1.7-.7-2.1-.5-.4-1.1-.6-1.8-.6-.6 0-1.1.2-1.5.5-.4.3-.8.7-1 1.2-.2.5-.4 1-.4 1.6v5.7h-1.7zm10-10.2h6v1.5h-6v-1.5zm1.8 7.5v-10.4h1.7v10c0 .5.1.9.3 1.2.2.3.6.4 1.1.4.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.4-.5-.7-1.2-.7-2.1zM418.1 55.2c-1.8 0-3.3 1.5-3.3 3.3 0 1.5 1 2.7 2.3 3.1V83h2V61.6c1.3-.4 2.3-1.7 2.3-3.1 0-1.8-1.5-3.3-3.3-3.3zM546.5 58.6c0-1.8-1.5-3.3-3.3-3.3s-3.3 1.5-3.3 3.3c0 1.5 1 2.7 2.3 3.1v21.2h2V61.7c1.3-.4 2.3-1.6 2.3-3.1z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/figure&gt;
&lt;p&gt;Depending on the website, there may be few to no interactions—such as pages of mostly text and images with few to no interactive elements. Or, in the case of websites such as text editors or games, there could be hundreds—even thousands—of interactions. In either case, where there&#39;s a high INP, the user experience is at risk.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-good-bg color-state-good-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; height=&quot;24&quot; width=&quot;24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Check&quot;&gt;   &lt;path d=&quot;M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Objective&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Read to learn more:&lt;/strong&gt; &lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;It takes time and effort to improve INP, but the reward is a better user experience. In this guide, a path to improving INP will be explored.&lt;/p&gt;
&lt;h2 id=&quot;figure-out-whats-causing-poor-inp&quot;&gt;Figure out what&#39;s causing poor INP &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-inp/#figure-out-whats-causing-poor-inp&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before you can fix slow interactions, you&#39;ll need data to tell you if your website&#39;s INP is poor or needs improvement. Once you have that information, you can move into the lab to begin diagnosing slow interactions, and work your way toward a solution.&lt;/p&gt;
&lt;h3 id=&quot;find-slow-interactions-in-the-field&quot;&gt;Find slow interactions in the field &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-inp/#find-slow-interactions-in-the-field&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Ideally, your journey in optimizing INP will start with &lt;a href=&quot;https://web.dev/lab-and-field-data-differences/#field-data&quot;&gt;field data&lt;/a&gt;. At its best, field data from a Real User Monitoring (RUM) provider will give you not only a page&#39;s INP value, but also contextual data that highlights what specific interaction was responsible for the INP value itself, whether the interaction occurred during or after page load, the type of interaction (click, keypress, or tap), and other valuable information.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-good-bg color-state-good-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; height=&quot;24&quot; width=&quot;24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Check&quot;&gt;   &lt;path d=&quot;M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Objective&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Read to learn more:&lt;/strong&gt; &lt;a href=&quot;https://web.dev/find-slow-interactions-in-the-field/&quot;&gt;Find slow interactions in the field&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;If you&#39;re not relying on a RUM provider to get field data, the &lt;a href=&quot;https://web.dev/find-slow-interactions-in-the-field/&quot;&gt;INP field data guide&lt;/a&gt; advises &lt;a href=&quot;https://web.dev/find-slow-interactions-in-the-field/#get-field-data-quickly-with-crux&quot;&gt;using the Chrome User Experience Report (CrUX) via PageSpeed Insights&lt;/a&gt; to help fill in the gaps. CrUX is the official dataset of the Core Web Vitals program and provides a high-level summary of metrics for millions of websites, including INP. However, CrUX often does not provide the contextual data you&#39;d get from a RUM provider to help you to analyze issues. Because of this, we still recommend that sites use a RUM provider when possible, or implement their own RUM solution to supplement what is available in CrUX.&lt;/p&gt;
&lt;h3 id=&quot;diagnose-slow-interactions-in-the-lab&quot;&gt;Diagnose slow interactions in the lab &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-inp/#diagnose-slow-interactions-in-the-lab&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Ideally, you&#39;ll want to start testing in the lab once you have field data that suggests you have slow interactions. In the absence of field data, there are some strategies for identifying slow interactions in the lab. Such strategies include following common user flows and testing interactions along the way, as well as interacting with the page during load—when the main thread is often busiest—in order to surface slow interactions during that crucial part of the user experience.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-good-bg color-state-good-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; height=&quot;24&quot; width=&quot;24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Check&quot;&gt;   &lt;path d=&quot;M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Objective&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Read to learn more:&lt;/strong&gt; &lt;a href=&quot;https://web.dev/diagnose-slow-interactions-in-the-lab/&quot;&gt;Diagnose slow interactions in the lab&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;optimize-interactions&quot;&gt;Optimize interactions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-inp/#optimize-interactions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once you&#39;ve identified a slow interaction and &lt;a href=&quot;https://web.dev/diagnose-slow-interactions-in-the-lab/&quot;&gt;can reproduce it in the lab&lt;/a&gt;, the next step is to optimize it. Interactions can be broken down into three phases:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The input delay, which starts when the user initiates an interaction with the page, and ends when the event callbacks for the interaction begin to run.&lt;/li&gt;
&lt;li&gt;The processing time, which consists of the time it takes for event callbacks to run to completion.&lt;/li&gt;
&lt;li&gt;The presentation delay, which is the time it takes for the browser to present the next frame which contains the visual result of the interaction.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The sum of these three phases is the total interaction latency. Every single phase of an interaction contributes some amount of time to total interaction latency, so it&#39;s important to know how you can optimize each part of the interaction so it runs for as little time as possible.&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; When optimizing interactions, it&#39;s important to understand that each &lt;a href=&quot;https://developer.mozilla.org/docs/Glossary/Browsing_context&quot;&gt;browsing context&lt;/a&gt; will have its own main thread. This means that the top-level page will have a main thread, but each &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; element on the page will have its own main thread as well. INP will be reported at the page-level including any slow interactions on the page or any iframes within that page. Ensure you understand which frame an interaction is happening in, to understand which main thread to look at. However, even with multiple main threads, resource-constrained devices can result in impact being felt across these seemingly independent threads. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;identify-and-reduce-input-delay&quot;&gt;Identify and reduce input delay &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-inp/#identify-and-reduce-input-delay&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When a user interacts with a page, the first part of that interaction is the &lt;em&gt;input delay&lt;/em&gt;. Depending on other activity on the page, input delays can be considerable in length. This could be due to activity occurring on the main thread (perhaps due to scripts loading, parsing and compiling), fetch handling, timer functions, or even from other interactions that occur in quick succession and overlap with one another.&lt;/p&gt;
&lt;p&gt;Whatever the source of an interaction&#39;s input delay, you&#39;ll want to reduce input delay to a minimum so that interactions can begin running event callbacks as soon as possible.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-good-bg color-state-good-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; height=&quot;24&quot; width=&quot;24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Check&quot;&gt;   &lt;path d=&quot;M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Objective&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Read to learn more:&lt;/strong&gt; &lt;a href=&quot;https://web.dev/optimize-input-delay/&quot;&gt;Optimize input delay&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;the-relationship-between-script-evaluation-and-long-tasks-during-startup&quot;&gt;The relationship between script evaluation and long tasks during startup &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-inp/#the-relationship-between-script-evaluation-and-long-tasks-during-startup&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A critical aspect of interactivity in the page lifecycle is during startup. As a page loads, it will initially render, but it&#39;s important to remember that just because a page has &lt;em&gt;rendered&lt;/em&gt;, doesn&#39;t mean that the page is finished &lt;em&gt;loading&lt;/em&gt;. Depending on how many resources a page requires to become fully functional, it&#39;s possible that users may attempt to interact with the page while it&#39;s still loading.&lt;/p&gt;
&lt;p&gt;One thing that can extend an interaction&#39;s input delay while a page loads is script evaluation. After a JavaScript file has been fetched from the network, the browser still has work to do before that JavaScript can run; that work includes parsing a script to ensure its syntax is valid, compiling it into bytecode, and then finally executing it.&lt;/p&gt;
&lt;p&gt;Depending on the size of a script, this work can introduce long tasks on the main thread, which will delay the browser from responding to other user interactions. To keep your page responsive to user input during page load, it&#39;s important to understand what you can do to reduce the likelihood of long tasks during page load so the page stays snappy.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-good-bg color-state-good-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; height=&quot;24&quot; width=&quot;24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Check&quot;&gt;   &lt;path d=&quot;M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Objective&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Read to learn more:&lt;/strong&gt; &lt;a href=&quot;https://web.dev/script-evaluation-and-long-tasks/&quot;&gt;Script evaluation and long tasks&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;optimize-event-callbacks&quot;&gt;Optimize event callbacks &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-inp/#optimize-event-callbacks&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The input delay is only the first part of what INP measures. You&#39;ll also need to make sure that the event callbacks that run in response to a user interaction can complete as quickly as possible.&lt;/p&gt;
&lt;h4 id=&quot;yield-to-the-main-thread-often&quot;&gt;Yield to the main thread often &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-inp/#yield-to-the-main-thread-often&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The best general advice in optimizing event callbacks is to do as little work as possible in them. However, your interaction logic may be complex, and you may only be able to marginally reduce the work they do.&lt;/p&gt;
&lt;p&gt;If you find this is the case for your website, the next thing you can try is to break up the work in event callbacks into separate tasks. This prevents the collective work from becoming a long task that blocks the main thread, which allows other interactions that otherwise would be waiting on the main thread to run sooner.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;setTimeout&lt;/code&gt; is one way to break up tasks, because the callback passed to it runs in a new task. You can &lt;a href=&quot;https://web.dev/optimize-long-tasks/#manually-defer-code-execution&quot;&gt;use &lt;code&gt;setTimeout&lt;/code&gt; by itself&lt;/a&gt; or abstract its use into a separate function &lt;a href=&quot;https://web.dev/optimize-long-tasks/#use-asyncawait-to-create-yield-points&quot;&gt;for more ergonomic yielding&lt;/a&gt;.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-good-bg color-state-good-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; height=&quot;24&quot; width=&quot;24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Check&quot;&gt;   &lt;path d=&quot;M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Objective&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Read to learn more:&lt;/strong&gt; &lt;a href=&quot;https://web.dev/optimize-long-tasks/&quot;&gt;Optimize long tasks&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Yielding indiscriminately is better than not yielding at all—however, there is a more nuanced way of yielding to the main thread, and that involves only yielding immediately after an event callback that updates the user interface so rendering logic can run sooner.&lt;/p&gt;
&lt;h4 id=&quot;yield-to-allow-rendering-work-to-occur-sooner&quot;&gt;Yield to allow rendering work to occur sooner &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-inp/#yield-to-allow-rendering-work-to-occur-sooner&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A more advanced yielding technique involves structuring the code in your event callbacks to limit what gets run to just the logic required to apply visual updates for the next frame. Everything else can be deferred to a subsequent task. This not only keeps callbacks light and nimble, but it also improves rendering time for interactions by not allowing visual updates to block on event callback code.&lt;/p&gt;
&lt;p&gt;For example, imagine a rich text editor that formats text as you type, but also updates other aspects of the UI in response to what you&#39;ve written (such as word count, highlighting spelling mistakes, and other important visual feedback). In addition, the application may also need to save what you&#39;ve written so that if you leave and return, you haven&#39;t lost any work.&lt;/p&gt;
&lt;p&gt;In this example, the following four things need to happen in response to characters typed by the user. However, only the first item needs to be done before the next frame is presented.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Update the text box with what the user typed and apply any required formatting.&lt;/li&gt;
&lt;li&gt;Update the part of the UI that displays the current word count.&lt;/li&gt;
&lt;li&gt;Run logic to check for spelling mistakes.&lt;/li&gt;
&lt;li&gt;Save the most recent changes (either locally or to a remote database).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The code to do this might look something like the following:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;textBox&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;input&#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;inputEvent&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;// Update the UI immediately, so the changes the user made&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// are visible as soon as the next frame is presented.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;updateTextBox&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputEvent&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;// Use `setTimeout` to defer all other work until at least the next&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// frame by queuing a task in a `requestAnimationFrame()` callback.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;requestAnimationFrame&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 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;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; textBox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textContent&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token function&quot;&gt;updateWordCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&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;checkSpelling&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&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;saveChanges&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&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;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 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 punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The following visualization shows how deferring any non-critical updates until after the next frame can reduce the processing time and thus the overall interaction latency.&lt;/p&gt;
&lt;figure&gt;
  &lt;a href=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Me4oU1cqMPOqEaEg2XAP.svg&quot; rel=&quot;noopener&quot;&gt;
    &lt;img alt=&quot;A depiction of a keyboard interaction and subsequent tasks in two scenarios. In the top figure, the render-critical task and all subsequent background tasks run synchronously until the opportunity to present a frame has arrived. In the bottom figure, the render-critical work runs first, then yields to the main thread to present a new frame sooner. The background tasks run thereafter.&quot; decoding=&quot;async&quot; height=&quot;495&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 770px) 770px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/MN6LranaUtJMlGZEA66T.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/MN6LranaUtJMlGZEA66T.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/MN6LranaUtJMlGZEA66T.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/MN6LranaUtJMlGZEA66T.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/MN6LranaUtJMlGZEA66T.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/MN6LranaUtJMlGZEA66T.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/MN6LranaUtJMlGZEA66T.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/MN6LranaUtJMlGZEA66T.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/MN6LranaUtJMlGZEA66T.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/MN6LranaUtJMlGZEA66T.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/MN6LranaUtJMlGZEA66T.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/MN6LranaUtJMlGZEA66T.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/MN6LranaUtJMlGZEA66T.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/MN6LranaUtJMlGZEA66T.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/MN6LranaUtJMlGZEA66T.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/MN6LranaUtJMlGZEA66T.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/MN6LranaUtJMlGZEA66T.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/MN6LranaUtJMlGZEA66T.png?auto=format&amp;w=1540 1540w&quot; width=&quot;770&quot; /&gt;
  &lt;/a&gt;
  &lt;figcaption&gt;
    Click the above figure to see a high-resolution version.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;While the use of &lt;code&gt;setTimeout()&lt;/code&gt; inside a &lt;code&gt;requestAnimationFrame()&lt;/code&gt; call in the previous code example is admittedly a bit esoteric, it is an effective method that works in all browsers to ensure that the non-critical code does not block the next frame.&lt;/p&gt;
&lt;h4 id=&quot;avoid-layout-thrashing&quot;&gt;Avoid layout thrashing &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-inp/#avoid-layout-thrashing&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Layout thrashing—sometimes called forced synchronous layout—is a rendering performance problem where layout occurs synchronously. It occurs when you update styles in JavaScript, and then read them in the same task—and &lt;a href=&quot;https://gist.github.com/paulirish/5d52fb081b3570c81e3a&quot; rel=&quot;noopener&quot;&gt;there are many properties in JavaScript that can cause layout thrashing&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A visualization of layout thrashing as shown in the performance panel of Chrome DevTools.&quot; decoding=&quot;async&quot; height=&quot;336&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/06CXJcBsqO6kdj1Bjml7.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/06CXJcBsqO6kdj1Bjml7.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/06CXJcBsqO6kdj1Bjml7.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/06CXJcBsqO6kdj1Bjml7.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/06CXJcBsqO6kdj1Bjml7.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/06CXJcBsqO6kdj1Bjml7.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/06CXJcBsqO6kdj1Bjml7.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/06CXJcBsqO6kdj1Bjml7.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/06CXJcBsqO6kdj1Bjml7.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/06CXJcBsqO6kdj1Bjml7.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/06CXJcBsqO6kdj1Bjml7.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/06CXJcBsqO6kdj1Bjml7.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/06CXJcBsqO6kdj1Bjml7.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/06CXJcBsqO6kdj1Bjml7.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/06CXJcBsqO6kdj1Bjml7.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/06CXJcBsqO6kdj1Bjml7.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/06CXJcBsqO6kdj1Bjml7.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/06CXJcBsqO6kdj1Bjml7.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    An example of layout thrashing, as shown in the performance panel of Chrome DevTools. Rendering tasks that involve layout thrashing will be noted with a red triangle at the upper right corner of the portion of the call stack, often labeled &lt;strong&gt;Recalculate Style&lt;/strong&gt; or &lt;strong&gt;Layout&lt;/strong&gt;.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Layout thrashing is a performance bottleneck because by updating styles and then immediately requesting the values of those styles in JavaScript, the browser is forced to do synchronous layout work it otherwise could have waited to perform asynchronously later on after event callbacks have finished running.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-good-bg color-state-good-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; height=&quot;24&quot; width=&quot;24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Check&quot;&gt;   &lt;path d=&quot;M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Objective&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Read to learn more:&lt;/strong&gt; &lt;a href=&quot;https://web.dev/avoid-large-complex-layouts-and-layout-thrashing/&quot;&gt;Avoid large, complex layouts and layout thrashing&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;minimize-presentation-delay&quot;&gt;Minimize presentation delay &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-inp/#minimize-presentation-delay&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;em&gt;presentation delay&lt;/em&gt; of an interaction marks spans from when an interaction&#39;s event callbacks have finished running, up to the point at which the browser is able to paint the next frame that shows the resulting visual changes.&lt;/p&gt;
&lt;h4 id=&quot;minimize-dom-size&quot;&gt;Minimize DOM size &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-inp/#minimize-dom-size&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When a page&#39;s DOM is small, rendering work usually finishes quickly. However, when DOMs get very large, rendering work tends to scale with increasing DOM size. The relationship between rendering work and DOM size isn&#39;t a linear one, but large DOMs do require more work to render than small DOMs. A large DOM is problematic in two cases:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;During the initial page render, where a large DOM requires a lot of work to render the page&#39;s initial state.&lt;/li&gt;
&lt;li&gt;In response to a user interaction, where a large DOM can cause rendering updates to be very expensive, and therefore increase the time it takes for the browser to present the next frame.&lt;/li&gt;
&lt;/ol&gt;
&lt;aside class=&quot;aside flow bg-state-good-bg color-state-good-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; height=&quot;24&quot; width=&quot;24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Check&quot;&gt;   &lt;path d=&quot;M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Objective&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Read to learn more:&lt;/strong&gt; &lt;a href=&quot;https://web.dev/dom-size-and-interactivity/&quot;&gt;DOM size and interactivity&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Bear in mind that there are instances in which large DOMs can&#39;t be significantly reduced. While there are approaches you can take to reduce DOM size, such as &lt;a href=&quot;https://web.dev/dom-size-and-interactivity/#how-can-i-reduce-dom-size&quot;&gt;flattening your DOM&lt;/a&gt; or &lt;a href=&quot;https://web.dev/dom-size-and-interactivity/#consider-an-additive-approach&quot;&gt;add to the DOM during user interactions&lt;/a&gt; to keep your initial DOM size small, those techniques may only go so far.&lt;/p&gt;
&lt;h4 id=&quot;use-content-visibility-to-lazily-render-off-screen-elements&quot;&gt;Use &lt;code&gt;content-visibility&lt;/code&gt; to lazily render off-screen elements &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-inp/#use-content-visibility-to-lazily-render-off-screen-elements&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;One way you can limit the amount of both rendering work during page load and rendering work in response to user interactions is to lean on the CSS &lt;code&gt;content-visibility&lt;/code&gt; property, which effectively amounts to lazily rendering elements as they approach the viewport. While &lt;code&gt;content-visibility&lt;/code&gt; can take some practice to use effectively, it&#39;s worth investigating if the result is lower rendering time that can improve your page&#39;s INP.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-good-bg color-state-good-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; height=&quot;24&quot; width=&quot;24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Check&quot;&gt;   &lt;path d=&quot;M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Objective&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Read to learn more:&lt;/strong&gt; &lt;a href=&quot;https://web.dev/content-visibility/&quot;&gt;&lt;code&gt;content-visibility&lt;/code&gt;: the new CSS property that boosts your rendering performance&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;be-aware-of-performance-costs-when-rendering-html-using-javascript&quot;&gt;Be aware of performance costs when rendering HTML using JavaScript &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-inp/#be-aware-of-performance-costs-when-rendering-html-using-javascript&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Where there&#39;s HTML, there&#39;s HTML parsing, and after the browser has finished parsing HTML into a DOM, it must apply styles to it, perform layout calculations, and subsequently render that layout. This is an unavoidable cost, but &lt;em&gt;how&lt;/em&gt; you go about rendering HTML matters.&lt;/p&gt;
&lt;p&gt;When the server sends HTML, it arrives in the browser as a stream. Streaming means that the HTML response from the server is arriving in chunks. The browser optimizes how it handles a stream by incrementally parsing chunks of that stream as they arrive, and rendering them bit by bit. This is a performance optimization in that the browser implicitly yields periodically and automatically during page load, and you get that for free.&lt;/p&gt;
&lt;p&gt;While the first visit to any website will always involve &lt;em&gt;some&lt;/em&gt; amount of HTML, a common approach starts with a minimal initial bit of HTML, and then JavaScript is used to populate the content area. Subsequent updates to that content area also occur as the result of user interactions. This is usually called the &lt;a href=&quot;https://en.wikipedia.org/wiki/Single-page_application&quot; rel=&quot;noopener&quot;&gt;single-page application (SPA) model&lt;/a&gt;. One drawback of this pattern is that, by rendering HTML with JavaScript on the client, you not only get the cost of the JavaScript processing to create that HTML, but also the browser will &lt;em&gt;not&lt;/em&gt; yield until it has finished parsing that HTML, and rendering it.&lt;/p&gt;
&lt;p&gt;It&#39;s vital to remember though, that even websites that &lt;em&gt;aren&#39;t&lt;/em&gt; SPAs will probably involve some amount of HTML rendering through JavaScript as the result of interactions. This is generally fine, so long as you&#39;re not rendering large amounts of HTML on the client, which can delay presentation of the next frame. However, it&#39;s important to understand the performance implications of this approach to rendering HTML in the browser, and how it can impact the responsiveness of your website to user input if you are rendering a lot of HTML via JavaScript.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-good-bg color-state-good-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; height=&quot;24&quot; width=&quot;24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Check&quot;&gt;   &lt;path d=&quot;M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Objective&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Read to learn more:&lt;/strong&gt; &lt;a href=&quot;https://web.dev/client-side-rendering-of-html-and-interactivity/&quot;&gt;Client-side rendering of HTML and interactivity&lt;/a&gt;. &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-inp/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Improving your site&#39;s INP is an iterative process. When you fix a slow interaction in the field, the chances are good that—especially if your website provides lots of interactivity—you&#39;ll start to find other slow interactions, and you&#39;ll need to optimize them too.&lt;/p&gt;
&lt;p&gt;The key to improving INP is persistence. In time, you can get your page&#39;s responsiveness to a place where users are happy with the experience you&#39;re providing them. The chances are also good that as you develop new features for your users, you may need to go through the same process in optimizing interactions specific to them. It will take time and effort, but it&#39;s time and effort well spent.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hero image from &lt;a href=&quot;https://unsplash.com/&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;, by &lt;a href=&quot;https://unsplash.com/@davidpisnoy&quot; rel=&quot;noopener&quot;&gt;David Pisnoy&lt;/a&gt; and modified in accordance with the &lt;a href=&quot;https://unsplash.com/license&quot; rel=&quot;noopener&quot;&gt;Unsplash license&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>Jeremy Wagner</name>
    </author><author>
      <name>Philip Walton</name>
    </author>
  </entry>
  
  <entry>
    <title>Optimize long tasks</title>
    <link href="https://web.dev/optimize-long-tasks/"/>
    <updated>2022-09-30T00:00:00Z</updated>
    <id>https://web.dev/optimize-long-tasks/</id>
    <content type="html" mode="escaped">&lt;p&gt;If you read lots of stuff about web performance, the advice for keeping your JavaScript apps fast tends to involve some of these tidbits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;Don&#39;t block the main thread.&amp;quot;&lt;/li&gt;
&lt;li&gt;&amp;quot;Break up your long tasks.&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What does any of that mean? Shipping &lt;em&gt;less&lt;/em&gt; JavaScript is good, but does that automatically equate to snappier user interfaces throughout the page lifecycle? Maybe, but maybe not.&lt;/p&gt;
&lt;p&gt;To get your head around why it&#39;s important to optimize tasks in JavaScript, you need to understand the role of tasks and how the browser handles them—and that starts with understanding what a task is.&lt;/p&gt;
&lt;h2 id=&quot;what-is-a-task&quot;&gt;What is a task? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-long-tasks/#what-is-a-task&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A &lt;em&gt;task&lt;/em&gt; is any discrete piece of work that the browser does. Tasks involve work such as rendering, parsing HTML and CSS, running the JavaScript code you write, and other things you may not have direct control over. Of all of this, the JavaScript you write and deploy to the web is a major source of tasks.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of a task as depicted in the performance profliler of Chrome&amp;#x27;s DevTools. The task is at the top of a stack, with a click event handler, a function call, and more items beneath it. The task also includes some rendering work on the right-hand side.&quot; decoding=&quot;async&quot; height=&quot;338&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDfxNSJzEDGD2jy1fU45.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDfxNSJzEDGD2jy1fU45.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDfxNSJzEDGD2jy1fU45.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDfxNSJzEDGD2jy1fU45.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDfxNSJzEDGD2jy1fU45.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDfxNSJzEDGD2jy1fU45.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDfxNSJzEDGD2jy1fU45.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDfxNSJzEDGD2jy1fU45.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDfxNSJzEDGD2jy1fU45.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDfxNSJzEDGD2jy1fU45.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDfxNSJzEDGD2jy1fU45.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDfxNSJzEDGD2jy1fU45.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDfxNSJzEDGD2jy1fU45.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDfxNSJzEDGD2jy1fU45.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDfxNSJzEDGD2jy1fU45.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDfxNSJzEDGD2jy1fU45.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDfxNSJzEDGD2jy1fU45.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDfxNSJzEDGD2jy1fU45.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    A depiction of a task kicked off by a &lt;code&gt;click&lt;/code&gt; event handler in the performance profiler in Chrome DevTools.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Tasks impact performance in a couple of ways. For example, when a browser downloads a JavaScript file during startup, it queues tasks to parse and compile that JavaScript so it can be executed. Later on in the page lifecycle, tasks are kicked off when your JavaScript does work such as driving interactions through event handlers, JavaScript-driven animations, and background activity such as analytics collection. All of this stuff—with the exception of &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Web_Workers_API/Using_web_workers&quot; rel=&quot;noopener&quot;&gt;web workers&lt;/a&gt; and similar APIs—happens on the main thread.&lt;/p&gt;
&lt;h2 id=&quot;what-is-the-main-thread&quot;&gt;What is the main thread? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-long-tasks/#what-is-the-main-thread&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;em&gt;main thread&lt;/em&gt; is where most tasks are run in the browser. It&#39;s called the main thread for a reason: it&#39;s the one thread where nearly all the JavaScript you write does its work.&lt;/p&gt;
&lt;p&gt;The main thread can only process one task at a time. When tasks stretch beyond a certain point—50 milliseconds to be exact—they&#39;re classified as &lt;em&gt;long tasks&lt;/em&gt;. If the user is attempting to interact with the page while a long task runs—or if an important rendering update needs to happen—the browser will be delayed in handling that work. This results in interaction or rendering latency.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A long task in the performance profiler of Chrome&amp;#x27;s DevTools. The blocking portion of the task (greater than 50 milliseconds) is depicted with a pattern of red diagonal stripes.&quot; decoding=&quot;async&quot; height=&quot;171&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZQtmFaSwfopJ3mEn7whr.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZQtmFaSwfopJ3mEn7whr.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZQtmFaSwfopJ3mEn7whr.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZQtmFaSwfopJ3mEn7whr.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZQtmFaSwfopJ3mEn7whr.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZQtmFaSwfopJ3mEn7whr.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZQtmFaSwfopJ3mEn7whr.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZQtmFaSwfopJ3mEn7whr.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZQtmFaSwfopJ3mEn7whr.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZQtmFaSwfopJ3mEn7whr.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZQtmFaSwfopJ3mEn7whr.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZQtmFaSwfopJ3mEn7whr.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZQtmFaSwfopJ3mEn7whr.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZQtmFaSwfopJ3mEn7whr.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZQtmFaSwfopJ3mEn7whr.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZQtmFaSwfopJ3mEn7whr.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZQtmFaSwfopJ3mEn7whr.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZQtmFaSwfopJ3mEn7whr.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    A long task as depicted in Chrome&#39;s performance profiler. Long tasks are indicated by a red triangle in the corner of the task, with the blocking portion of the task filled in with a pattern of diagonal red stripes.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You need to &lt;em&gt;break up&lt;/em&gt; tasks. This means taking a single long task and dividing it into smaller tasks that take less time to run individually.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A single long task versus the same task broken up into shorter task. The long task is one large rectangle, whereas the chunked task is five smaller boxes which are collectively the same width as the long task.&quot; decoding=&quot;async&quot; height=&quot;242&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8Bhl9Ilki4tM0aC1nfn8.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8Bhl9Ilki4tM0aC1nfn8.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8Bhl9Ilki4tM0aC1nfn8.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8Bhl9Ilki4tM0aC1nfn8.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8Bhl9Ilki4tM0aC1nfn8.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8Bhl9Ilki4tM0aC1nfn8.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8Bhl9Ilki4tM0aC1nfn8.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8Bhl9Ilki4tM0aC1nfn8.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8Bhl9Ilki4tM0aC1nfn8.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8Bhl9Ilki4tM0aC1nfn8.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8Bhl9Ilki4tM0aC1nfn8.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8Bhl9Ilki4tM0aC1nfn8.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8Bhl9Ilki4tM0aC1nfn8.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8Bhl9Ilki4tM0aC1nfn8.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8Bhl9Ilki4tM0aC1nfn8.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8Bhl9Ilki4tM0aC1nfn8.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8Bhl9Ilki4tM0aC1nfn8.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8Bhl9Ilki4tM0aC1nfn8.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    A visualization of a single long task versus that same task broken up into five shorter tasks.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This matters because when tasks are broken up, the browser has more opportunities to respond to higher-priority work—and that includes user interactions.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A depiction of how breaking up a task can facilitate a user interaction. At the top, a long task blocks an event handler from running until the task is finished. At the bottom, the chunked up task permits the event handler to run sooner than it otherwise would have.&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/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0yV0ynwW7FujIwvCbCxQ.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    A visualization of what happens to interactions when tasks are too long and the browser can&#39;t respond quickly enough to interactions, versus when longer tasks are broken up into smaller tasks.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;At the top of the preceding figure, an event handler queued up by a user interaction had to wait for a single long task before it could run, This delays the interaction from taking place. At the bottom, the event handler has a chance to run sooner. Because the event handler had an opportunity to run in between smaller tasks, it runs sooner than if it had to wait for a long task to finish. In the top example, the user might have noticed lag; in the bottom, the interaction might have felt &lt;em&gt;instant&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The problem, though, is that the advice of &amp;quot;break up your long tasks&amp;quot; and &amp;quot;don&#39;t block the main thread&amp;quot; isn&#39;t specific enough unless you already know &lt;em&gt;how&lt;/em&gt; to do those things. That&#39;s what this guide will explain.&lt;/p&gt;
&lt;h2 id=&quot;task-management-strategies&quot;&gt;Task management strategies &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-long-tasks/#task-management-strategies&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A common bit of advice in software architecture is to break up your work into smaller functions. This gives you the benefits of better code readability, and project maintainability. This also makes tests easier to write.&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;saveSettings&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 function&quot;&gt;validateForm&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 function&quot;&gt;showSpinner&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 function&quot;&gt;saveToDatabase&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 function&quot;&gt;updateUI&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 function&quot;&gt;sendAnalytics&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In this example, there&#39;s a function named &lt;code&gt;saveSettings()&lt;/code&gt; that calls five functions within it to do the work, such as validating a form, showing a spinner, sending data, and so on. Conceptually, this is well architected. If you need to debug one of these functions, you can traverse the project tree to figure out what each function does.&lt;/p&gt;
&lt;p&gt;The problem, however, is that JavaScript doesn&#39;t run each of these functions as separate tasks because they are being executed within the &lt;code&gt;saveSettings()&lt;/code&gt; function. &lt;strong&gt;This means that all five functions run as a single task.&lt;/strong&gt;&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; JavaScript works this way because it uses the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/EventLoop#run-to-completion&quot;&gt;run-to-completion model&lt;/a&gt; of task execution. This means that each task will run until it finishes, regardless of how long it blocks the main thread. &lt;/div&gt;&lt;/aside&gt;
&lt;figure&gt;
  &lt;img alt=&quot;The saveSettings function as depicted in Chrome&amp;#x27;s performance profiler. While the top-level function calls five other functions, all the work takes place in one long task that blocks the main thread.&quot; decoding=&quot;async&quot; height=&quot;301&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/1gENo7PnUwPhbTJmQKmX.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/1gENo7PnUwPhbTJmQKmX.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/1gENo7PnUwPhbTJmQKmX.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/1gENo7PnUwPhbTJmQKmX.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/1gENo7PnUwPhbTJmQKmX.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/1gENo7PnUwPhbTJmQKmX.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/1gENo7PnUwPhbTJmQKmX.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/1gENo7PnUwPhbTJmQKmX.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/1gENo7PnUwPhbTJmQKmX.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/1gENo7PnUwPhbTJmQKmX.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/1gENo7PnUwPhbTJmQKmX.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/1gENo7PnUwPhbTJmQKmX.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/1gENo7PnUwPhbTJmQKmX.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/1gENo7PnUwPhbTJmQKmX.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/1gENo7PnUwPhbTJmQKmX.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/1gENo7PnUwPhbTJmQKmX.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/1gENo7PnUwPhbTJmQKmX.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/1gENo7PnUwPhbTJmQKmX.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    A single function &lt;code&gt;saveSettings()&lt;/code&gt; that calls five functions. The work is run as part of one long monolithic task.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In the best case scenario, even just one of those functions can contribute 50 milliseconds or more to the total length of the task. In the worst case, more of those tasks can run quite a bit longer—especially on resource-constrained devices. What follows is a set of strategies you can use to break up and prioritize tasks.&lt;/p&gt;
&lt;h3 id=&quot;manually-defer-code-execution&quot;&gt;Manually defer code execution &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-long-tasks/#manually-defer-code-execution&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One method developers have used to break up tasks into smaller ones involves &lt;code&gt;setTimeout()&lt;/code&gt;. With this technique, you pass the function to &lt;code&gt;setTimeout()&lt;/code&gt;. This postpones execution of the callback into a separate task, even if you specify a timeout of &lt;code&gt;0&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;saveSettings&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;// Do critical work that is user-visible:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;validateForm&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 function&quot;&gt;showSpinner&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 function&quot;&gt;updateUI&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;// Defer work that isn&#39;t user-visible to a separate task:&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;    &lt;span class=&quot;token function&quot;&gt;saveToDatabase&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 function&quot;&gt;sendAnalytics&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;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 punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This works well if you have a series of functions that need to run sequentially, but your code may not always be organized this way. For example, you could have a large amount of data that needs to be processed in a loop, and that task could take a very long time if you have millions of items.&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;processData&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;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; item &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; largeDataArray&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;// Process the individual item here.&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Using &lt;code&gt;setTimeout()&lt;/code&gt; here is problematic, because the ergonomics of it make it difficult to implement, and the entire array of data could take a very long time to process, even if each item can be processed very quickly. It all adds up, and &lt;code&gt;setTimeout()&lt;/code&gt; isn&#39;t the right tool for the job—at least not when used this way.&lt;/p&gt;
&lt;p&gt;In addition to &lt;code&gt;setTimeout()&lt;/code&gt;, there are a few other APIs that allow you to defer code execution to a subsequent task. One &lt;a href=&quot;https://dbaron.org/log/20100309-faster-timeouts&quot; rel=&quot;noopener&quot;&gt;involves using &lt;code&gt;postMessage()&lt;/code&gt; for faster timeouts&lt;/a&gt;. You can also break up work using &lt;code&gt;requestIdleCallback()&lt;/code&gt;—but beware!—&lt;code&gt;requestIdleCallback()&lt;/code&gt; schedules tasks at the lowest possible priority, and only during browser idle time. When the main thread is congested, tasks scheduled with &lt;code&gt;requestIdleCallback()&lt;/code&gt; may never get to run.&lt;/p&gt;
&lt;h3 id=&quot;use-asyncawait-to-create-yield-points&quot;&gt;Use &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; to create yield points &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-long-tasks/#use-asyncawait-to-create-yield-points&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A phrase you&#39;ll see throughout the rest of this guide is &amp;quot;yield to the main thread&amp;quot;—but what does that mean? Why should you do it? When should you do it?&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; When you &lt;em&gt;yield&lt;/em&gt; to the main thread, you&#39;re giving it an opportunity to handle more important tasks than the ones that are currently queued up. Ideally, you should yield to the main thread whenever you have some crucial user-facing work that needs to execute sooner than if you didn&#39;t yield. &lt;strong&gt;Yielding to the main thread creates opportunities for critical work to run sooner.&lt;/strong&gt; &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;When tasks are broken up, other tasks can be prioritized better by the browser&#39;s internal prioritization scheme. One way to yield to the main thread involves using a combination of a &lt;code&gt;Promise&lt;/code&gt; that resolves with a call to &lt;code&gt;setTimeout()&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;yieldToMain&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; &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 parameter&quot;&gt;resolve&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 function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resolve&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 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;/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; While this code example returns a &lt;code&gt;Promise&lt;/code&gt; that resolves after a call to &lt;code&gt;setTimeout()&lt;/code&gt;, it&#39;s not the &lt;code&gt;Promise&lt;/code&gt; that is responsible for running the rest of the code in a new task, it&#39;s the &lt;code&gt;setTimeout()&lt;/code&gt; call. Promise callbacks run as &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/HTML_DOM_API/Microtask_guide#tasks_vs_microtasks&quot;&gt;microtasks&lt;/a&gt; rather than tasks, and therefore don&#39;t yield to the main thread. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;In the &lt;code&gt;saveSettings()&lt;/code&gt; function, you can yield to the main thread after each bit of work if you &lt;code&gt;await&lt;/code&gt; the &lt;code&gt;yieldToMain()&lt;/code&gt; function after each function call:&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;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;saveSettings&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;// Create an array of functions to run:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; tasks &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;    validateForm&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    showSpinner&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    saveToDatabase&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    updateUI&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    sendAnalytics&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;// Loop over the tasks:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tasks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &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;    &lt;span class=&quot;token comment&quot;&gt;// Shift the first task off the tasks array:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; task &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tasks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shift&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;// Run the task:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token function&quot;&gt;task&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;// Yield to the main thread:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;yieldToMain&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;/code&gt;&lt;/pre&gt;
&lt;/div&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; You don&#39;t have to yield after &lt;em&gt;every&lt;/em&gt; function call. For example, if you run two functions that result in critical updates to the user interface, you probably don&#39;t want to yield in between them. If you can, let that work run first, &lt;em&gt;then&lt;/em&gt; consider yielding in between functions that do less critical or background work that the user doesn&#39;t see. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The result is that the once-monolithic task is now broken up into separate tasks.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;The same saveSettings function depicted in Chrome&amp;#x27;s performance profiler, only with yielding. The result is the once-monolithic task is now broken up into five separate tasks&amp;amp;mdash;one for each function.&quot; decoding=&quot;async&quot; height=&quot;350&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mzrFeqjk00zUlKK0xzI8.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mzrFeqjk00zUlKK0xzI8.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mzrFeqjk00zUlKK0xzI8.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mzrFeqjk00zUlKK0xzI8.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mzrFeqjk00zUlKK0xzI8.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mzrFeqjk00zUlKK0xzI8.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mzrFeqjk00zUlKK0xzI8.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mzrFeqjk00zUlKK0xzI8.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mzrFeqjk00zUlKK0xzI8.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mzrFeqjk00zUlKK0xzI8.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mzrFeqjk00zUlKK0xzI8.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mzrFeqjk00zUlKK0xzI8.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mzrFeqjk00zUlKK0xzI8.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mzrFeqjk00zUlKK0xzI8.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mzrFeqjk00zUlKK0xzI8.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mzrFeqjk00zUlKK0xzI8.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mzrFeqjk00zUlKK0xzI8.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mzrFeqjk00zUlKK0xzI8.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    The &lt;code&gt;saveSettings()&lt;/code&gt; function now executes its child functions as separate tasks.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The benefit of using a promise-based approach to yielding rather than manual use of &lt;code&gt;setTimeout()&lt;/code&gt; is better ergonomics. Yield points become declarative, and therefore easier to write, read, and understand.&lt;/p&gt;
&lt;h3 id=&quot;yield-only-when-necessary&quot;&gt;Yield only when necessary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-long-tasks/#yield-only-when-necessary&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;What if you have a bunch of tasks, but you only want to yield if the user attempts to interact with the page? That&#39;s the kind of thing that &lt;a href=&quot;https://web.dev/isinputpending/&quot;&gt;&lt;code&gt;isInputPending()&lt;/code&gt;&lt;/a&gt; was made for.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;isInputPending()&lt;/code&gt; is a function you can run at any time to determine if the user is attempting to interact with a page element: a call to &lt;code&gt;isInputPending()&lt;/code&gt; will return &lt;code&gt;true&lt;/code&gt;. It returns &lt;code&gt;false&lt;/code&gt; otherwise.&lt;/p&gt;
&lt;p&gt;Say you have a queue of tasks you need to run, but you don&#39;t want to get in the way of any inputs. This code—which uses both &lt;code&gt;isInputPending()&lt;/code&gt; and our custom &lt;code&gt;yieldToMain()&lt;/code&gt; function—ensures that an input won&#39;t be delayed while the user is trying to interact with the page:&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;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;saveSettings&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;// A task queue of functions&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; tasks &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;    validateForm&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    showSpinner&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    saveToDatabase&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    updateUI&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    sendAnalytics&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;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tasks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &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;    &lt;span class=&quot;token comment&quot;&gt;// Yield to a pending user input:&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;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scheduling&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isInputPending&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;      &lt;span class=&quot;token comment&quot;&gt;// There&#39;s a pending user input. Yield here:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;yieldToMain&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;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;// Shift the task out of the queue:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; task &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tasks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shift&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;// Run the task:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token function&quot;&gt;task&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;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;While &lt;code&gt;saveSettings()&lt;/code&gt; runs, it will loop over the tasks in the queue. If &lt;code&gt;isInputPending()&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt; during the loop, &lt;code&gt;saveSettings()&lt;/code&gt; will call &lt;code&gt;yieldToMain()&lt;/code&gt; so the user input can be handled. Otherwise, it will shift the next task off the front of the queue and run it continuously. It will do this until no more tasks are left.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A depiction of the saveSettings function running in Chrome&amp;#x27;s performance profiler. The resulting task blocks the main thread until isInputPending returns true, at which point, the task yields to the main thread.&quot; decoding=&quot;async&quot; height=&quot;260&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Oz00JZNrYH4dNE3pmYss.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Oz00JZNrYH4dNE3pmYss.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Oz00JZNrYH4dNE3pmYss.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Oz00JZNrYH4dNE3pmYss.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Oz00JZNrYH4dNE3pmYss.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Oz00JZNrYH4dNE3pmYss.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Oz00JZNrYH4dNE3pmYss.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Oz00JZNrYH4dNE3pmYss.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Oz00JZNrYH4dNE3pmYss.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Oz00JZNrYH4dNE3pmYss.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Oz00JZNrYH4dNE3pmYss.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Oz00JZNrYH4dNE3pmYss.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Oz00JZNrYH4dNE3pmYss.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Oz00JZNrYH4dNE3pmYss.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Oz00JZNrYH4dNE3pmYss.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Oz00JZNrYH4dNE3pmYss.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Oz00JZNrYH4dNE3pmYss.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Oz00JZNrYH4dNE3pmYss.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;code&gt;saveSettings()&lt;/code&gt; runs a task queue for five tasks, but the user has clicked to open a menu while the second work item was running. &lt;code&gt;isInputPending()&lt;/code&gt; yields to the main thread to handle the interaction, and resume running the rest of the tasks.
  &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; &lt;code&gt;isInputPending()&lt;/code&gt; may not always return &lt;code&gt;true&lt;/code&gt; immediately after user input. This is because it takes time for the operating system to tell the browser that the interaction occurred. This means that other code may have already started executing (as you can see with the &lt;code&gt;saveToDatabase()&lt;/code&gt; function in the above screenshot). Even if you use &lt;code&gt;isInputPending()&lt;/code&gt; it&#39;s still crucial that you limit the amount of work you do in each function. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Using &lt;code&gt;isInputPending()&lt;/code&gt; in combination with a yielding mechanism is a great way to get the browser to stop whatever tasks it&#39;s processing so that it can respond to critical user-facing interactions. That can help improve your page&#39;s ability to respond to the user in many situations when a lot of tasks are in flight.&lt;/p&gt;
&lt;p&gt;Another way to use &lt;code&gt;isInputPending()&lt;/code&gt;—particularly if you&#39;re concerned about providing a fallback for browsers that don&#39;t support it—is to use a time-based approach in conjunction with the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Optional_chaining&quot; rel=&quot;noopener&quot;&gt;optional chaining operator&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 keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;saveSettings&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;// A task queue of functions&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; tasks &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;    validateForm&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    showSpinner&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    saveToDatabase&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    updateUI&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    sendAnalytics&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;let&lt;/span&gt; deadline &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;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&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;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tasks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &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;    &lt;span class=&quot;token comment&quot;&gt;// Optional chaining operator used here helps to avoid&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// errors in browsers that don&#39;t support `isInputPending`:&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;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scheduling&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isInputPending&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;||&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;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; deadline&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;// There&#39;s a pending user input, or the&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token comment&quot;&gt;// deadline has been reached. Yield here:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;yieldToMain&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;// Extend the deadline:&lt;/span&gt;&lt;br /&gt;      deadline &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;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&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;// Stop the execution of the current loop and&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token comment&quot;&gt;// move onto the next iteration:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;continue&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 comment&quot;&gt;// Shift the task out of the queue:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; task &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tasks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shift&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;// Run the task:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token function&quot;&gt;task&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;With this approach, you get a fallback for browsers that don&#39;t support &lt;code&gt;isInputPending()&lt;/code&gt; by using a time-based approach that uses (and adjusts) a deadline so that work will be broken up where necessary, whether by yielding to user input, or by a certain point in time.&lt;/p&gt;
&lt;h2 id=&quot;gaps-in-current-apis&quot;&gt;Gaps in current APIs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-long-tasks/#gaps-in-current-apis&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The APIs mentioned so far can help you break up tasks, but they have a significant downside: when you yield to the main thread by deferring code to run in a subsequent task, that code gets added to the very end of the task queue.&lt;/p&gt;
&lt;p&gt;If you control all the code on your page, it&#39;s possible to create your own scheduler with the ability to prioritize tasks, but third-party scripts won&#39;t use your scheduler. In effect, you&#39;re not really able to &lt;em&gt;prioritize&lt;/em&gt; work in such environments. You can only chunk it up, or explicitly yield to user interactions.&lt;/p&gt;
&lt;p&gt;Fortunately, there is a dedicated scheduler API that is currently in development that addresses these problems.&lt;/p&gt;
&lt;h3 id=&quot;a-dedicated-scheduler-api&quot;&gt;A dedicated scheduler API &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-long-tasks/#a-dedicated-scheduler-api&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The scheduler API currently offers the &lt;code&gt;postTask()&lt;/code&gt; function which, at the time of writing, is available in Chromium browsers, and in Firefox behind a flag. &lt;code&gt;postTask()&lt;/code&gt; allows for finer-grained scheduling of tasks, and is one way to help the browser prioritize work so that low priority tasks yield to the main thread. &lt;code&gt;postTask()&lt;/code&gt; uses promises, and accepts a &lt;code&gt;priority&lt;/code&gt; setting.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;postTask()&lt;/code&gt; API has three priorities you can use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&#39;background&#39;&lt;/code&gt; for the lowest priority tasks.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&#39;user-visible&#39;&lt;/code&gt; for medium priority tasks. This is the default if no &lt;code&gt;priority&lt;/code&gt; is set.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&#39;user-blocking&#39;&lt;/code&gt; for critical tasks that need to run at high priority.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Take the following code as an example, where the &lt;code&gt;postTask()&lt;/code&gt; API is used to run three tasks at the highest possible priority, and the remaining two tasks at the lowest possible priority.&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;saveSettings&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;// Validate the form at high priority&lt;/span&gt;&lt;br /&gt;  scheduler&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postTask&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;validateForm&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;user-blocking&#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;// Show the spinner at high priority:&lt;/span&gt;&lt;br /&gt;  scheduler&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postTask&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;showSpinner&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;user-blocking&#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;// Update the database in the background:&lt;/span&gt;&lt;br /&gt;  scheduler&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postTask&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;saveToDatabase&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;background&#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;// Update the user interface at high priority:&lt;/span&gt;&lt;br /&gt;  scheduler&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postTask&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;updateUI&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;user-blocking&#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;// Send analytics data in the background:&lt;/span&gt;&lt;br /&gt;  scheduler&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postTask&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sendAnalytics&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;background&#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;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;Here, the priority of tasks is scheduled in such a way that browser-prioritized tasks—such as user interactions—can work their way in.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;The saveSettings function as depicted in Chrome&amp;#x27;s performance profiler, but using postTask. postTask splits up each function saveSettings runs, and prioritizes them such that a user interaction has a chance to run without being blocked.&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/jL3OLOhcWUQDnR4XjewLBx4e3PC3/J6Ec735EReBFCTj1CuVf.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/J6Ec735EReBFCTj1CuVf.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/J6Ec735EReBFCTj1CuVf.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/J6Ec735EReBFCTj1CuVf.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/J6Ec735EReBFCTj1CuVf.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/J6Ec735EReBFCTj1CuVf.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/J6Ec735EReBFCTj1CuVf.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/J6Ec735EReBFCTj1CuVf.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/J6Ec735EReBFCTj1CuVf.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/J6Ec735EReBFCTj1CuVf.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/J6Ec735EReBFCTj1CuVf.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/J6Ec735EReBFCTj1CuVf.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/J6Ec735EReBFCTj1CuVf.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/J6Ec735EReBFCTj1CuVf.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/J6Ec735EReBFCTj1CuVf.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/J6Ec735EReBFCTj1CuVf.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/J6Ec735EReBFCTj1CuVf.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/J6Ec735EReBFCTj1CuVf.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    When &lt;code&gt;saveSettings()&lt;/code&gt; is run, the function schedules the individual functions using &lt;code&gt;postTask()&lt;/code&gt;. The critical user-facing work is scheduled at high priority, while work the user doesn&#39;t know about is scheduled to run in the background. This allows for user interactions to execute more quickly, as the work is both broken up &lt;em&gt;and&lt;/em&gt; prioritized appropriately.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This is a simplistic example of how &lt;code&gt;postTask()&lt;/code&gt; can be used. It&#39;s possible to instantiate different &lt;code&gt;TaskController&lt;/code&gt; objects that can share priorities between tasks, including the ability to change priorities for different &lt;code&gt;TaskController&lt;/code&gt; instances as needed.&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;a href=&quot;https://caniuse.com/mdn-api_scheduler_posttask&quot;&gt;&lt;code&gt;postTask()&lt;/code&gt; is not supported in all browsers&lt;/a&gt;. You can use feature detection to see if it&#39;s available, or consider using &lt;a href=&quot;https://www.npmjs.com/package/scheduler-polyfill&quot;&gt;a polyfill&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;built-in-yield-with-continuation-via-scheduleryield&quot;&gt;Built-in yield with continuation via &lt;code&gt;scheduler.yield&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-long-tasks/#built-in-yield-with-continuation-via-scheduleryield&quot;&gt;#&lt;/a&gt;&lt;/h3&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; For a more detailed explanation on &lt;code&gt;scheduler.yield&lt;/code&gt;, read &lt;a href=&quot;https://developer.chrome.com/blog/introducing-scheduler-yield-origin-trial/&quot;&gt;this post about its origin trial&lt;/a&gt;, as well as &lt;a href=&quot;https://github.com/WICG/scheduling-apis/blob/main/explainers/yield-and-continuation.md&quot;&gt;its explainer&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;One proposed part of the scheduler API is &lt;code&gt;scheduler.yield&lt;/code&gt;, an API specifically designed for yielding to the main thread in the browser &lt;a href=&quot;https://developer.chrome.com/origintrials/#/view_trial/836543630784069633&quot; rel=&quot;noopener&quot;&gt;which is currently available to try as an origin trial&lt;/a&gt;. Its use resembles the &lt;code&gt;yieldToMain()&lt;/code&gt; function demonstrated earlier in this article:&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;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;saveSettings&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;// Create an array of functions to run:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; tasks &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;    validateForm&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    showSpinner&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    saveToDatabase&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    updateUI&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    sendAnalytics&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;// Loop over the tasks:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tasks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &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;    &lt;span class=&quot;token comment&quot;&gt;// Shift the first task off the tasks array:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; task &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tasks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shift&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;// Run the task:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token function&quot;&gt;task&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;// Yield to the main thread with the scheduler&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// API&#39;s own yielding mechanism:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; scheduler&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;yield&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You&#39;ll note that the code above is largely familiar, but instead of using &lt;code&gt;yieldToMain()&lt;/code&gt;, you call and &lt;code&gt;await scheduler.yield()&lt;/code&gt; instead.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Three diagrams depicting tasks without yielding, yielding, and with yielding and continuation. Without yielding, there are long tasks. With yielding, there are more tasks that are shorter, but may be interrupted by other unrelated tasks. With yielding and continuation, there are more tasks that are shorter, but their order of execution is preserved.&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/5q9WG0lmBOwlqGYK16hd.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/5q9WG0lmBOwlqGYK16hd.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/5q9WG0lmBOwlqGYK16hd.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/5q9WG0lmBOwlqGYK16hd.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/5q9WG0lmBOwlqGYK16hd.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/5q9WG0lmBOwlqGYK16hd.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/5q9WG0lmBOwlqGYK16hd.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/5q9WG0lmBOwlqGYK16hd.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/5q9WG0lmBOwlqGYK16hd.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/5q9WG0lmBOwlqGYK16hd.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/5q9WG0lmBOwlqGYK16hd.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/5q9WG0lmBOwlqGYK16hd.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/5q9WG0lmBOwlqGYK16hd.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/5q9WG0lmBOwlqGYK16hd.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/5q9WG0lmBOwlqGYK16hd.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/5q9WG0lmBOwlqGYK16hd.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/5q9WG0lmBOwlqGYK16hd.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/5q9WG0lmBOwlqGYK16hd.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    A visualization of task execution without yielding, with yielding, and with yielding and continuation. When &lt;code&gt;scheduler.yield()&lt;/code&gt; is used, task execution picks up where it left off even after the yield point.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The benefit of &lt;code&gt;scheduler.yield()&lt;/code&gt; is continuation, which means that if you yield in the middle of a set of tasks, the other scheduled tasks will continue in the same order after the yield point. &lt;strong&gt;This avoids code from third-party scripts from usurping the order of your code&#39;s execution.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-long-tasks/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Managing tasks is challenging, but doing so helps your page respond more quickly to user interactions. There&#39;s no one single piece of advice for managing and prioritizing tasks. Rather, it&#39;s a number of different techniques. To reiterate, these are the main things you&#39;ll want to consider when managing tasks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Yield to the main thread for critical, user-facing tasks.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;isInputPending()&lt;/code&gt; to yield to the main thread when the user is trying to interact with the page.&lt;/li&gt;
&lt;li&gt;Prioritize tasks with &lt;code&gt;postTask()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Finally, &lt;strong&gt;do as little work as possible in your functions.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With one or more of these tools, you should be able to structure the work in your application so that it prioritizes the user&#39;s needs, while ensuring that less critical work still gets done. That&#39;s going to create a better user experience which is more responsive and more enjoyable to use.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Special thanks to &lt;a href=&quot;https://philipwalton.com/&quot; rel=&quot;noopener&quot;&gt;Philip Walton&lt;/a&gt; for his technical vetting of this article.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hero image sourced from &lt;a href=&quot;https://unsplash.com/&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;, courtesy of &lt;a href=&quot;https://unsplash.com/@amir_v_ali&quot; rel=&quot;noopener&quot;&gt;Amirali Mirhashemian&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>How the BBC is rolling out HSTS for better security and performance.</title>
    <link href="https://web.dev/bbc-hsts/"/>
    <updated>2022-07-25T00:00:00Z</updated>
    <id>https://web.dev/bbc-hsts/</id>
    <content type="html" mode="escaped">&lt;p&gt;HTTPS adoption has been steadily increasing in recent years. Per the HTTP Archive&#39;s 2021 Web Almanac, &lt;a href=&quot;https://almanac.httparchive.org/en/2021/security#transport-security&quot; rel=&quot;noopener&quot;&gt;around 91% of all requests for both desktop and mobile were served over HTTPS&lt;/a&gt;. HTTPS isn&#39;t just here to stay, it&#39;s a necessary prerequisite to use features such as &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Service_Worker_API&quot; rel=&quot;noopener&quot;&gt;Service Worker&lt;/a&gt; and modern protocols such as &lt;a href=&quot;https://web.dev/performance-http2/&quot;&gt;HTTP/2&lt;/a&gt; and &lt;a href=&quot;https://www.cloudflare.com/learning/performance/what-is-http3/&quot; rel=&quot;noopener&quot;&gt;HTTP/3&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Recently &lt;a href=&quot;https://twitter.com/tdp_org&quot; rel=&quot;noopener&quot;&gt;Neil Craig&lt;/a&gt;—a lead technical Architect at the BBC—&lt;a href=&quot;https://twitter.com/tdp_org/status/1549028675328573440?s=20&amp;amp;t=S5mm1eEqGUbxZ4jfi-SvtA&quot; rel=&quot;noopener&quot;&gt;tweeted that HTTP Strict Transport Security (HSTS) is being slowly rolled out&lt;/a&gt; for &lt;a href=&quot;https://www.bbc.com/&quot; rel=&quot;noopener&quot;&gt;bbc.com&lt;/a&gt;. Let&#39;s find out what that means for the BBC, and what it could mean for you.&lt;/p&gt;
&lt;h2 id=&quot;the-problem&quot;&gt;The problem &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bbc-hsts/#the-problem&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Web servers often listen for requests on both ports 80 and 443. Port 80 is for insecure HTTP requests, whereas 443 is for secure HTTPS. This can create a problem, because when you enter an address into your address bar without the &lt;code&gt;https://&lt;/code&gt; protocol prefix—like most users tend to do—some browsers will direct traffic to the insecure HTTP version of a site, for legacy reasons (&lt;a href=&quot;https://blog.chromium.org/2021/03/a-safer-default-for-navigation-https.html&quot; rel=&quot;noopener&quot;&gt;though this isn&#39;t always the case&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;A common way to ensure users don&#39;t access an unsecured version of a website is to place an HTTP-to-HTTPS redirect for all requests. This certainly works, but it kicks off the following chain of events:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The server receives a request via HTTP.&lt;/li&gt;
&lt;li&gt;The server issues a redirect to go to the HTTPS equivalent of the requested resource.&lt;/li&gt;
&lt;li&gt;The server via HTTPS must negotiate a secure connection with the browser.&lt;/li&gt;
&lt;li&gt;The content loads as usual.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;While redirects work fine, they can be misconfigured in ways that still allow access to the insecure version of a site. Even if everything &lt;em&gt;is&lt;/em&gt; configured properly, there is still a security issue in that the user will still connect over insecure HTTP during the redirect phase, which exposes users to the possibility of dangerous &lt;a href=&quot;https://en.wikipedia.org/wiki/Man-in-the-middle_attack&quot; rel=&quot;noopener&quot;&gt;man-in-the-middle attacks&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;enter-hsts&quot;&gt;Enter HSTS &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bbc-hsts/#enter-hsts&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 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;
      4
    &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 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;
      4
    &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 7, 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;
      7
    &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/HTTP/Headers/Strict-Transport-Security#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Strict-Transport-Security&quot; rel=&quot;noopener&quot;&gt;HSTS&lt;/a&gt; is dictated by the &lt;code&gt;Strict-Transport-Security&lt;/code&gt; HTTP response header for HTTPS requests. When set, return visits to a website will trigger a special redirect known as a &amp;quot;307 Internal Redirect&amp;quot;, which is when the browser handles the redirect logic, rather than the server. This prevents the request being intercepted, since it never leaves the browser, so is more secure. As an added bonus, these types of redirect are extremely fast, so any noticeable latency during an HTTP-to-HTTPS hop is eliminated.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A 307 internal redirect from HTTP to HTTPS, triggered by an HSTS header. The 307 redirect only takes 2 milliseconds.&quot; decoding=&quot;async&quot; height=&quot;84&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/RCCZMoqNd4aLb91Uj1hx.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/RCCZMoqNd4aLb91Uj1hx.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/RCCZMoqNd4aLb91Uj1hx.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/RCCZMoqNd4aLb91Uj1hx.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/RCCZMoqNd4aLb91Uj1hx.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/RCCZMoqNd4aLb91Uj1hx.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/RCCZMoqNd4aLb91Uj1hx.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/RCCZMoqNd4aLb91Uj1hx.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/RCCZMoqNd4aLb91Uj1hx.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/RCCZMoqNd4aLb91Uj1hx.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/RCCZMoqNd4aLb91Uj1hx.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/RCCZMoqNd4aLb91Uj1hx.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/RCCZMoqNd4aLb91Uj1hx.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/RCCZMoqNd4aLb91Uj1hx.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/RCCZMoqNd4aLb91Uj1hx.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/RCCZMoqNd4aLb91Uj1hx.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/RCCZMoqNd4aLb91Uj1hx.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/RCCZMoqNd4aLb91Uj1hx.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Similar in syntax to &lt;code&gt;Cache-Control&lt;/code&gt;&#39;s &lt;code&gt;max-age&lt;/code&gt; directive, an HSTS header specifies a &lt;code&gt;max-age&lt;/code&gt; directive. This directive takes a value in seconds that specifies how long the policy is effective for:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Strict-Transport-Security&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value hsts languages-hsts&quot;&gt;max-age=3600&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In the above example, the policy should only take effect for an hour.&lt;/p&gt;
&lt;h2 id=&quot;deploying-hsts&quot;&gt;Deploying HSTS &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bbc-hsts/#deploying-hsts&quot;&gt;#&lt;/a&gt;&lt;/h2&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; Take care to read the information in this section &lt;strong&gt;very carefully&lt;/strong&gt; before you decide to deploy HSTS! Botching an HSTS deployment can result in serious problems for users! &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The main drawback of deploying HSTS is if you&#39;re not ready to treat your origin as strictly secure. Let&#39;s say you have a number of subdomains you&#39;re serving resources from, but maybe not all of them are secure. In this scenario, &lt;strong&gt;an HSTS header could break your website.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The BBC took the right approach to deploying HSTS. As &lt;a href=&quot;https://twitter.com/tdp_org&quot; rel=&quot;noopener&quot;&gt;Neil Craig&lt;/a&gt; mentioned in his tweet, the initial value that was set for bbc.com was &lt;code&gt;max-age=10&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This approach means that the policy was only initially effective for ten seconds. This doesn&#39;t provide much of a benefit, but the idea is to feel out whether there may be issues with applying HSTS at all. As time goes on, you can increase the policy incrementally and see if issues occur. At the time of this writing, bbc.com is specifying an HSTS policy of &lt;code&gt;max-age=86400&lt;/code&gt;, and that will almost certainly increase over time.&lt;/p&gt;
&lt;p&gt;You certainly don&#39;t want to come out of the gate with a long &lt;code&gt;max-age&lt;/code&gt; value when deploying HSTS. You could find yourself suddenly scrambling to fix issues while users experience problems. &lt;strong&gt;Start small, and increment over time!&lt;/strong&gt; When you&#39;re confident all is well, you can set your &lt;code&gt;max-age&lt;/code&gt; directive to a much longer period of time. It is &lt;a href=&quot;https://hstspreload.org/#deployment-recommendations&quot; rel=&quot;noopener&quot;&gt;recommended to set &lt;code&gt;max-age&lt;/code&gt; to one or two years&lt;/a&gt; when it is fully rolled out.&lt;/p&gt;
&lt;h2 id=&quot;get-safer-and-faster-initial-navigations-with-the-hsts-preload-list&quot;&gt;Get safer and faster initial navigations with the HSTS preload list &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bbc-hsts/#get-safer-and-faster-initial-navigations-with-the-hsts-preload-list&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An HSTS policy only takes effect after the first visit to a website, so the benefits are not present for the first visit to the site. This will still require the insecure redirect. However, you can &lt;em&gt;preload&lt;/em&gt; your HSTS policy by submitting your website to the &lt;a href=&quot;https://hstspreload.org/&quot; rel=&quot;noopener&quot;&gt;HSTS preload list&lt;/a&gt;, which is a hardcoded list of websites that the browser knows are strictly HTTPS. When your site is on the preload list, the first visit is also protected and HTTP-to-HTTPS redirect latency via HSTS will be instantaneous.&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 have a domain with a &lt;code&gt;.dev&lt;/code&gt; top level domain (TLD), your website is already protected by HSTS as the whole &lt;code&gt;.dev&lt;/code&gt; TLD has been included in preload list. It will already be assumed as strictly secure by the browser and is required to have a valid SSL certificate to function at all. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;try-it-out-for-yourself&quot;&gt;Try it out for yourself &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/bbc-hsts/#try-it-out-for-yourself&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If the BBC feels comfortable testing out HSTS, there&#39;s a good chance that you can do the same for your website. Give it a shot for your website, and—if you&#39;re looking to rev things up—add it to the HSTS preload list when you&#39;re confident there are no bugs to give your users a safer &lt;em&gt;and&lt;/em&gt; faster experience.&lt;/p&gt;
</content>
    <author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>GOV.UK drops jQuery from their front end.</title>
    <link href="https://web.dev/gov-uk-drops-jquery/"/>
    <updated>2022-05-19T00:00:00Z</updated>
    <id>https://web.dev/gov-uk-drops-jquery/</id>
    <content type="html" mode="escaped">&lt;p&gt;&lt;a href=&quot;https://jquery.com/&quot; rel=&quot;noopener&quot;&gt;jQuery&lt;/a&gt; is a &lt;a href=&quot;https://bundlephobia.com/package/jquery@3.6.0&quot; rel=&quot;noopener&quot;&gt;roughly 30 KiB dependency&lt;/a&gt; that nearly &lt;a href=&quot;https://almanac.httparchive.org/en/2021/javascript#libraries-usage&quot; rel=&quot;noopener&quot;&gt;84% of mobile pages used in 2021&lt;/a&gt;—and for good reason. jQuery was an instrumental tool in a time when we really needed a way to script interactivity in a way that smoothed over the differing implementations of stuff like event handling, selecting elements, animating elements, and so on.&lt;/p&gt;
&lt;p&gt;The web is better because of jQuery—not just because it has such incredible utility, but because its ubiquity led to making what it provided part of the web platform itself. Nowadays, we can do just about anything jQuery can do in vanilla JavaScript:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We can select elements using a CSS selector syntax with &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Document/querySelector&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;querySelector&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Document/querySelectorAll&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;querySelectorAll&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;We can add, remove, and toggle classes on elements with the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Element/classList&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;classList API&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;We can attach event handlers to DOM elements and the &lt;code&gt;window&lt;/code&gt; using &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;addEventListener&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;And so, so, much more.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It really begs the question: &lt;a href=&quot;https://youmightnotneedjquery.com/&quot; rel=&quot;noopener&quot;&gt;Do we &lt;em&gt;really&lt;/em&gt; need jQuery today?&lt;/a&gt; That&#39;s a question that &lt;a href=&quot;https://www.gov.uk/&quot; rel=&quot;noopener&quot;&gt;GOV.UK&lt;/a&gt; has answered with a resounding &amp;quot;no&amp;quot;. In March of 2022, &lt;a href=&quot;https://twitter.com/TheRealNooshu&quot; rel=&quot;noopener&quot;&gt;Matt Hobbs&lt;/a&gt; announced that GOV.UK removed their jQuery dependency. This is a big deal when it comes to the user experience, because GOV.UK provides services and information online for The United Kingdom at scale. Not everyone is tapping away on their 2022 MacBook Pro on a rip-roarin&#39; broadband connection. GOV.UK has to be accessible to everyone, and that means keepin&#39; it &lt;em&gt;lean&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Here&#39;s a few of the greatest hits from Matt Hobbs on what GOV.UK noticed in removing jQuery:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://twitter.com/TheRealNooshu/status/1509487061300039681&quot; rel=&quot;noopener&quot;&gt;Less front end processing time overall&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://twitter.com/TheRealNooshu/status/1509487066941374466/photo/1&quot; rel=&quot;noopener&quot;&gt;11% less blocking time&lt;/a&gt; at the 75th percentile.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://twitter.com/TheRealNooshu/status/1509487072721125376/photo/1&quot; rel=&quot;noopener&quot;&gt;10% less blocking time&lt;/a&gt; for users at the 95th percentile. These are users who experience seriously adverse network and device conditions, and every performance gain matters &lt;em&gt;especially&lt;/em&gt; for them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the full story, check out &lt;a href=&quot;https://twitter.com/TheRealNooshu/status/1509487050122276864&quot; rel=&quot;noopener&quot;&gt;Matt&#39;s informative Twitter thread&lt;/a&gt;. It&#39;s great stuff for web performance geeks, and drives home the point that &lt;em&gt;dependencies matter&lt;/em&gt; when it comes to performance. Don&#39;t shortchange your users if the web platform can easily do the job a framework can.&lt;/p&gt;
&lt;p&gt;This level of commitment to the user experience from a institution that works at the scale GOV.UK does is commendable. I can only hope others follow in their footsteps.&lt;/p&gt;
</content>
    <author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Don&#39;t fight the browser preload scanner</title>
    <link href="https://web.dev/preload-scanner/"/>
    <updated>2022-05-13T00:00:00Z</updated>
    <id>https://web.dev/preload-scanner/</id>
    <content type="html" mode="escaped">&lt;p&gt;One overlooked aspect of optimizing page speed involves knowing a bit about browser internals. Browsers make certain optimizations to improve performance in ways that we as developers can&#39;t—but only so long as those optimizations aren&#39;t unintentionally thwarted.&lt;/p&gt;
&lt;p&gt;One internal browser optimization to understand is the browser preload scanner. This post will cover how the preload scanner works—and more importantly, how you can avoid getting in its way.&lt;/p&gt;
&lt;h2 id=&quot;whats-a-preload-scanner&quot;&gt;What&#39;s a preload scanner? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-scanner/#whats-a-preload-scanner&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Every browser has a primary HTML parser that &lt;a href=&quot;https://en.wikipedia.org/wiki/Lexical_analysis#Tokenization&quot; rel=&quot;noopener&quot;&gt;tokenizes&lt;/a&gt; raw markup and processes it into &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Document_Object_Model&quot; rel=&quot;noopener&quot;&gt;an object model&lt;/a&gt;. This all merrily goes on until the parser pauses when it finds a &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/render-blocking-resources/&quot; rel=&quot;noopener&quot;&gt;blocking resource&lt;/a&gt;, such as a stylesheet loaded with a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; element, or script loaded with a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; element without an &lt;code&gt;async&lt;/code&gt; or &lt;code&gt;defer&lt;/code&gt; attribute.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;HTML parser diagram.&quot; decoding=&quot;async&quot; height=&quot;450&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mXRoJneD6CbMAqaTqNZW.svg&quot; width=&quot;333&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 1:&lt;/strong&gt; A diagram of how the browser&#39;s primary HTML parser can be blocked. In this case, the parser runs into a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; element for an external CSS file, which blocks the browser from parsing the rest of the document&amp;mdash;or even rendering any of it&amp;mdash;until the CSS is downloaded and parsed.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In the case of CSS files, both parsing and rendering are blocked in order to prevent a &lt;a href=&quot;https://en.wikipedia.org/wiki/Flash_of_unstyled_content&quot; rel=&quot;noopener&quot;&gt;flash of unstyled content (FOUC)&lt;/a&gt;, which is when an unstyled version of a page can be seen briefly before styles are applied to it.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;The web.dev home page in an unstyled state (left) and the styled state (right).&quot; decoding=&quot;async&quot; height=&quot;748&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8LkMd46pUlwgfYvmTBrO.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8LkMd46pUlwgfYvmTBrO.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8LkMd46pUlwgfYvmTBrO.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8LkMd46pUlwgfYvmTBrO.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8LkMd46pUlwgfYvmTBrO.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8LkMd46pUlwgfYvmTBrO.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8LkMd46pUlwgfYvmTBrO.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8LkMd46pUlwgfYvmTBrO.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8LkMd46pUlwgfYvmTBrO.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8LkMd46pUlwgfYvmTBrO.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8LkMd46pUlwgfYvmTBrO.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8LkMd46pUlwgfYvmTBrO.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8LkMd46pUlwgfYvmTBrO.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8LkMd46pUlwgfYvmTBrO.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8LkMd46pUlwgfYvmTBrO.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8LkMd46pUlwgfYvmTBrO.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8LkMd46pUlwgfYvmTBrO.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/8LkMd46pUlwgfYvmTBrO.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 2:&lt;/strong&gt; A simulated example of FOUC. At left is the front page of web.dev without styles. At right is the same page with styles applied. The unstyled state can occur in a flash if the browser doesn&#39;t block rendering while a stylesheet is being downloaded and processed.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The browser also blocks parsing and rendering of the page when it encounters &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; elements without a &lt;code&gt;defer&lt;/code&gt; or &lt;code&gt;async&lt;/code&gt; attribute.&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; Scripts loaded from &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; elements with a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Guide/Modules#applying_the_module_to_your_html&quot;&gt;&lt;code&gt;type=module&lt;/code&gt; attribute&lt;/a&gt; are deferred by default. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The reason for this is that the browser can&#39;t know for sure if any given script will modify the DOM while the primary HTML parser is still doing its job. This is why it&#39;s been a common practice to load your JavaScript at the end of the document so that the effects of blocked parsing and rendering become marginal.&lt;/p&gt;
&lt;p&gt;These are good reasons for why the browser &lt;em&gt;should&lt;/em&gt; block both parsing and rendering. Yet, blocking either of these important steps is undesirable, as they can hold up the show by delaying the discovery of other important resources. Thankfully, browsers do their best to mitigate these problems by way of a secondary HTML parser called a &lt;em&gt;preload scanner&lt;/em&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A diagram of both the primary HTML parser (left) and the preload scanner (right), which is the secondary HTML parser.&quot; decoding=&quot;async&quot; height=&quot;450&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/6lccoVh4f6IJXA8UBKxH.svg&quot; width=&quot;657&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 3:&lt;/strong&gt; A diagram depicting how the preload scanner works in parallel with the primary HTML parser to speculatively load assets. Here, the primary HTML parser is blocked as it loads and processes CSS before it can begin processing image markup in the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element, but the preload scanner can look ahead in the raw markup to find that image resource and begin loading it before the primary HTML parser is unblocked.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;https://html.spec.whatwg.org/multipage/parsing.html#speculative-html-parsing&quot; rel=&quot;noopener&quot;&gt;A preload scanner&#39;s role is speculative&lt;/a&gt;, meaning that it examines raw markup in order to find resources to opportunistically fetch before the primary HTML parser would otherwise discover them.&lt;/p&gt;
&lt;h2 id=&quot;how-to-tell-when-the-preload-scanner-is-working&quot;&gt;How to tell when the preload scanner is working &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-scanner/#how-to-tell-when-the-preload-scanner-is-working&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The preload scanner exists &lt;em&gt;because&lt;/em&gt; of blocked rendering and parsing. If these two performance issues never existed, the preload scanner wouldn&#39;t be very useful. The key to figuring out whether a web page benefits from the preload scanner depends on these blocking phenomena. To do that, you can introduce an artificial delay for requests to find out where the preload scanner is working.&lt;/p&gt;
&lt;p&gt;Take &lt;a href=&quot;https://preload-scanner-fights.glitch.me/artifically-delayed-requests.html&quot; rel=&quot;noopener&quot;&gt;this page&lt;/a&gt; of basic text and images with a stylesheet as an example. Because CSS files block both rendering and parsing, you introduce an artificial delay of two seconds for the stylesheet through a proxy service. This delay makes it easier to see in the network waterfall where the preload scanner is working.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;The WebPageTest network waterfall chart illustrates an artificial delay of 2 seconds imposed on the styleesheet.&quot; decoding=&quot;async&quot; height=&quot;219&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Gtw08XaoFETKEauBBbBl.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Gtw08XaoFETKEauBBbBl.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Gtw08XaoFETKEauBBbBl.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Gtw08XaoFETKEauBBbBl.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Gtw08XaoFETKEauBBbBl.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Gtw08XaoFETKEauBBbBl.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Gtw08XaoFETKEauBBbBl.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Gtw08XaoFETKEauBBbBl.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Gtw08XaoFETKEauBBbBl.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Gtw08XaoFETKEauBBbBl.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Gtw08XaoFETKEauBBbBl.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Gtw08XaoFETKEauBBbBl.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Gtw08XaoFETKEauBBbBl.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Gtw08XaoFETKEauBBbBl.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Gtw08XaoFETKEauBBbBl.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Gtw08XaoFETKEauBBbBl.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Gtw08XaoFETKEauBBbBl.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Gtw08XaoFETKEauBBbBl.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 4:&lt;/strong&gt; A &lt;a href=&quot;https://www.webpagetest.org/&quot; rel=&quot;nofollow noopener&quot;&gt;WebPageTest&lt;/a&gt; &lt;a href=&quot;https://developer.chrome.com/docs/devtools/network/reference/#waterfall&quot; rel=&quot;noopener&quot;&gt;network waterfall chart&lt;/a&gt; of &lt;a href=&quot;https://preload-scanner-fights.glitch.me/artifically-delayed-requests.html&quot; rel=&quot;noopener&quot;&gt;a web page&lt;/a&gt; run on Chrome on a mobile device over a simulated 3G connection. Even though the stylesheet is artificially delayed through a proxy by two seconds before it begins to load, the image located later in the markup payload is discovered by the preload scanner.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;As you can see in the waterfall, the preload scanner discovers the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element &lt;em&gt;even while rendering and document parsing is blocked&lt;/em&gt;. Without this optimization, the browser can&#39;t fetch things opportunistically during the blocking period, and more resource requests would be consecutive rather than concurrent.&lt;/p&gt;
&lt;p&gt;With that toy example out of the way, let&#39;s take a look at some real-world patterns where the preload scanner can be defeated—and what can be done to fix them.&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; This smattering of patterns isn&#39;t an exhaustive list, just some common ones. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;injected-async-scripts&quot;&gt;Injected &lt;code&gt;async&lt;/code&gt; scripts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-scanner/#injected-async-scripts&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let&#39;s say you&#39;ve got HTML in your &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; that includes some inline JavaScript like this:&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 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;const&lt;/span&gt; scriptEl &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;  scriptEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/yall.min.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;head&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;scriptEl&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;p&gt;Injected scripts are &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/script#attr-async&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;async&lt;/code&gt;&lt;/a&gt; by default, so when this script is injected, it will behave as if the &lt;code&gt;async&lt;/code&gt; attribute was applied to it. That means it will run as soon as possible and not block rendering. Sounds optimal, right? Yet, if you presume that this inline &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; comes after a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; element that loads an external CSS file, you&#39;ll get a suboptimal result:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;This WebPageTest chart shows the preload scan defeated when a script is injected.&quot; decoding=&quot;async&quot; height=&quot;219&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/L9ltNSHa6D4FI6YPw1vF.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/L9ltNSHa6D4FI6YPw1vF.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/L9ltNSHa6D4FI6YPw1vF.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/L9ltNSHa6D4FI6YPw1vF.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/L9ltNSHa6D4FI6YPw1vF.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/L9ltNSHa6D4FI6YPw1vF.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/L9ltNSHa6D4FI6YPw1vF.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/L9ltNSHa6D4FI6YPw1vF.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/L9ltNSHa6D4FI6YPw1vF.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/L9ltNSHa6D4FI6YPw1vF.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/L9ltNSHa6D4FI6YPw1vF.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/L9ltNSHa6D4FI6YPw1vF.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/L9ltNSHa6D4FI6YPw1vF.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/L9ltNSHa6D4FI6YPw1vF.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/L9ltNSHa6D4FI6YPw1vF.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/L9ltNSHa6D4FI6YPw1vF.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/L9ltNSHa6D4FI6YPw1vF.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/L9ltNSHa6D4FI6YPw1vF.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 5:&lt;/strong&gt; A WebPageTest network waterfall chart of &lt;a href=&quot;https://preload-scanner-fights.glitch.me/injected-async-script.html&quot; rel=&quot;noopener&quot;&gt;a web page&lt;/a&gt; run on Chrome on a mobile device over a simulated 3G connection. The page contains a single stylesheet and an injected &lt;code&gt;async&lt;/code&gt; script. The preload scanner can&#39;t discover the script during the render blocking phase, because it&#39;s injected on the client.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Let&#39;s break down what happened here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;At 0 seconds, the main document is requested.&lt;/li&gt;
&lt;li&gt;At 1.4 seconds, the first byte of the navigation request arrives.&lt;/li&gt;
&lt;li&gt;At 2.0 seconds, the CSS and image are requested.&lt;/li&gt;
&lt;li&gt;Because the parser is blocked loading the stylesheet and the inline JavaScript that injects the &lt;code&gt;async&lt;/code&gt; script comes &lt;em&gt;after&lt;/em&gt; that stylesheet at 2.6 seconds, the functionality that script provides isn&#39;t available as soon as it could be.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is suboptimal because the request for the script occurs only after the stylesheet has finished downloading. This delays the script from running as soon as possible. This could have the potential to affect a page&#39;s &lt;a href=&quot;https://web.dev/tti/&quot;&gt;Time to Interactive (TTI)&lt;/a&gt;. By contrast, because the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element is discoverable in the server-provided markup, it&#39;s discovered by the preload scanner.&lt;/p&gt;
&lt;p&gt;So, what happens if you use a regular &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag with the &lt;code&gt;async&lt;/code&gt; attribute as opposed to injecting the script into the DOM?&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;/yall.min.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 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;This is the result:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A WebPageTest network waterfall depicting how an async script loaded by using the HTML &amp;amp;lt;script&amp;amp;gt; element is still discoverable by the browser preload scanner, even though the browser&amp;#x27;s primary HTML parser is blocked while downloading and processing a stylesheet.&quot; decoding=&quot;async&quot; height=&quot;219&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Hb4IRf5XDVBpfakZbteE.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Hb4IRf5XDVBpfakZbteE.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Hb4IRf5XDVBpfakZbteE.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Hb4IRf5XDVBpfakZbteE.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Hb4IRf5XDVBpfakZbteE.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Hb4IRf5XDVBpfakZbteE.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Hb4IRf5XDVBpfakZbteE.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Hb4IRf5XDVBpfakZbteE.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Hb4IRf5XDVBpfakZbteE.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Hb4IRf5XDVBpfakZbteE.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Hb4IRf5XDVBpfakZbteE.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Hb4IRf5XDVBpfakZbteE.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Hb4IRf5XDVBpfakZbteE.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Hb4IRf5XDVBpfakZbteE.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Hb4IRf5XDVBpfakZbteE.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Hb4IRf5XDVBpfakZbteE.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Hb4IRf5XDVBpfakZbteE.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Hb4IRf5XDVBpfakZbteE.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 6:&lt;/strong&gt; A WebPageTest network waterfall chart of &lt;a href=&quot;https://preload-scanner-fights.glitch.me/inline-async-script.html&quot; rel=&quot;noopener&quot;&gt;a web page&lt;/a&gt; run on Chrome on a mobile device over a simulated 3G connection. The page contains a single stylesheet and a single &lt;code&gt;async&lt;/code&gt; &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; element. The preload scanner discovers the script during the render blocking phase, and loads it concurrently with the CSS.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;There may be some temptation to suggest that these issues could be remedied by using &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Link_types/preload&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;rel=preload&lt;/code&gt;&lt;/a&gt;. This would certainly work, but it may carry some side effects. After all, why use &lt;code&gt;rel=preload&lt;/code&gt; to fix a problem that can be avoided by &lt;em&gt;not&lt;/em&gt; injecting a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; element into the DOM?&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A WebPageTest waterfall showing how the rel=preload resource hint is used to promote discovery of an async injected script&amp;amp;mdash;albeit in a way that may have unintended side-effects.&quot; decoding=&quot;async&quot; height=&quot;219&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/O6KzUOeFD2cCFg11XK8W.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/O6KzUOeFD2cCFg11XK8W.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/O6KzUOeFD2cCFg11XK8W.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/O6KzUOeFD2cCFg11XK8W.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/O6KzUOeFD2cCFg11XK8W.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/O6KzUOeFD2cCFg11XK8W.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/O6KzUOeFD2cCFg11XK8W.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/O6KzUOeFD2cCFg11XK8W.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/O6KzUOeFD2cCFg11XK8W.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/O6KzUOeFD2cCFg11XK8W.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/O6KzUOeFD2cCFg11XK8W.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/O6KzUOeFD2cCFg11XK8W.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/O6KzUOeFD2cCFg11XK8W.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/O6KzUOeFD2cCFg11XK8W.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/O6KzUOeFD2cCFg11XK8W.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/O6KzUOeFD2cCFg11XK8W.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/O6KzUOeFD2cCFg11XK8W.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/O6KzUOeFD2cCFg11XK8W.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 7:&lt;/strong&gt; A WebPageTest network waterfall chart of &lt;a href=&quot;https://preload-scanner-fights.glitch.me/preloaded-injected-async-script.html&quot; rel=&quot;noopener&quot;&gt;a web page&lt;/a&gt; run on Chrome on a mobile device over a simulated 3G connection. The page contains a single stylesheet and an injected &lt;code&gt;async&lt;/code&gt; script, but the &lt;code&gt;async&lt;/code&gt; script is preloaded to ensure it is discovered sooner.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Preloading &amp;quot;fixes&amp;quot; the problem here, but it introduces a new problem: the &lt;code&gt;async&lt;/code&gt; script in the first two demos—despite being loaded in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;—are loaded at &amp;quot;Low&amp;quot; priority, whereas the stylesheet is loaded at &amp;quot;Highest&amp;quot; priority. In the last demo where the &lt;code&gt;async&lt;/code&gt; script is preloaded, the stylesheet is still loaded at &amp;quot;Highest&amp;quot; priority, but the script&#39;s priority has been promoted to &amp;quot;High&amp;quot;.&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; Resource priority can be discovered in the network tab in modern browsers. For Chrome DevTools in particular, &lt;a href=&quot;https://developer.chrome.com/docs/devtools/network/reference/#columns&quot;&gt;you can right click on the column headers&lt;/a&gt; to ensure the priority column is visible. Be sure to test in multiple browsers, as resource priority varies by browser and other factors. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;When a resource&#39;s priority is raised, the browser allocates more bandwidth to it. This means that—even though the stylesheet has the highest priority—the script&#39;s raised priority may cause bandwidth contention. That could be a factor on slow connections, or in cases where resources are quite large.&lt;/p&gt;
&lt;p&gt;The answer here is straightforward: if a script is needed during startup, don&#39;t defeat the preload scanner by injecting it into the DOM. Experiment as needed with &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; element placement, as well as with attributes such as &lt;code&gt;defer&lt;/code&gt; and &lt;code&gt;async&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; Ilya Grigorik has written &lt;a href=&quot;https://www.igvita.com/2014/05/20/script-injected-async-scripts-considered-harmful/&quot;&gt;an informative post&lt;/a&gt; that goes into great detail about injected &lt;code&gt;async&lt;/code&gt; scripts. Give it a read if you want a deep dive into this topic. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;lazy-loading-with-javascript&quot;&gt;Lazy loading with JavaScript &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-scanner/#lazy-loading-with-javascript&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Lazy loading is a great method of conserving data, one that&#39;s often applied to images. However, sometimes lazy loading is incorrectly applied to images that are &amp;quot;above the fold&amp;quot;, so to speak.&lt;/p&gt;
&lt;p&gt;This introduces potential issues with resource discoverability where the preload scanner is concerned, and can unnecessarily delay how long it takes to discover a reference to an image, download it, decode it, and present it. Let&#39;s take this image markup 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 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;/sand-wasp.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;Sand Wasp&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;384&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;255&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 use of a &lt;code&gt;data-&lt;/code&gt; prefix is a common pattern in JavaScript-powered lazy loaders. When the image is scrolled into the viewport, the lazy loader strips the &lt;code&gt;data-&lt;/code&gt; prefix, meaning that in the preceding example, &lt;code&gt;data-src&lt;/code&gt; becomes &lt;code&gt;src&lt;/code&gt;. This update prompts the browser to fetch the resource.&lt;/p&gt;
&lt;p&gt;This pattern isn&#39;t problematic until it&#39;s applied to images that are in the viewport during startup. Because the preload scanner doesn&#39;t read the &lt;code&gt;data-src&lt;/code&gt; attribute in the same way that it would an &lt;code&gt;src&lt;/code&gt; (or &lt;code&gt;srcset&lt;/code&gt;) attribute, the image reference isn&#39;t discovered earlier. Worse, the image is delayed from loading until &lt;em&gt;after&lt;/em&gt; the lazy loader JavaScript downloads, compiles, and executes.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A WebPageTest network waterfall chart showing how a lazily-loaded image that is in the viewport during startup is necessarily delayed because the browser preload scanner can&amp;#x27;t find the image resource, and only loads when the JavaScript required for lazy loading to work loads. The image is discovered far later than it should be.&quot; decoding=&quot;async&quot; height=&quot;220&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Qu1Ghkpp2K5FUu2etfz7.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Qu1Ghkpp2K5FUu2etfz7.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Qu1Ghkpp2K5FUu2etfz7.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Qu1Ghkpp2K5FUu2etfz7.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Qu1Ghkpp2K5FUu2etfz7.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Qu1Ghkpp2K5FUu2etfz7.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Qu1Ghkpp2K5FUu2etfz7.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Qu1Ghkpp2K5FUu2etfz7.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Qu1Ghkpp2K5FUu2etfz7.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Qu1Ghkpp2K5FUu2etfz7.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Qu1Ghkpp2K5FUu2etfz7.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Qu1Ghkpp2K5FUu2etfz7.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Qu1Ghkpp2K5FUu2etfz7.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Qu1Ghkpp2K5FUu2etfz7.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Qu1Ghkpp2K5FUu2etfz7.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Qu1Ghkpp2K5FUu2etfz7.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Qu1Ghkpp2K5FUu2etfz7.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Qu1Ghkpp2K5FUu2etfz7.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 8:&lt;/strong&gt; A WebPageTest network waterfall chart of &lt;a href=&quot;https://preload-scanner-fights.glitch.me/js-lazy-load-suboptimal.html&quot; rel=&quot;noopener&quot;&gt;a web page&lt;/a&gt; run on Chrome on a mobile device over a simulated 3G connection. The image resource is unnecessarily lazy-loaded, even though it is visible in the viewport during startup. This defeats the preload scanner and causes an unnecessary delay.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Depending on the size of the image—which may depend on the size of the viewport—it may be a candidate element for &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt;. When the preload scanner cannot speculatively fetch the image resource ahead of time—possibly during the point at which the page&#39;s stylesheet(s) block rendering—LCP suffers.&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; For more information on optimizing LCP beyond the scope of this article, read &lt;a href=&quot;https://web.dev/optimize-lcp/&quot;&gt;Optimizing Largest Contentful Paint&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The solution is to change the image markup:&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;/sand-wasp.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;Sand Wasp&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;384&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;255&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 is the optimal pattern for images that are in the viewport during startup, as the preload scanner will discover and fetch the image resource more quickly.&lt;/p&gt;
&lt;figure id=&quot;fig-9&quot;&gt;
  &lt;img alt=&quot;A WebPageTest network waterfall chart depicting an loading scenario for an image in the viewport during startup. The image is not lazily loaded, which means it is not dependent on the script to load, meaning the preload scanner can discover it sooner.&quot; decoding=&quot;async&quot; height=&quot;220&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0pRjNYnUCcbrEHaaTJ3t.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0pRjNYnUCcbrEHaaTJ3t.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0pRjNYnUCcbrEHaaTJ3t.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0pRjNYnUCcbrEHaaTJ3t.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0pRjNYnUCcbrEHaaTJ3t.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0pRjNYnUCcbrEHaaTJ3t.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0pRjNYnUCcbrEHaaTJ3t.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0pRjNYnUCcbrEHaaTJ3t.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0pRjNYnUCcbrEHaaTJ3t.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0pRjNYnUCcbrEHaaTJ3t.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0pRjNYnUCcbrEHaaTJ3t.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0pRjNYnUCcbrEHaaTJ3t.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0pRjNYnUCcbrEHaaTJ3t.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0pRjNYnUCcbrEHaaTJ3t.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0pRjNYnUCcbrEHaaTJ3t.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0pRjNYnUCcbrEHaaTJ3t.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0pRjNYnUCcbrEHaaTJ3t.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/0pRjNYnUCcbrEHaaTJ3t.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 9:&lt;/strong&gt; A WebPageTest network waterfall chart of &lt;a href=&quot;https://preload-scanner-fights.glitch.me/js-lazy-load-optimal.html&quot; rel=&quot;noopener&quot;&gt;a web page&lt;/a&gt; run on Chrome on a mobile device over a simulated 3G connection. The preload scanner discovers the image resource before the CSS and JavaScript begin loading, which gives the browser a head start on loading it.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The result in this simplified example is a 100-millisecond improvement in LCP on a slow connection. This may not seem like a huge improvement, but it is when you consider that the solution is a quick markup fix, and that most web pages are more complex than this set of examples. That means that LCP candidates may have to contend for bandwidth with many other resources, so optimizations like this become increasingly important.&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; Images aren&#39;t the only resource type that can suffer from suboptimal lazy loading patterns. The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/iframe&quot;&gt;&lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; element&lt;/a&gt; can also be affected, and since &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; elements can load many subresources, the impact of performance could be substantially worse. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;css-background-images&quot;&gt;CSS background images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-scanner/#css-background-images&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Remember that the browser preload scanner scans &lt;em&gt;markup&lt;/em&gt;. It doesn&#39;t scan other resource types, such as CSS which may involve fetches for images referenced by the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/background-image&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;background-image&lt;/code&gt; property&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Like HTML, browsers process CSS into its own object model, known as the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/CSS_Object_Model&quot; rel=&quot;noopener&quot;&gt;CSSOM&lt;/a&gt;. If external resources are discovered as the CSSOM is constructed, those resources are requested at the time of discovery, and not by the preload scanner.&lt;/p&gt;
&lt;p&gt;Let&#39;s say your page&#39;s &lt;a href=&quot;https://web.dev/lcp/#what-elements-are-considered&quot;&gt;LCP candidate&lt;/a&gt; is an element with a CSS &lt;code&gt;background-image&lt;/code&gt; property. The following is what happens as resources load:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A WebPageTest network waterfall chart depicting a page with an LCP candidate loaded from CSS using the background-image property. Because the LCP candidate image is in a resource type that the browser preload scanner can&amp;#x27;t examine, the resource is delayed from loading until the CSS is downloaded and processed, delaying the LCP candidate&amp;#x27;s paint time.&quot; decoding=&quot;async&quot; height=&quot;206&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YDBXJlSV1xN4SheYVQUY.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YDBXJlSV1xN4SheYVQUY.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YDBXJlSV1xN4SheYVQUY.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YDBXJlSV1xN4SheYVQUY.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YDBXJlSV1xN4SheYVQUY.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YDBXJlSV1xN4SheYVQUY.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YDBXJlSV1xN4SheYVQUY.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YDBXJlSV1xN4SheYVQUY.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YDBXJlSV1xN4SheYVQUY.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YDBXJlSV1xN4SheYVQUY.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YDBXJlSV1xN4SheYVQUY.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YDBXJlSV1xN4SheYVQUY.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YDBXJlSV1xN4SheYVQUY.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YDBXJlSV1xN4SheYVQUY.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YDBXJlSV1xN4SheYVQUY.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YDBXJlSV1xN4SheYVQUY.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YDBXJlSV1xN4SheYVQUY.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/YDBXJlSV1xN4SheYVQUY.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 10:&lt;/strong&gt; A WebPageTest network waterfall chart of &lt;a href=&quot;https://preload-scanner-fights.glitch.me/css-background-image-no-preload.html&quot; rel=&quot;noopener&quot;&gt;a web page&lt;/a&gt; run on Chrome on a mobile device over a simulated 3G connection. The page&#39;s LCP candidate is an element with a CSS &lt;code&gt;background-image&lt;/code&gt; property (row 3). The image it requests doesn&#39;t begin fetching until the CSS parser finds it.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In this case, the preload scanner isn&#39;t so much defeated as it is uninvolved. Even so, if an LCP candidate on the page is from a &lt;code&gt;background-image&lt;/code&gt; CSS property, you&#39;re going to want to preload that image:&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;!-- Make sure this is in the &amp;lt;head&gt; below any&lt;br /&gt;     stylesheets, so as not to block them from loading --&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;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;lcp-image.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;/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; If your LCP candidate is from a &lt;code&gt;background-image&lt;/code&gt; CSS property, but that image varies based on the viewport size, you&#39;ll need to specify the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/link#attr-imagesrcset&quot;&gt;&lt;code&gt;imagesrcset&lt;/code&gt; attribute&lt;/a&gt; on the &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; element. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;That &lt;code&gt;rel=preload&lt;/code&gt; hint is small, but it helps the browser discover the image sooner than it otherwise would:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A WebPageTest network waterfall chart showing a CSS background image (which is the LCP candidate) loading much sooner due to the use of a rel=preload hint. The LCP time improves by roughly 250 milliseconds.&quot; decoding=&quot;async&quot; height=&quot;206&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/SdBeh352sU0rN5umquSQ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/SdBeh352sU0rN5umquSQ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/SdBeh352sU0rN5umquSQ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/SdBeh352sU0rN5umquSQ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/SdBeh352sU0rN5umquSQ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/SdBeh352sU0rN5umquSQ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/SdBeh352sU0rN5umquSQ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/SdBeh352sU0rN5umquSQ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/SdBeh352sU0rN5umquSQ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/SdBeh352sU0rN5umquSQ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/SdBeh352sU0rN5umquSQ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/SdBeh352sU0rN5umquSQ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/SdBeh352sU0rN5umquSQ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/SdBeh352sU0rN5umquSQ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/SdBeh352sU0rN5umquSQ.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/SdBeh352sU0rN5umquSQ.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/SdBeh352sU0rN5umquSQ.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/SdBeh352sU0rN5umquSQ.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 11:&lt;/strong&gt; A WebPageTest network waterfall chart of &lt;a href=&quot;https://preload-scanner-fights.glitch.me/css-background-image-with-preload.html&quot; rel=&quot;noopener&quot;&gt;a web page&lt;/a&gt; run on Chrome on a mobile device over a simulated 3G connection. The page&#39;s LCP candidate is an element with a CSS &lt;code&gt;background-image&lt;/code&gt; property (row 3). The &lt;code&gt;rel=preload&lt;/code&gt; hint helps the browser to discover the image around 250 milliseconds sooner than without the hint.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;With the &lt;code&gt;rel=preload&lt;/code&gt; hint, the LCP candidate is discovered sooner, lowering the LCP time. While that hint helps fix this issue, the better option may be to assess whether or not your image LCP candidate &lt;em&gt;has&lt;/em&gt; to be loaded from CSS. With an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag, you&#39;ll have more control over loading an image that&#39;s appropriate for the viewport while allowing the preload scanner to discover it.&lt;/p&gt;
&lt;h2 id=&quot;inlining-too-many-resources&quot;&gt;Inlining too many resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-scanner/#inlining-too-many-resources&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Inlining is a practice that places a resource inside of the HTML. You can inline stylesheets in &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; elements, scripts in &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; elements, and virtually any other resource using &lt;a href=&quot;https://developer.mozilla.org/docs/Glossary/Base64&quot; rel=&quot;noopener&quot;&gt;base64 encoding&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Inlining resources can be faster than downloading them because a separate request isn&#39;t issued for the resource. It&#39;s right in the document, and loads instantly. However, there are significant drawbacks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you&#39;re not caching your HTML—and you just can&#39;t if the HTML response is dynamic—the inlined resources are never cached. This affects performance because the inlined resources aren&#39;t reusable.&lt;/li&gt;
&lt;li&gt;Even if you can cache HTML, inlined resources aren&#39;t shared between documents. This reduces caching efficiency compared to external files that can be cached and reused across an entire origin.&lt;/li&gt;
&lt;li&gt;If you inline too much, you delay the preload scanner from discovering resources later in the document, because downloading that extra, inlined, content takes longer.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Take &lt;a href=&quot;https://preload-scanner-fights.glitch.me/inline-nothing.html&quot; rel=&quot;noopener&quot;&gt;this page&lt;/a&gt; as an example. In certain conditions the LCP candidate is the image at the top of the page, and the CSS is in a separate file loaded by a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; element. The page also uses four web fonts which are requested as separate files from the CSS resource.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A WebPageTest network waterfall chart of page with an external CSS file with four fonts referenced in it. The LCP candidate image is discovered by the preload scanner in due course.&quot; decoding=&quot;async&quot; height=&quot;347&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDCAZc9Vkl9phYrZk2vW.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDCAZc9Vkl9phYrZk2vW.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDCAZc9Vkl9phYrZk2vW.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDCAZc9Vkl9phYrZk2vW.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDCAZc9Vkl9phYrZk2vW.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDCAZc9Vkl9phYrZk2vW.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDCAZc9Vkl9phYrZk2vW.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDCAZc9Vkl9phYrZk2vW.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDCAZc9Vkl9phYrZk2vW.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDCAZc9Vkl9phYrZk2vW.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDCAZc9Vkl9phYrZk2vW.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDCAZc9Vkl9phYrZk2vW.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDCAZc9Vkl9phYrZk2vW.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDCAZc9Vkl9phYrZk2vW.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDCAZc9Vkl9phYrZk2vW.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDCAZc9Vkl9phYrZk2vW.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDCAZc9Vkl9phYrZk2vW.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/uDCAZc9Vkl9phYrZk2vW.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 12:&lt;/strong&gt; A WebPageTest network waterfall chart of &lt;a href=&quot;https://preload-scanner-fights.glitch.me/inline-nothing.html&quot; rel=&quot;noopener&quot;&gt;a web page&lt;/a&gt; run on Chrome on a mobile device over a simulated 3G connection. The page&#39;s LCP candidate is an image loaded from an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element, but is discovered by the preload scanner because the CSS and the fonts required for the page load in separate resources, which doesn&#39;t delay the preload scanner from doing its job.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Now what happens if the CSS &lt;em&gt;and&lt;/em&gt; all the fonts are inlined as base64 resources?&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A WebPageTest network waterfall chart of page with an external CSS file with four fonts referenced in it. The preload scanner is delayed significantly from discovering the LCP image .&quot; decoding=&quot;async&quot; height=&quot;297&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/frUPqxpkkouS0eUWWFVc.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/frUPqxpkkouS0eUWWFVc.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/frUPqxpkkouS0eUWWFVc.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/frUPqxpkkouS0eUWWFVc.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/frUPqxpkkouS0eUWWFVc.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/frUPqxpkkouS0eUWWFVc.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/frUPqxpkkouS0eUWWFVc.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/frUPqxpkkouS0eUWWFVc.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/frUPqxpkkouS0eUWWFVc.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/frUPqxpkkouS0eUWWFVc.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/frUPqxpkkouS0eUWWFVc.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/frUPqxpkkouS0eUWWFVc.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/frUPqxpkkouS0eUWWFVc.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/frUPqxpkkouS0eUWWFVc.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/frUPqxpkkouS0eUWWFVc.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/frUPqxpkkouS0eUWWFVc.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/frUPqxpkkouS0eUWWFVc.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/frUPqxpkkouS0eUWWFVc.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 13:&lt;/strong&gt; A WebPageTest network waterfall chart of &lt;a href=&quot;https://preload-scanner-fights.glitch.me/inline-everything.html&quot; rel=&quot;noopener&quot;&gt;a web page&lt;/a&gt; run on Chrome on a mobile device over a simulated 3G connection. The page&#39;s LCP candidate is an image loaded from an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element, but the inlining of the CSS and its four font resources in the `&lt;head&gt;` delays the preload scanner from discovering the image until those resources are fully downloaded.
  &lt;/head&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The impact of inlining yields negative consequences for LCP in this example—and for performance in general. The version of the page that doesn&#39;t inline anything paints the LCP image in about 3.5 seconds. The page that inlines everything doesn&#39;t paint the LCP image until just over 7 seconds.&lt;/p&gt;
&lt;p&gt;There&#39;s more at play here than just the preload scanner. Inlining fonts is not a great strategy because base64 is an inefficient format for binary resources. Another factor at play is that external font resources aren&#39;t downloaded unless they&#39;re determined necessary by the CSSOM. When those fonts are inlined as base64, they&#39;re downloaded whether they&#39;re needed for the current page or not.&lt;/p&gt;
&lt;p&gt;Could a preload improve things here? Sure. You &lt;em&gt;could&lt;/em&gt; preload the LCP image and reduce LCP time, but bloating your potentially uncacheable HTML with inlined resources has other negative performance consequences. &lt;a href=&quot;https://web.dev/fcp/&quot;&gt;First Contentful Paint (FCP)&lt;/a&gt; is also affected by this pattern. In the version of the page where nothing is inlined, FCP is roughly 2.7 seconds. In the version where everything is inlined, FCP is roughly 5.8 seconds.&lt;/p&gt;
&lt;p&gt;Be very careful with inlining stuff into HTML, especially base64-encoded resources. In general it is not recommended, except for very small resources. Inline as little as possible, because inlining too much is playing with fire.&lt;/p&gt;
&lt;h2 id=&quot;rendering-markup-with-client-side-javascript&quot;&gt;Rendering markup with client-side JavaScript &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-scanner/#rendering-markup-with-client-side-javascript&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There&#39;s no doubt about it: &lt;a href=&quot;https://almanac.httparchive.org/en/2021/performance#total-blocking-time-tbt&quot; rel=&quot;noopener&quot;&gt;JavaScript definitely affects page speed&lt;/a&gt;. Not only do developers depend on it to provide interactivity, but there has also been a tendency to rely on it to deliver content itself. This leads to a better developer experience in some ways; but benefits for developers don&#39;t always translate into benefits for users.&lt;/p&gt;
&lt;p&gt;One pattern that can defeat the preload scanner is rendering markup with client-side JavaScript:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A WebPageTest network waterfall showing a basic page with images and text rendered completely on the client in JavaScript. Because the markup is contained within JavaScript, the preload scanner can&amp;#x27;t detect any of the resources. All resources are additionally delayed due to the extra network and processing time that JavaScript frameworks require.&quot; decoding=&quot;async&quot; height=&quot;260&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZhwXAzscucsECuG6XDRl.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZhwXAzscucsECuG6XDRl.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZhwXAzscucsECuG6XDRl.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZhwXAzscucsECuG6XDRl.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZhwXAzscucsECuG6XDRl.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZhwXAzscucsECuG6XDRl.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZhwXAzscucsECuG6XDRl.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZhwXAzscucsECuG6XDRl.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZhwXAzscucsECuG6XDRl.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZhwXAzscucsECuG6XDRl.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZhwXAzscucsECuG6XDRl.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZhwXAzscucsECuG6XDRl.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZhwXAzscucsECuG6XDRl.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZhwXAzscucsECuG6XDRl.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZhwXAzscucsECuG6XDRl.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZhwXAzscucsECuG6XDRl.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZhwXAzscucsECuG6XDRl.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZhwXAzscucsECuG6XDRl.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &lt;strong&gt;Fig. 14:&lt;/strong&gt; A WebPageTest network waterfall chart of &lt;a href=&quot;https://preload-scanner-fights.glitch.me/client-rendered.html&quot; rel=&quot;noopener&quot;&gt;a client-rendered web page&lt;/a&gt; run on Chrome on a mobile device over a simulated 3G connection. Because the content is contained in JavaScript and relies on a framework to render, the image resource in the client-rendered markup is hidden from the preload scanner. The equivalent server-rendered experience is depicted in &lt;a href=&quot;https://web.dev/preload-scanner/#fig-9&quot;&gt;Fig. 9&lt;/a&gt;.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;When markup payloads are contained in and rendered entirely by JavaScript in the browser, any resources in that markup are effectively invisible to the preload scanner. This delays the discovery of important resources, which certainly affects LCP. In the case of these examples, the request for the LCP image is &lt;em&gt;significantly&lt;/em&gt; delayed when compared to the equivalent server-rendered experience that doesn&#39;t require JavaScript to appear.&lt;/p&gt;
&lt;p&gt;This veers a bit from the focus of this article, but the effects of rendering markup on the client go far beyond defeating the preload scanner. For one, introducing JavaScript to power an experience that doesn&#39;t require it introduces unnecessary processing time that can affect &lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Additionally, rendering extremely large amounts of markup on the client is more likely to generate &lt;a href=&quot;https://web.dev/long-tasks-devtools/&quot;&gt;long tasks&lt;/a&gt; compared to the same amount of markup being sent by the server. The reason for this—aside from the extra processing that JavaScript involves—is that browsers stream markup from the server and chunk up rendering in such a way that avoids long tasks. Client-rendered markup, on the other hand, is handled as a single, monolithic task, which may affect page responsiveness metrics such as &lt;a href=&quot;https://web.dev/tbt/&quot;&gt;Total Blocking Time (TBT)&lt;/a&gt; or &lt;a href=&quot;https://web.dev/fid/&quot;&gt;First Input Delay (FID)&lt;/a&gt; in addition to INP.&lt;/p&gt;
&lt;p&gt;The remedy for this scenario depends on the answer to this question: &lt;strong&gt;Is there a reason why your page&#39;s markup can&#39;t be provided by the server as opposed to being rendered on the client?&lt;/strong&gt; If the answer to this is &amp;quot;no&amp;quot;, server-side rendering (SSR) or statically generated markup should be considered where possible, as it will help the preload scanner to discover and opportunistically fetch important resources ahead of time.&lt;/p&gt;
&lt;p&gt;If your page &lt;em&gt;does&lt;/em&gt; need JavaScript to attach functionality to some parts of your page markup, you can still do so with SSR, either with vanilla JavaScript, or &lt;a href=&quot;https://www.patterns.dev/posts/progressive-hydration/&quot; rel=&quot;noopener&quot;&gt;hydration&lt;/a&gt; to get the best of both worlds.&lt;/p&gt;
&lt;h2 id=&quot;help-the-preload-scanner-help-you&quot;&gt;Help the preload scanner help you &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-scanner/#help-the-preload-scanner-help-you&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The preload scanner is a highly effective browser optimization that helps pages load faster during startup. By avoiding patterns which defeat its ability to discover important resources ahead of time, you&#39;re not just making development simpler for yourself, you&#39;re creating better user experiences that will deliver better results in many metrics, including some &lt;a href=&quot;https://web.dev/vitals/&quot;&gt;web vitals&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To recap, here&#39;s the following things you&#39;ll want to take away from this post:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The browser preload scanner is a secondary HTML parser that scans ahead of the primary one if it&#39;s blocked to opportunistically discover resources it can fetch sooner.&lt;/li&gt;
&lt;li&gt;Resources that aren&#39;t present in markup provided by the server on the initial navigation request can&#39;t be discovered by the preload scanner. Ways the preload scanner can be defeated may include (but are not limited to):
&lt;ul&gt;
&lt;li&gt;Injecting resources into the DOM with JavaScript, be they scripts, images, stylesheets, or anything else that would be better off in the initial markup payload from the server.&lt;/li&gt;
&lt;li&gt;Lazy loading above-the-fold images or iframes using a JavaScript solution.&lt;/li&gt;
&lt;li&gt;Rendering markup on the client that may contain references to document subresources using JavaScript.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The preload scanner only scans HTML. It does not examine the contents of other resources—particularly CSS—that may include references to important assets, including LCP candidates.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If, for whatever reason, you &lt;em&gt;can&#39;t&lt;/em&gt; avoid a pattern that negatively affects the preload scanner&#39;s ability to speed up loading performance, consider the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Link_types/preload&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;rel=preload&lt;/code&gt;&lt;/a&gt; resource hint. If you &lt;em&gt;do&lt;/em&gt; use &lt;code&gt;rel=preload&lt;/code&gt;, test in lab tools to ensure that it&#39;s giving you the desired effect. Finally, don&#39;t preload too many resources, because when you prioritize everything, nothing will be.&lt;/p&gt;
&lt;h2 id=&quot;resources&quot;&gt;Resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-scanner/#resources&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.igvita.com/2014/05/20/script-injected-async-scripts-considered-harmful/&quot; rel=&quot;noopener&quot;&gt;Script-injected &amp;quot;async scripts&amp;quot; considered harmful&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://andydavies.me/blog/2013/10/22/how-the-browser-pre-loader-makes-pages-load-faster/&quot; rel=&quot;noopener&quot;&gt;How the Browser Pre-loader Makes Pages Load Faster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/preload-critical-assets/&quot;&gt;Preload critical assets to improve loading speed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/preconnect-and-dns-prefetch/&quot;&gt;Establish network connections early to improve perceived page speed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/optimize-lcp/&quot;&gt;Optimizing Largest Contentful Paint&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Hero image from &lt;a href=&quot;https://unsplash.com/photos/oXlXu2qukGE&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;, by &lt;a href=&quot;https://unsplash.com/@afgprogrammer&quot; rel=&quot;noopener&quot;&gt;Mohammad Rahmani
&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Interaction to Next Paint (INP)</title>
    <link href="https://web.dev/inp/"/>
    <updated>2022-05-06T00:00:00Z</updated>
    <id>https://web.dev/inp/</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 96, 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;
      96
    &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 96, 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;
96
&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/PerformanceEventTiming/interactionId#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; Interaction to Next Paint (INP) is a &lt;a href=&quot;https://web.dev/vitals/#pending&quot;&gt;pending&lt;/a&gt; Core Web Vital metric that will &lt;a href=&quot;https://web.dev/inp-cwv/&quot;&gt;replace First Input Delay (FID)&lt;/a&gt; in March 2024. INP assesses responsiveness using data from the Event Timing API. When an interaction causes a page to become unresponsive, that is a poor user experience. INP observes the latency of all interactions a user has made with the page, and reports a single value which all (or nearly all) interactions were below. A low INP means the page was consistently able to respond quickly to all—or the vast majority—of user interactions. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Chrome usage data shows that 90% of a user&#39;s time on a page is spent &lt;em&gt;after&lt;/em&gt; it loads, Thus, careful measurement of responsiveness &lt;em&gt;throughout&lt;/em&gt; the page lifecycle is important. This is what the INP metric assesses.&lt;/p&gt;
&lt;p&gt;Good responsiveness means that a page responds quickly to interactions made with it. When a page responds to an interaction, the result is &lt;em&gt;visual feedback&lt;/em&gt;, which is presented by the browser in the next frame the browser presents. Visual feedback tells you if, for example, an item you add to an online shopping cart is indeed being added, whether a mobile navigation menu has opened, if a login form&#39;s contents are being authenticated by the server, and so forth.&lt;/p&gt;
&lt;p&gt;Some interactions will naturally take longer than others, but for especially complex interactions, it&#39;s important to quickly present some initial visual feedback as a cue to the user that something is &lt;em&gt;happening&lt;/em&gt;.  The time until the next paint is the earliest opportunity to do this. Therefore, the intent of INP is not to measure all the eventual effects of the interaction (such as network fetches and UI updates from other asynchronous operations), but the time in which the &lt;em&gt;next&lt;/em&gt; paint is being blocked. By delaying visual feedback, you may be giving users the impression that the page is not responding to their actions.&lt;/p&gt;
&lt;p&gt;The goal of INP is to ensure the time from when a user initiates an interaction until the next frame is painted is as short as possible, for all or most interactions the user makes.&lt;/p&gt;
&lt;p&gt;In the following video, the example on the right gives immediate visual feedback that an accordion is opening. It also demonstrates how poor responsiveness can cause multiple unintended responses to input because the user thinks the experience is broken.&lt;/p&gt;
&lt;style&gt;
  #responsiveness-video {
    height: auto;
    aspect-ratio: 1445 / 370;
  }
&lt;/style&gt;
&lt;figure&gt;
  &lt;video autoplay=&quot;&quot; height=&quot;370&quot; id=&quot;responsiveness-video&quot; loop=&quot;&quot; muted=&quot;&quot; playsinline=&quot;&quot; width=&quot;1445&quot; style=&quot;--vid-width: 1445; --vid-height: 370&quot;&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WSmcjiQC4lyLxGoES4dd.mp4&quot; type=&quot;video/mp4&quot; /&gt;    &lt;/video&gt;
  &lt;figcaption&gt;
    An example of poor versus good responsiveness. At left, long tasks block the accordion from opening. This causes the user to click multiple times, thinking the experience is broken. When the main thread catches up, it processes the delayed inputs, resulting in the accordion opening and closing unexpectedly.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This article explains how INP works, how to measure it, and points to resources for improving it.&lt;/p&gt;
&lt;h2 id=&quot;what-is-inp&quot;&gt;What is INP? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/inp/#what-is-inp&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;INP is a metric that assesses a page&#39;s overall responsiveness to user interactions by observing the latency of all click, tap, and keyboard interactions that occur throughout the lifespan of a user&#39;s visit to a page. The final INP value is the longest interaction observed, ignoring outliers.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;
  A note on how INP is calculated
&lt;/summary&gt;
&lt;p&gt;As stated above, INP is calculated by observing all the interactions made with a page. For most sites the interaction with the worst latency is reported as INP. However, for pages with large numbers of interactions, random hiccups can result in an unusually high interaction on an otherwise responsive site. The more interactions, the more likely this is to happen. To counter this, and give a better measure of the actual responsiveness for those types of pages, we ignore one highest interaction for every 50 interactions. The vast majority of page experiences do not have over 50 interactions so will report the worst interaction. The 75th percentile of all the page views is then reported as usual, which further removes outliers to give a value that the vast majority of users experience or better.&lt;/p&gt;
&lt;/details&gt;
&lt;p&gt;An &lt;em&gt;interaction&lt;/em&gt; is a group of event handlers that fire during the same logical user gesture. For example, &amp;quot;tap&amp;quot; interactions on a touchscreen device include multiple events, such as &lt;code&gt;pointerup&lt;/code&gt;, &lt;code&gt;pointerdown&lt;/code&gt;, and &lt;code&gt;click&lt;/code&gt;. An interaction can be driven by JavaScript, CSS, built-in browser controls (such as form elements), or a combination thereof.&lt;/p&gt;
&lt;p&gt;An interaction&#39;s latency consists of the single longest &lt;a href=&quot;https://w3c.github.io/event-timing/#ref-for-dom-performanceentry-duration%E2%91%A1:~:text=The%20Event%20Timing%20API%20exposes%20a%20duration%20value%2C%20which%20is%20meant%20to%20be%20the%20time%20from%20when%20user%20interaction%20occurs%20(estimated%20via%20the%20Event%27s%20timeStamp)%20to%20the%20next%20time%20the%20rendering%20of%20the%20Event%27s%20relevant%20global%20object%27s%20associated%20Document%E2%80%99s%20is%20updated&quot; rel=&quot;noopener&quot;&gt;duration&lt;/a&gt; of a group of event handlers that drives the interaction, from the time the user begins the interaction to the moment the next frame is presented with visual feedback.&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; For more details on how INP is measured, read the &lt;a href=&quot;https://web.dev/inp/#whats-in-an-interaction&quot;&gt;&amp;quot;What&#39;s in an interaction?&amp;quot;&lt;/a&gt; section. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;what-is-a-good-inp-score&quot;&gt;What is a good INP score? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/inp/#what-is-a-good-inp-score&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Pinning labels such as &amp;quot;good&amp;quot; or &amp;quot;poor&amp;quot; on a responsiveness metric is difficult. On one hand, you want to encourage development practices that prioritize good responsiveness. On the other hand, you must account for the fact that there&#39;s considerable variability in the capabilities of devices people use to set achievable development expectations.&lt;/p&gt;
&lt;p&gt;To ensure you&#39;re delivering user experiences with good responsiveness, a good threshold to measure is the &lt;strong&gt;75th percentile&lt;/strong&gt; of page loads recorded in the field, segmented across mobile and desktop devices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An INP below or at &lt;strong&gt;200 milliseconds&lt;/strong&gt; means that your page has &lt;strong&gt;good responsiveness&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;An INP above &lt;strong&gt;200 milliseconds&lt;/strong&gt; and below or at &lt;strong&gt;500 milliseconds&lt;/strong&gt; means that your page&#39;s responsiveness &lt;strong&gt;needs improvement&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;An INP above &lt;strong&gt;500 milliseconds&lt;/strong&gt; means that your page has &lt;strong&gt;poor responsiveness&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;style&gt;
  .inp-mobile {
    display: inline;
  }

  .inp-desktop {
    display: none;
  }

  @media screen and (min-width: 640px) {
    .inp-mobile {
      display: none;
    }

    .inp-desktop {
      display: inline;
    }
  }
&lt;/style&gt;
&lt;figure&gt;
  &lt;svg title=&quot;A diagram of the INP thresholds. An INP at or below 200 milliseconds is considered good. Between 200 and 500 milliseconds suggests a page&#39;s responsiveness needs improvement. Anything over 500 milliseconds means that a page&#39;s responsiveness is poor.&quot; class=&quot;inp-mobile&quot; version=&quot;1.1&quot; id=&quot;Layer_1&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; x=&quot;0&quot; y=&quot;0&quot; viewBox=&quot;0 0 296.6 220.2&quot; style=&quot;enable-background:new 0 0 296.6 220.2&quot; xml:space=&quot;preserve&quot;&gt;&lt;style&gt;.st0{fill:#2979FF} .st1{fill-rule:evenodd;clip-rule:evenodd;fill:#0CCE6B} .st2{fill:#191919} .st3{fill-rule:evenodd;clip-rule:evenodd;fill:#FFA400} .st4{fill-rule:evenodd;clip-rule:evenodd;fill:#FF4E42} @media screen and (prefers-color-scheme: light){.st2{fill:#191919}} [data-user-theme=light] .st2 {fill:#191919} @media screen and (prefers-color-scheme: dark){.st2{fill:#fff}} [data-user-theme=dark] .st2{fill:#fff}&lt;/style&gt;&lt;path class=&quot;st0&quot; d=&quot;M83.3 63V0h11.9v63H83.3zm26.3 0V0h13.8l25.3 42.2h.7l-.7-12.1V0h11.8v63H148l-26.8-44.6h-.7l.7 12.1V63h-11.6zm65.4 0V0h22.2c4.1 0 7.7.8 10.9 2.5s5.8 4 7.7 7 2.9 6.4 2.9 10.4c0 3.9-1 7.4-2.9 10.4s-4.5 5.4-7.7 7c-3.2 1.7-6.9 2.5-10.9 2.5h-15.6V28.6h15.9c2.1 0 3.8-.4 5.2-1.2s2.5-1.9 3.2-3.2c.7-1.3 1.1-2.8 1.1-4.3s-.4-2.9-1.1-4.2c-.7-1.3-1.8-2.3-3.2-3.2s-3.1-1.2-5.2-1.2h-10.7V63H175z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st1&quot; d=&quot;M0 137.1h96v38.4H0v-38.4z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st2&quot; d=&quot;M34 161c-.6 0-1.2-.1-1.8-.3-.5-.2-1-.5-1.4-.9-.4-.4-.7-.9-1-1.4-.2-.6-.3-1.1-.3-1.8 0-.6.1-1.2.3-1.8.2-.6.6-1 1-1.4.4-.4.9-.7 1.4-.9.5-.2 1.1-.3 1.8-.3s1.3.1 1.8.3c.6.2 1 .6 1.4 1l-.9.9c-.2-.2-.4-.4-.6-.5-.2-.1-.5-.3-.8-.3-.3-.1-.6-.1-.9-.1-.4 0-.8.1-1.2.2-.4.1-.7.4-1 .6-.3.3-.5.6-.7 1-.2.4-.2.8-.2 1.3s.1.9.2 1.3c.2.4.4.7.7 1 .3.3.6.5 1 .7.4.1.8.2 1.2.2.4 0 .8-.1 1.1-.2.3-.1.6-.3.9-.5.3-.2.5-.5.6-.8.2-.3.3-.6.3-1H34v-1.2h4.2v.7c0 .6-.1 1.2-.3 1.7-.2.5-.5.9-.9 1.3s-.8.6-1.3.8c-.5.3-1.1.4-1.7.4zm9.5 0c-.6 0-1.2-.1-1.8-.3-.5-.2-1-.5-1.4-1-.4-.4-.7-.9-.9-1.4-.2-.5-.3-1.1-.3-1.8 0-.6.1-1.2.3-1.8.2-.5.5-1 .9-1.4.4-.4.9-.7 1.4-1 .5-.2 1.1-.3 1.8-.3.6 0 1.2.1 1.8.3.5.2 1 .6 1.4 1 .4.4.7.9.9 1.4.2.5.3 1.1.3 1.8 0 .6-.1 1.2-.3 1.8-.2.5-.5 1-.9 1.4-.4.4-.9.7-1.4 1-.5.2-1.1.3-1.8.3zm0-1.2c.6 0 1.1-.1 1.6-.4.5-.3.8-.7 1.1-1.1.3-.5.4-1.1.4-1.7 0-.6-.1-1.2-.4-1.7-.3-.5-.7-.9-1.1-1.1-.5-.3-1-.4-1.6-.4-.6 0-1.1.1-1.6.4-.5.3-.8.7-1.1 1.1-.3.5-.4 1-.4 1.7 0 .6.1 1.2.4 1.7.3.5.7.9 1.1 1.1.5.3 1 .4 1.6.4zm9.9 1.2c-.6 0-1.2-.1-1.8-.3-.5-.2-1-.5-1.4-1-.4-.4-.7-.9-.9-1.4-.2-.5-.3-1.1-.3-1.8 0-.6.1-1.2.3-1.8.2-.5.5-1 .9-1.4.4-.4.9-.7 1.4-1 .5-.2 1.1-.3 1.8-.3.6 0 1.2.1 1.8.3.5.2 1 .6 1.4 1 .4.4.7.9.9 1.4.2.5.3 1.1.3 1.8 0 .6-.1 1.2-.3 1.8-.2.5-.5 1-.9 1.4-.4.4-.9.7-1.4 1-.6.2-1.2.3-1.8.3zm0-1.2c.6 0 1.1-.1 1.6-.4.5-.3.8-.7 1.1-1.1.3-.5.4-1.1.4-1.7 0-.6-.1-1.2-.4-1.7-.3-.5-.7-.9-1.1-1.1-.5-.3-1-.4-1.6-.4-.6 0-1.1.1-1.6.4-.5.3-.8.7-1.1 1.1-.3.5-.4 1-.4 1.7 0 .6.1 1.2.4 1.7.3.5.7.9 1.1 1.1.5.3 1 .4 1.6.4zm6 1.1v-8.6h2.8c.9 0 1.7.2 2.3.5.7.4 1.2.9 1.5 1.5.4.6.5 1.4.5 2.2 0 .8-.2 1.6-.5 2.2-.4.6-.9 1.2-1.5 1.5-.6.4-1.4.5-2.3.5h-2.8zm1.3-1.3h1.4c.6 0 1.2-.1 1.7-.4.5-.3.8-.6 1.1-1 .2-.5.4-1 .4-1.6 0-.6-.1-1.2-.4-1.6-.2-.5-.6-.8-1.1-1-.5-.3-1-.4-1.7-.4h-1.4v6z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st3&quot; d=&quot;M96 137.1h105.6v38.4H96v-38.4z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st2&quot; d=&quot;M130.8 153.9v-8.6h1.6l3.9 6.3h.1l-.1-1.7v-4.7h1.3v8.6h-1.4l-4.1-6.6h-.1l.1 1.7v5h-1.3zm8.7 0v-8.6h5.2v1.2h-3.9v6.1h3.9v1.2h-5.2zm.7-3.7V149h4.1v1.2h-4.1zm6.2 3.7v-8.6h5.2v1.2h-3.9v6.1h3.9v1.2h-5.2zm.7-3.7V149h4.1v1.2h-4.1zm6.1 3.7v-8.6h2.8c.9 0 1.7.2 2.3.5.7.4 1.2.9 1.5 1.5.4.6.5 1.4.5 2.2s-.2 1.6-.5 2.2c-.4.6-.9 1.2-1.5 1.5-.6.4-1.4.5-2.3.5h-2.8zm1.3-1.3h1.4c.6 0 1.2-.1 1.7-.4.5-.2.8-.6 1.1-1 .2-.5.4-1 .4-1.6 0-.6-.1-1.2-.4-1.6-.2-.5-.6-.8-1.1-1-.5-.2-1-.4-1.7-.4h-1.4v6zm9.9 1.4c-.5 0-.9-.1-1.3-.3-.4-.2-.8-.4-1.1-.8-.3-.4-.5-.8-.7-1.3l1.2-.5c.1.5.3.9.7 1.2.3.3.7.5 1.2.5.3 0 .5 0 .8-.1.2-.1.4-.2.6-.4s.2-.4.2-.7c0-.3-.1-.5-.2-.7-.1-.2-.3-.3-.6-.5s-.6-.3-1-.5l-.5-.2c-.2-.1-.5-.2-.7-.3-.2-.1-.5-.3-.7-.5-.2-.2-.4-.4-.5-.7-.1-.3-.2-.6-.2-.9 0-.4.1-.8.3-1.2.2-.4.5-.6.9-.8.4-.2.9-.3 1.4-.3.6 0 1 .1 1.4.3.4.2.7.4.9.7.2.3.4.5.4.8l-1.2.5c0-.2-.1-.3-.2-.5s-.3-.3-.5-.4c-.2-.1-.4-.2-.8-.2-.2 0-.5.1-.7.2-.2.1-.4.2-.5.4-.1.2-.2.3-.2.6s.1.6.4.8c.3.2.6.4 1.1.5l.6.2c.3.1.6.2.8.3.3.1.5.3.7.5.2.2.4.4.5.7.1.3.2.6.2 1s-.1.8-.3 1.2c-.2.3-.4.6-.7.8-.3.2-.6.3-.9.4s-.5.2-.8.2zM107 167.9v-8.6h1.3v8.6H107zm3.2 0v-8.6h1.8l2.5 6.5h.1l2.5-6.5h1.8v8.6h-1.3V163l.1-1.5h-.1l-2.5 6.4h-1l-2.5-6.4h-.1l.1 1.5v4.9h-1.4zm10.6 0v-8.6h3c.5 0 1 .1 1.4.3.4.2.8.5 1 .9.3.4.4.9.4 1.4 0 .5-.1 1-.4 1.4-.2.4-.6.7-1 .9-.4.2-.9.3-1.4.3h-2.2v-1.2h2.3c.3 0 .6-.1.8-.2.2-.1.4-.3.5-.5.1-.2.2-.4.2-.7 0-.2-.1-.4-.2-.7-.1-.2-.3-.4-.5-.5-.2-.1-.5-.2-.8-.2h-1.7v7.3h-1.4zm7.2 0v-8.6h3c.5 0 1 .1 1.4.3.4.2.7.5 1 .9.2.4.4.8.4 1.4 0 .3-.1.7-.2 1s-.3.6-.6.8c-.3.2-.6.4-1 .6-.4.1-.8.2-1.2.2h-2.1v-1.2h2.3c.3 0 .5-.1.7-.2.2-.1.4-.3.5-.5.1-.2.2-.5.2-.7 0-.2-.1-.5-.2-.7-.1-.2-.3-.4-.5-.5-.2-.1-.5-.2-.8-.2h-1.7v7.4H128zm2-4h1.5l2.7 3.9v.1h-1.5l-2.7-4zm9.3 4.1c-.6 0-1.2-.1-1.8-.3-.5-.2-1-.5-1.4-1-.4-.4-.7-.9-.9-1.4-.2-.5-.3-1.1-.3-1.8s.1-1.2.3-1.8c.2-.5.5-1 .9-1.4.4-.4.9-.7 1.4-1 .5-.2 1.1-.3 1.8-.3.6 0 1.2.1 1.8.3.5.2 1 .6 1.4 1 .4.4.7.9.9 1.4.2.5.3 1.1.3 1.8s-.1 1.2-.3 1.8c-.2.5-.5 1-.9 1.4-.4.4-.9.7-1.4 1-.6.2-1.2.3-1.8.3zm0-1.2c.6 0 1.1-.1 1.6-.4.5-.3.8-.7 1.1-1.1.3-.5.4-1.1.4-1.7 0-.6-.1-1.2-.4-1.7-.3-.5-.7-.9-1.1-1.1-.5-.3-1-.4-1.6-.4-.6 0-1.1.1-1.6.4-.5.3-.8.7-1.1 1.1-.3.5-.4 1-.4 1.7 0 .6.1 1.2.4 1.7.3.5.7.9 1.1 1.1.5.3 1 .4 1.6.4zm7.9 1.1-3-8.6h1.4l1.9 5.7.3 1h.1l.3-1 2-5.7h1.4l-3.1 8.6h-1.3zm5.6 0v-8.6h5.2v1.2h-3.9v6.1h3.9v1.2h-5.2zm.7-3.7V163h4.1v1.2h-4.1zm6.2 3.7v-8.6h1.8l2.5 6.5h.1l2.5-6.5h1.8v8.6H167V163l.1-1.5h-.1l-2.5 6.4h-1l-2.5-6.4h-.1l.1 1.5v4.9h-1.3zm10.6 0v-8.6h5.2v1.2h-3.9v6.1h3.9v1.2h-5.2zm.7-3.7V163h4.1v1.2H171zm6.1 3.7v-8.6h1.6l3.9 6.3h.1l-.1-1.7v-4.7h1.3v8.6h-1.4l-4.1-6.6h-.1l.1 1.7v5h-1.3zm10.5 0V160h1.3v7.9h-1.3zm-2.4-7.4v-1.2h6.1v1.2h-6.1z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st4&quot; d=&quot;M200.6 137.1h96v38.4h-96v-38.4z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st2&quot; d=&quot;M232.5 160.9v-8.6h3c.5 0 1 .1 1.4.3.4.2.8.5 1 .9.3.4.4.9.4 1.4 0 .5-.1 1-.4 1.4-.2.4-.6.7-1 .9-.4.2-.9.3-1.4.3h-2.2v-1.2h2.3c.3 0 .6-.1.8-.2.2-.1.4-.3.5-.5.1-.2.2-.4.2-.7 0-.2-.1-.4-.2-.7-.1-.2-.3-.4-.5-.5-.2-.1-.5-.2-.8-.2h-1.7v7.3h-1.4zm11 .1c-.6 0-1.2-.1-1.8-.3-.5-.2-1-.5-1.4-1-.4-.4-.7-.9-.9-1.4-.2-.5-.3-1.1-.3-1.8s.1-1.2.3-1.8c.2-.5.5-1 .9-1.4.4-.4.9-.7 1.4-1 .5-.2 1.1-.3 1.8-.3.6 0 1.2.1 1.8.3.5.2 1 .6 1.4 1 .4.4.7.9.9 1.4.2.5.3 1.1.3 1.8s-.1 1.2-.3 1.8c-.2.5-.5 1-.9 1.4-.4.4-.9.7-1.4 1-.5.2-1.1.3-1.8.3zm0-1.2c.6 0 1.1-.1 1.6-.4.5-.3.8-.7 1.1-1.1.3-.5.4-1.1.4-1.7 0-.6-.1-1.2-.4-1.7-.3-.5-.7-.9-1.1-1.1-.5-.3-1-.4-1.6-.4-.6 0-1.1.1-1.6.4-.5.3-.8.7-1.1 1.1-.3.5-.4 1-.4 1.7 0 .6.1 1.2.4 1.7.3.5.7.9 1.1 1.1.6.3 1.1.4 1.6.4zm9.9 1.2c-.6 0-1.2-.1-1.8-.3-.5-.2-1-.5-1.4-1-.4-.4-.7-.9-.9-1.4-.2-.5-.3-1.1-.3-1.8s.1-1.2.3-1.8c.2-.5.5-1 .9-1.4.4-.4.9-.7 1.4-1 .5-.2 1.1-.3 1.8-.3.6 0 1.2.1 1.8.3.5.2 1 .6 1.4 1 .4.4.7.9.9 1.4.2.5.3 1.1.3 1.8s-.1 1.2-.3 1.8c-.2.5-.5 1-.9 1.4-.4.4-.9.7-1.4 1-.6.2-1.1.3-1.8.3zm0-1.2c.6 0 1.1-.1 1.6-.4.5-.3.8-.7 1.1-1.1.3-.5.4-1.1.4-1.7 0-.6-.1-1.2-.4-1.7-.3-.5-.7-.9-1.1-1.1-.5-.3-1-.4-1.6-.4-.6 0-1.1.1-1.6.4-.5.3-.8.7-1.1 1.1-.3.5-.4 1-.4 1.7 0 .6.1 1.2.4 1.7.3.5.7.9 1.1 1.1.5.3 1 .4 1.6.4zm6 1.1v-8.6h3c.5 0 1 .1 1.4.3.4.2.7.5 1 .9.2.4.4.8.4 1.4 0 .3-.1.7-.2 1s-.3.6-.6.8c-.3.2-.6.4-1 .6-.4.1-.8.2-1.2.2h-2.1v-1.2h2.3c.3 0 .5-.1.7-.2.2-.1.4-.3.5-.5.1-.2.2-.5.2-.7 0-.2-.1-.5-.2-.7-.1-.2-.3-.4-.5-.5-.2-.1-.5-.2-.8-.2h-1.7v7.4h-1.2zm2-4h1.5l2.7 3.9v.1H264l-2.6-4zM178.1 219.9c-.5 0-1-.1-1.5-.3-.5-.2-1-.5-1.4-1-.4-.4-.7-1-.8-1.7l1.5-.6c.1.6.4 1 .8 1.4.4.4.9.5 1.4.5.6 0 1.1-.2 1.5-.6.4-.4.6-.9.6-1.5s-.2-1.1-.6-1.5c-.4-.4-.9-.6-1.5-.6-.4 0-.7.1-1 .2-.3.1-.5.4-.7.6l-1.7-.8.7-5.5h5.9v1.6h-4.5l-.4 2.9h.1c.2-.2.5-.3.8-.5.3-.1.7-.2 1.2-.2.6 0 1.2.2 1.7.5s1 .7 1.3 1.3c.3.5.5 1.2.5 1.9s-.2 1.3-.5 1.9c-.3.6-.8 1-1.3 1.3-.7.5-1.4.7-2.1.7zm9.9 0c-.7 0-1.3-.1-1.9-.4-.5-.3-1-.7-1.4-1.2-.4-.5-.7-1.1-.9-1.8-.2-.7-.3-1.4-.3-2.2 0-.8.1-1.6.3-2.2.2-.7.5-1.3.9-1.8s.9-.9 1.4-1.2c.6-.3 1.2-.5 1.9-.5s1.3.2 1.9.5c.6.3 1 .7 1.4 1.2.4.5.7 1.1.9 1.8.2.7.3 1.4.3 2.2 0 .8-.1 1.6-.3 2.2-.2.7-.5 1.3-.9 1.8s-.9.9-1.4 1.2c-.6.2-1.2.4-1.9.4zm.1-1.7c.6 0 1-.2 1.5-.5.4-.4.7-.9 1-1.5.2-.6.3-1.3.3-2.1s-.1-1.5-.3-2.1c-.2-.6-.5-1.1-1-1.5-.4-.4-.9-.5-1.5-.5s-1.1.2-1.5.5c-.4.4-.7.8-.9 1.5-.2.6-.3 1.3-.3 2.1s.1 1.5.3 2.1c.2.6.5 1.1.9 1.5.4.3.9.5 1.5.5zm10.5 1.7c-.7 0-1.3-.1-1.9-.4-.5-.3-1-.7-1.4-1.2-.4-.5-.7-1.1-.9-1.8-.2-.7-.3-1.4-.3-2.2 0-.8.1-1.6.3-2.2.2-.7.5-1.3.9-1.8s.9-.9 1.4-1.2c.6-.3 1.2-.5 1.9-.5s1.3.2 1.9.5c.6.3 1 .7 1.4 1.2.4.5.7 1.1.9 1.8.2.7.3 1.4.3 2.2 0 .8-.1 1.6-.3 2.2-.2.7-.5 1.3-.9 1.8s-.9.9-1.4 1.2c-.6.2-1.2.4-1.9.4zm0-1.7c.6 0 1-.2 1.5-.5.4-.4.7-.9 1-1.5.2-.6.3-1.3.3-2.1s-.1-1.5-.3-2.1c-.2-.6-.5-1.1-1-1.5-.4-.4-.9-.5-1.5-.5s-1.1.2-1.5.5c-.4.4-.7.8-.9 1.5-.2.6-.3 1.3-.3 2.1s.1 1.5.3 2.1c.2.6.5 1.1.9 1.5.4.3.9.5 1.5.5zm10 1.4v-8.2h1.6v1.1h.1c.2-.3.4-.5.6-.7.3-.2.6-.4.9-.5.3-.1.7-.2 1-.2.6 0 1.1.1 1.5.4.4.3.7.7.9 1.1.3-.4.6-.8 1.1-1.1.5-.3 1-.5 1.7-.5 1 0 1.7.3 2.1.9.5.6.7 1.4.7 2.3v5.2h-1.7v-4.9c0-.7-.1-1.1-.4-1.4-.3-.3-.7-.5-1.2-.5-.4 0-.7.1-1 .3-.3.2-.5.5-.7.9-.2.4-.2.8-.2 1.2v4.4h-1.7v-4.9c0-.7-.1-1.1-.4-1.4-.3-.3-.7-.5-1.2-.5-.4 0-.7.1-1 .3-.3.2-.5.5-.7.9-.2.4-.2.8-.2 1.2v4.4h-1.8zm17.2.3c-.6 0-1.2-.1-1.7-.3-.5-.2-.9-.5-1.2-.8-.3-.3-.5-.7-.7-1.1l1.5-.7c.2.4.5.8.8 1 .4.2.8.3 1.2.3.4 0 .8-.1 1.1-.2.3-.2.5-.4.5-.8 0-.2-.1-.4-.2-.6-.1-.1-.3-.3-.6-.4-.2-.1-.5-.2-.8-.2l-1-.2c-.4-.1-.8-.3-1.1-.5-.3-.2-.6-.5-.8-.8-.2-.3-.3-.7-.3-1.1 0-.5.1-.9.4-1.3.3-.4.7-.6 1.1-.8.5-.2 1-.3 1.6-.3.5 0 1 .1 1.4.2.4.1.8.3 1.1.6.3.3.6.6.8 1l-1.5.7c-.2-.4-.4-.6-.7-.8-.3-.1-.6-.2-1-.2s-.7.1-1 .2c-.3.2-.4.4-.4.6 0 .3.1.5.4.7.2.2.5.3.9.4l1.2.3c.8.2 1.4.5 1.8.9.4.4.6.9.6 1.5 0 .5-.2 1-.5 1.4-.3.4-.7.7-1.2.9-.6.2-1.1.4-1.7.4zM69 220v-1.6s.2-.1.4-.4c.2-.2.5-.5.8-.9l1.1-1.1 1-1c.3-.3.5-.6.7-.8.3-.3.5-.6.7-.8.2-.2.3-.5.4-.7.1-.2.1-.5.1-.8 0-.3-.1-.5-.2-.8-.1-.3-.3-.4-.6-.6-.3-.1-.6-.2-1-.2s-.7.1-1 .2c-.3.1-.5.3-.6.6-.1.2-.3.4-.3.7l-1.5-.6c.1-.3.2-.5.4-.8.2-.3.4-.5.7-.8.3-.3.6-.5 1-.6.4-.2.9-.3 1.4-.2.7 0 1.3.2 1.9.5.5.3.9.7 1.2 1.2.3.5.4 1 .4 1.6 0 .5-.1.9-.2 1.3-.2.4-.4.8-.7 1.2-.3.4-.5.7-.8 1l-.5.5c-.2.2-.4.5-.7.7l-.7.7-.6.6-.4.4h4.8v1.6H69zm13.2.2c-.7 0-1.3-.1-1.9-.4-.5-.3-1-.7-1.4-1.2-.4-.5-.7-1.1-.9-1.8-.2-.7-.3-1.4-.3-2.2 0-.8.1-1.6.3-2.2.2-.7.5-1.3.9-1.8s.9-.9 1.4-1.2c.6-.3 1.2-.5 1.9-.5s1.3.2 1.9.5c.6.3 1 .7 1.4 1.2.4.5.7 1.1.9 1.8.2.7.3 1.4.3 2.2 0 .8-.1 1.6-.3 2.2-.2.7-.5 1.3-.9 1.8s-.9.9-1.4 1.2c-.6.3-1.2.4-1.9.4zm0-1.6c.6 0 1-.2 1.5-.5.4-.4.7-.9 1-1.5.2-.6.3-1.3.3-2.1s-.1-1.5-.3-2.1c-.2-.6-.5-1.1-1-1.5-.4-.4-.9-.5-1.5-.5s-1.1.2-1.5.5c-.4.4-.7.8-.9 1.5-.2.6-.3 1.3-.3 2.1s.1 1.5.3 2.1c.2.6.5 1.1.9 1.5.4.3.9.5 1.5.5zm10.5 1.6c-.7 0-1.3-.1-1.9-.4-.5-.3-1-.7-1.4-1.2-.4-.5-.7-1.1-.9-1.8-.2-.7-.3-1.4-.3-2.2 0-.8.1-1.6.3-2.2.2-.7.5-1.3.9-1.8s.9-.9 1.4-1.2c.6-.3 1.2-.5 1.9-.5s1.3.2 1.9.5c.6.3 1 .7 1.4 1.2.4.5.7 1.1.9 1.8.2.7.3 1.4.3 2.2 0 .8-.1 1.6-.3 2.2-.2.7-.5 1.3-.9 1.8s-.9.9-1.4 1.2c-.6.3-1.2.4-1.9.4zm0-1.6c.6 0 1-.2 1.5-.5.4-.4.7-.9 1-1.5.2-.6.3-1.3.3-2.1s-.1-1.5-.3-2.1c-.2-.6-.5-1.1-1-1.5-.4-.4-.9-.5-1.5-.5s-1.1.2-1.5.5c-.4.4-.7.8-.9 1.5-.2.6-.3 1.3-.3 2.1s.1 1.5.3 2.1c.2.6.5 1.1.9 1.5.5.3 1 .5 1.5.5zm10 1.4v-8.2h1.6v1.1h.1c.2-.3.4-.5.6-.7.3-.2.6-.4.9-.5.3-.1.7-.2 1-.2.6 0 1.1.1 1.5.4.4.3.7.7.9 1.1.3-.4.6-.8 1.1-1.1.5-.3 1-.5 1.7-.5 1 0 1.7.3 2.1.9.5.6.7 1.4.7 2.3v5.2h-1.7v-4.9c0-.7-.1-1.1-.4-1.4-.3-.3-.7-.5-1.2-.5-.4 0-.7.1-1 .3-.3.2-.5.5-.7.9-.2.4-.2.8-.2 1.2v4.4H108v-4.9c0-.7-.1-1.1-.4-1.4-.3-.3-.7-.5-1.2-.5-.4 0-.7.1-1 .3-.3.2-.5.5-.7.9-.2.4-.2.8-.2 1.2v4.4h-1.8zm17.2.2c-.6 0-1.2-.1-1.7-.3-.5-.2-.9-.5-1.2-.8-.3-.3-.5-.7-.7-1.1l1.5-.7c.2.4.5.8.8 1 .4.2.8.3 1.2.3.4 0 .8-.1 1.1-.2.3-.2.5-.4.5-.8 0-.2-.1-.4-.2-.6-.1-.1-.3-.3-.6-.4-.2-.1-.5-.2-.8-.2l-1-.2c-.4-.1-.8-.3-1.1-.5-.3-.2-.6-.5-.8-.8-.2-.3-.3-.7-.3-1.1 0-.5.1-.9.4-1.3.3-.4.7-.6 1.1-.8.5-.2 1-.3 1.6-.3.5 0 1 .1 1.4.2.4.1.8.3 1.1.6.3.3.6.6.8 1l-1.5.7c-.2-.4-.4-.6-.7-.8-.3-.1-.6-.2-1-.2s-.7.1-1 .2c-.3.2-.4.4-.4.6 0 .3.1.5.4.7.2.2.5.3.9.4l1.2.3c.8.2 1.4.5 1.8.9.4.4.6.9.6 1.5 0 .5-.2 1-.5 1.4-.3.4-.7.7-1.2.9-.5.3-1.1.4-1.7.4zM42.4 96.3V82h1.7v14.3h-1.7zm4.6 0V86.1h1.6v1.5h.1c.3-.5.7-.9 1.3-1.3.6-.4 1.3-.5 2-.5 1.2 0 2.2.4 2.8 1.1.6.7 1 1.7 1 2.9v6.5h-1.7V90c0-1-.2-1.7-.7-2.1-.5-.4-1.1-.6-1.8-.6-.6 0-1.1.2-1.5.5-.4.3-.8.7-1 1.2-.2.5-.4 1-.4 1.6v5.7H47zm10-10.2h6v1.5h-6v-1.5zm1.8 7.5V83.3h1.7v10c0 .5.1.9.3 1.2s.6.4 1.1.4c.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.5-.6-.7-1.3-.7-2.2zm10.3 3c-1 0-1.9-.2-2.6-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.6-1.7-.6-2.8 0-1 .2-1.9.6-2.7s1-1.5 1.7-2 1.6-.8 2.6-.8 1.9.2 2.6.7c.7.4 1.3 1.1 1.7 1.8.4.8.6 1.7.6 2.7v.5H65V90h7c0-.3-.1-.6-.2-.9-.1-.3-.3-.6-.5-.9-.2-.3-.6-.5-.9-.7-.4-.2-.8-.3-1.4-.3-.7 0-1.2.2-1.7.5s-.8.8-1.1 1.4c-.3.6-.4 1.2-.4 2 0 .9.2 1.6.5 2.2.3.6.8 1 1.3 1.3.5.3 1.1.4 1.7.4.8 0 1.4-.2 1.8-.5.5-.4.9-.8 1.2-1.3l1.4.7c-.4.8-1 1.4-1.7 1.9-.9.6-1.8.8-2.9.8zm6.7-.3V86.1h1.6v1.6h.1c.1-.4.4-.7.7-1 .3-.3.7-.5 1.1-.7s.8-.2 1.2-.2h.7c.2 0 .3.1.5.2v1.8c-.2-.1-.5-.2-.7-.2-.2-.1-.5-.1-.7-.1-.5 0-1 .1-1.4.4-.4.3-.7.7-1 1.1-.2.5-.4 1-.4 1.5v5.7h-1.7zm10.4.3c-.8 0-1.4-.1-2-.4s-1-.7-1.3-1.2c-.3-.5-.5-1.1-.5-1.8 0-.8.2-1.4.6-1.9.4-.5.9-.9 1.5-1.2.7-.3 1.4-.4 2.2-.4.4 0 .9 0 1.2.1s.7.2 1 .3c.3.1.5.2.7.3v-.6c0-.8-.3-1.4-.8-1.8-.5-.5-1.2-.7-2-.7-.6 0-1.1.1-1.6.4-.5.2-.9.6-1.1 1l-1.3-1c.3-.4.6-.7 1-1 .4-.3.9-.5 1.4-.7.5-.2 1.1-.2 1.6-.2 1.4 0 2.5.4 3.2 1.1.8.7 1.2 1.7 1.2 3v6.5h-1.6v-1.5h-.1c-.2.3-.4.6-.7.8s-.7.5-1.1.7c-.5.2-1 .2-1.5.2zm.1-1.5c.6 0 1.1-.1 1.6-.4.5-.3.9-.7 1.2-1.2.3-.5.5-1 .5-1.6-.3-.2-.7-.4-1.2-.5s-1-.2-1.5-.2c-1 0-1.7.2-2.1.6-.4.4-.7.9-.7 1.5s.2 1 .6 1.4 1 .4 1.6.4zm11.8 1.5c-1 0-1.9-.2-2.7-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.7-1.7-.7-2.8 0-1 .2-2 .7-2.8.4-.8 1.1-1.5 1.8-1.9.8-.5 1.7-.7 2.7-.7 1.1 0 2.1.3 2.8.8.7.5 1.3 1.2 1.6 2l-1.5.6c-.2-.6-.6-1.1-1.1-1.4-.5-.3-1.1-.5-1.8-.5-.6 0-1.2.2-1.7.5s-.9.8-1.2 1.4c-.3.6-.5 1.3-.5 2 0 .8.2 1.4.5 2 .3.6.7 1 1.2 1.4.5.3 1.1.5 1.7.5.7 0 1.3-.2 1.9-.5s.9-.8 1.2-1.4l1.5.6c-.3.8-.9 1.5-1.6 2s-1.9.8-3 .8zm5.5-10.5h6v1.5h-6v-1.5zm1.7 7.5V83.3h1.7v10c0 .5.1.9.3 1.2s.6.4 1.1.4c.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8s-.7-1.3-.7-2.2zm6.5 2.7V86.1h1.7v10.2h-1.7zm.9-12c-.3 0-.6-.1-.9-.4s-.4-.5-.4-.9c0-.3.1-.6.4-.9.2-.2.5-.4.9-.4.3 0 .6.1.9.4.2.2.4.5.4.9 0 .3-.1.6-.4.9-.3.2-.6.4-.9.4zm8.1 12.3c-1 0-1.9-.2-2.7-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.7-1.7-.7-2.8 0-1 .2-1.9.7-2.8.4-.8 1.1-1.5 1.8-2 .8-.5 1.7-.7 2.7-.7 1 0 1.9.2 2.7.7.8.5 1.4 1.1 1.9 2s.7 1.7.7 2.7c0 1-.2 1.9-.7 2.8-.4.8-1.1 1.5-1.9 1.9-.8.6-1.7.8-2.7.8zm0-1.5c.6 0 1.2-.2 1.7-.5s1-.8 1.3-1.3c.3-.6.5-1.3.5-2.1s-.2-1.5-.5-2.1-.8-1-1.3-1.3c-.5-.3-1.1-.5-1.7-.5-.6 0-1.2.2-1.7.5s-1 .7-1.3 1.3-.5 1.3-.5 2.1.2 1.5.5 2.1c.3.6.8 1 1.3 1.3.5.4 1.1.5 1.7.5zm7 1.2V86.1h1.6v1.5h.1c.3-.5.7-.9 1.3-1.3.6-.4 1.3-.5 2-.5 1.2 0 2.2.4 2.8 1.1.6.7 1 1.7 1 2.9v6.5h-1.7V90c0-1-.2-1.7-.7-2.1-.5-.4-1.1-.6-1.8-.6-.6 0-1.1.2-1.5.5-.4.3-.8.7-1 1.2-.2.5-.4 1-.4 1.6v5.7h-1.7zM143 86.1h6v1.5h-6v-1.5zm1.7 7.5V83.3h1.7v10c0 .5.1.9.3 1.2s.6.4 1.1.4c.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.4-.6-.7-1.3-.7-2.2zm10.5 3c-1 0-1.9-.2-2.7-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.7-1.7-.7-2.8 0-1 .2-1.9.7-2.8.4-.8 1.1-1.5 1.8-2 .8-.5 1.7-.7 2.7-.7 1 0 1.9.2 2.7.7.8.5 1.4 1.1 1.9 2 .4.8.7 1.7.7 2.7 0 1-.2 1.9-.7 2.8-.4.8-1.1 1.5-1.9 1.9-.8.6-1.7.8-2.7.8zm0-1.5c.6 0 1.2-.2 1.7-.5s1-.8 1.3-1.3c.3-.6.5-1.3.5-2.1s-.2-1.5-.5-2.1c-.3-.6-.8-1-1.3-1.3-.5-.3-1.1-.5-1.7-.5-.6 0-1.2.2-1.7.5s-1 .7-1.3 1.3c-.3.6-.5 1.3-.5 2.1s.2 1.5.5 2.1c.3.6.8 1 1.3 1.3.5.4 1.1.5 1.7.5zm12.2 1.2V82h2.1l7.2 11.4h.1l-.1-2.8V82h1.7v14.3h-1.8l-7.5-11.9h-.1l.1 2.8v9.2h-1.7zm18.2.3c-1 0-1.9-.2-2.6-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.6-1.7-.6-2.8 0-1 .2-1.9.6-2.7.4-.8 1-1.5 1.7-2s1.6-.8 2.6-.8 1.9.2 2.6.7c.7.4 1.3 1.1 1.7 1.8.4.8.6 1.7.6 2.7v.5h-8.8V90h7c0-.3-.1-.6-.2-.9-.1-.3-.3-.6-.5-.9-.2-.3-.6-.5-.9-.7-.4-.2-.8-.3-1.4-.3-.7 0-1.2.2-1.7.5s-.8.8-1.1 1.4c-.3.6-.4 1.2-.4 2 0 .9.2 1.6.5 2.2.3.6.8 1 1.3 1.3.5.3 1.1.4 1.7.4.8 0 1.4-.2 1.8-.5.5-.4.9-.8 1.2-1.3l1.4.7c-.4.8-1 1.4-1.7 1.9-1 .6-1.9.8-3 .8zm5.5-.3 4.1-5.9h.2l2.9-4.3h2l-4 5.6h-.1l-3.1 4.6h-2zm.1-10.2h1.9l3.2 4.5h.1l4 5.7h-2l-3-4.5h-.1l-4.1-5.7zm9.9 0h6v1.5h-6v-1.5zm1.8 7.5V83.3h1.7v10c0 .5.1.9.3 1.2.2.3.6.4 1.1.4.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.4-.6-.7-1.3-.7-2.2zm11.2 2.7V82h4.8c.8 0 1.5.2 2.2.5.7.4 1.2.8 1.6 1.5.4.6.6 1.4.6 2.2 0 .8-.2 1.6-.6 2.2-.4.6-.9 1.1-1.6 1.5-.7.4-1.4.5-2.2.5H215v-1.6h4c.6 0 1-.1 1.4-.4.4-.3.7-.6.9-1 .2-.4.3-.8.3-1.2s-.1-.8-.3-1.2c-.2-.4-.5-.7-.9-1-.4-.3-.9-.4-1.4-.4h-3.2v12.7h-1.7zm14.1.3c-.8 0-1.4-.1-2-.4s-1-.7-1.3-1.2-.5-1.1-.5-1.8c0-.8.2-1.4.6-1.9s.9-.9 1.5-1.2c.7-.3 1.4-.4 2.2-.4.4 0 .9 0 1.2.1s.7.2 1 .3c.3.1.5.2.7.3v-.6c0-.8-.3-1.4-.8-1.8-.5-.5-1.2-.7-2-.7-.6 0-1.1.1-1.6.4-.5.2-.9.6-1.1 1l-1.3-1c.3-.4.6-.7 1-1 .4-.3.9-.5 1.4-.7s1.1-.2 1.6-.2c1.4 0 2.5.4 3.2 1.1.8.7 1.2 1.7 1.2 3v6.5h-1.6v-1.5h-.1c-.2.3-.4.6-.7.8s-.7.5-1.1.7c-.6.2-1 .2-1.5.2zm.1-1.5c.6 0 1.1-.1 1.6-.4.5-.3.9-.7 1.2-1.2.3-.5.5-1 .5-1.6-.3-.2-.7-.4-1.2-.5s-1-.2-1.5-.2c-1 0-1.7.2-2.1.6-.4.4-.7.9-.7 1.5s.2 1 .6 1.4 1 .4 1.6.4zm7.4 1.2V86.1h1.7v10.2h-1.7zm.8-12c-.3 0-.6-.1-.9-.4s-.4-.5-.4-.9c0-.3.1-.6.4-.9.2-.2.5-.4.9-.4.3 0 .6.1.9.4.2.2.4.5.4.9 0 .3-.1.6-.4.9-.2.2-.5.4-.9.4zm3.5 12V86.1h1.6v1.5h.1c.3-.5.7-.9 1.3-1.3.6-.4 1.3-.5 2-.5 1.2 0 2.2.4 2.8 1.1.6.7 1 1.7 1 2.9v6.5h-1.7V90c0-1-.2-1.7-.7-2.1-.5-.4-1.1-.6-1.8-.6-.6 0-1.1.2-1.5.5-.4.3-.8.7-1 1.2-.2.5-.4 1-.4 1.6v5.7H240zm10.1-10.2h6v1.5h-6v-1.5zm1.7 7.5V83.3h1.7v10c0 .5.1.9.3 1.2s.6.4 1.1.4c.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8s-.7-1.3-.7-2.2zM96 172.8c-2.4 0-4.3 1.9-4.3 4.3 0 2 1.4 3.7 3.3 4.2v20.1h2v-20.1c1.9-.5 3.3-2.1 3.3-4.2 0-2.3-1.9-4.3-4.3-4.3zM201.6 172.6c-2.4 0-4.3 1.9-4.3 4.3 0 2 1.4 3.7 3.3 4.2v19.8h2v-19.8c1.9-.5 3.3-2.1 3.3-4.2 0-2.4-1.9-4.3-4.3-4.3z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
  &lt;svg title=&quot;A diagram of the INP thresholds. An INP at or below 200 milliseconds is considered good. Between 200 and 500 milliseconds suggests a page&#39;s responsiveness needs improvement. Anything over 500 milliseconds means that a page&#39;s responsiveness is poor.&quot; class=&quot;inp-desktop&quot; version=&quot;1.1&quot; id=&quot;Layer_1&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; x=&quot;0&quot; y=&quot;0&quot; viewBox=&quot;0 0 658.4 113.6&quot; style=&quot;enable-background:new 0 0 658.4 113.6&quot; xml:space=&quot;preserve&quot;&gt;&lt;style&gt;.st0{fill: #2979FF} v.st1{fill-rule: evenodd;clip-rule: evenodd;fill: #0CCE6B} .st2 {fill: #191919} .st3{fill-rule: evenodd;clip-rule: evenodd;fill: #FFA400} .st4{fill-rule: evenodd;clip-rule: evenodd;fill: #FF4E42} @media screen and (prefers-color-scheme: light){.st2{fill: #191919}} @media screen and (prefers-color-scheme: dark){.st2{fill: #fff}} [data-user-theme=dark] .st2{fill: #fff}&lt;/style&gt;&lt;path class=&quot;st0&quot; d=&quot;M30.2 68.7V0h13v68.7h-13zm28.7 0V0H74l27.7 46.1h.8l-.8-13.2V0h12.9v68.7H101L71.7 20.1h-.8l.8 13.2v35.4H58.9zm71.3 0V0h24.2c4.4 0 8.4.9 11.9 2.7 3.5 1.8 6.3 4.4 8.4 7.6 2.1 3.3 3.1 7 3.1 11.3 0 4.3-1 8.1-3.1 11.4-2.1 3.3-4.9 5.8-8.4 7.7-3.5 1.8-7.5 2.7-11.9 2.7h-17V31.2h17.4c2.2 0 4.1-.4 5.7-1.3 1.5-.9 2.7-2.1 3.5-3.5.8-1.4 1.2-3 1.2-4.7 0-1.7-.4-3.2-1.2-4.6-.8-1.4-1.9-2.6-3.5-3.5-1.5-.9-3.4-1.3-5.7-1.3h-11.6v56.5h-13z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st1&quot; d=&quot;M303.2 14.9h115.2v43.2H303.2V14.9z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st2&quot; d=&quot;M345.3 41.5c-.7 0-1.3-.1-1.9-.4-.6-.2-1.1-.6-1.6-1s-.8-1-1.1-1.6c-.3-.6-.4-1.3-.4-2s.1-1.4.4-2c.3-.6.6-1.1 1.1-1.6.5-.5 1-.8 1.6-1 .6-.2 1.2-.4 1.9-.4s1.4.1 2 .4c.6.2 1.1.6 1.6 1.1l-1 1c-.2-.2-.4-.4-.7-.6-.3-.2-.5-.3-.9-.4-.3-.1-.6-.1-1-.1-.5 0-.9.1-1.3.3-.4.2-.8.4-1.1.7-.3.3-.6.7-.8 1.1-.2.4-.3.9-.3 1.5s.1 1 .3 1.5c.2.4.5.8.8 1.1.3.3.7.6 1.1.7.4.2.9.2 1.3.2s.8-.1 1.2-.2c.4-.1.7-.3 1-.5.3-.2.5-.5.7-.8.2-.3.3-.7.3-1.1h-3.3v-1.3h4.6v.8c0 .7-.1 1.3-.4 1.9-.2.6-.6 1-1 1.5-.4.4-.9.7-1.5.9-.3.2-.9.3-1.6.3zm10.5 0c-.7 0-1.4-.1-2-.4-.6-.3-1.1-.6-1.6-1.1-.4-.5-.8-1-1-1.6-.2-.6-.4-1.2-.4-1.9s.1-1.3.4-1.9c.2-.6.6-1.1 1-1.6s1-.8 1.6-1.1c.6-.3 1.3-.4 2-.4s1.4.1 2 .4c.6.2 1.1.6 1.6 1.1.5.5.8 1 1 1.6.2.6.4 1.2.4 1.9s-.1 1.3-.4 1.9c-.2.6-.6 1.1-1 1.6s-1 .8-1.6 1.1-1.2.4-2 .4zm0-1.4c.6 0 1.2-.2 1.8-.5.5-.3.9-.7 1.2-1.3s.5-1.2.5-1.9-.2-1.3-.5-1.9-.7-1-1.2-1.3c-.5-.3-1.1-.5-1.8-.5-.6 0-1.2.2-1.8.5-.5.3-.9.7-1.2 1.3s-.5 1.2-.5 1.9.2 1.3.5 1.9.7 1 1.2 1.3c.6.4 1.2.5 1.8.5zm11 1.4c-.7 0-1.4-.1-2-.4-.6-.3-1.1-.6-1.6-1.1-.4-.5-.8-1-1-1.6-.2-.6-.4-1.2-.4-1.9s.1-1.3.4-1.9c.2-.6.6-1.1 1-1.6s1-.8 1.6-1.1c.6-.3 1.3-.4 2-.4s1.4.1 2 .4c.6.2 1.1.6 1.6 1.1.5.5.8 1 1 1.6.2.6.4 1.2.4 1.9s-.1 1.3-.4 1.9c-.2.6-.6 1.1-1 1.6s-1 .8-1.6 1.1-1.3.4-2 .4zm0-1.4c.6 0 1.2-.2 1.8-.5.5-.3.9-.7 1.2-1.3s.5-1.2.5-1.9-.2-1.3-.5-1.9-.7-1-1.2-1.3c-.5-.3-1.1-.5-1.8-.5-.6 0-1.2.2-1.8.5-.5.3-.9.7-1.2 1.3s-.5 1.2-.5 1.9.2 1.3.5 1.9.7 1 1.2 1.3c.6.4 1.2.5 1.8.5zm6.6 1.2v-9.5h3c1 0 1.9.2 2.6.6.7.4 1.3 1 1.7 1.7s.6 1.5.6 2.5c0 .9-.2 1.8-.6 2.5s-1 1.3-1.7 1.7c-.7.4-1.6.6-2.6.6h-3zm1.5-1.4h1.5c.7 0 1.3-.1 1.8-.4.5-.3.9-.7 1.2-1.2.3-.5.4-1.1.4-1.8s-.1-1.3-.4-1.8c-.3-.5-.7-.9-1.2-1.2-.5-.3-1.1-.4-1.8-.4h-1.5v6.8z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st3&quot; d=&quot;M418.4 14.9h124.8v43.2H418.4V14.9z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st2&quot; d=&quot;M460.8 33.3v-9.5h1.7l4.3 7h.1l-.1-1.8v-5.2h1.5v9.5h-1.5l-4.5-7.4h-.1l.1 1.8v5.5h-1.5zm9.7 0v-9.5h5.8v1.4H472V32h4.3v1.4h-5.8zm.7-4.1v-1.4h4.6v1.4h-4.6zm6.9 4.1v-9.5h5.8v1.4h-4.3V32h4.3v1.4h-5.8zm.8-4.1v-1.4h4.6v1.4h-4.6zm6.8 4.1v-9.5h3c1 0 1.9.2 2.6.6.7.4 1.3 1 1.7 1.7s.6 1.5.6 2.5c0 .9-.2 1.8-.6 2.5s-1 1.3-1.7 1.7c-.7.4-1.6.6-2.6.6h-3zm1.5-1.4h1.5c.7 0 1.3-.1 1.8-.4.5-.3.9-.7 1.2-1.2.3-.5.4-1.1.4-1.8s-.1-1.3-.4-1.8c-.3-.5-.7-.9-1.2-1.2-.5-.3-1.1-.4-1.8-.4h-1.5v6.8zm10.9 1.6c-.5 0-1-.1-1.5-.3-.5-.2-.9-.5-1.2-.9-.3-.4-.6-.9-.8-1.5l1.4-.6c.1.5.4.9.8 1.3.4.3.8.5 1.3.5.3 0 .6-.1.8-.2.3-.1.5-.3.6-.5.2-.2.2-.5.2-.8 0-.3-.1-.5-.2-.7-.1-.2-.3-.4-.6-.5-.3-.2-.7-.3-1.1-.5l-.6-.2c-.3-.1-.5-.2-.8-.4-.3-.1-.5-.3-.7-.5-.2-.2-.4-.5-.5-.7-.1-.3-.2-.6-.2-1 0-.5.1-.9.4-1.3.2-.4.6-.7 1-.9.4-.2 1-.4 1.6-.4.6 0 1.1.1 1.5.3.4.2.7.5 1 .8.2.3.4.6.5.9l-1.3.6c-.1-.2-.2-.4-.3-.5-.1-.2-.3-.3-.5-.4-.2-.1-.5-.2-.8-.2-.3 0-.5.1-.8.2-.2.1-.4.2-.6.4-.1.2-.2.4-.2.6 0 .3.1.6.4.8s.7.4 1.2.6l.6.2c.3.1.6.2.9.4.3.1.6.3.8.6.2.2.4.5.6.8.1.3.2.7.2 1.2s-.1.9-.3 1.3c-.2.4-.4.6-.8.9-.3.2-.7.4-1 .5-.3 0-.7.1-1 .1zM434.4 49.3v-9.5h1.5v9.5h-1.5zm3.6 0v-9.5h2l2.8 7.3h.1l2.8-7.3h2v9.5h-1.4v-5.4l.1-1.7h-.1l-2.8 7.1h-1.2l-2.8-7.1h-.1l.1 1.7v5.4H438zm11.7 0v-9.5h3.3c.6 0 1.1.1 1.6.4.5.2.9.6 1.1 1 .3.4.4.9.4 1.5s-.1 1.1-.4 1.5c-.3.4-.7.8-1.1 1-.5.2-1 .4-1.6.4h-2.5v-1.4h2.5c.4 0 .7-.1.9-.2.2-.1.4-.3.5-.6.1-.2.2-.5.2-.8 0-.3-.1-.5-.2-.7-.1-.2-.3-.4-.5-.6-.2-.2-.5-.2-.9-.2h-1.8v8.2h-1.5zm8 0v-9.5h3.3c.6 0 1.1.1 1.6.4.5.2.8.6 1.1 1s.4.9.4 1.5c0 .4-.1.8-.2 1.1s-.4.7-.7.9c-.3.3-.6.5-1 .6-.4.1-.9.2-1.4.2h-2.3v-1.3h2.5c.3 0 .6-.1.8-.2.2-.1.4-.3.6-.6.2-.2.2-.5.2-.8 0-.3-.1-.5-.2-.7-.1-.2-.3-.4-.5-.6-.2-.1-.5-.2-.9-.2h-1.9v8.2h-1.4zm2.2-4.4h1.7l3 4.3v.1h-1.7l-3-4.4zm10.3 4.6c-.7 0-1.4-.1-2-.4-.6-.3-1.1-.6-1.6-1.1-.4-.5-.8-1-1-1.6-.2-.6-.4-1.2-.4-1.9s.1-1.3.4-1.9c.2-.6.6-1.1 1-1.6s1-.8 1.6-1.1c.6-.3 1.3-.4 2-.4s1.4.1 2 .4c.6.2 1.1.6 1.6 1.1.5.5.8 1 1 1.6.2.6.4 1.2.4 1.9s-.1 1.3-.4 1.9c-.2.6-.6 1.1-1 1.6s-1 .8-1.6 1.1c-.6.3-1.2.4-2 .4zm0-1.4c.6 0 1.2-.2 1.8-.5.5-.3.9-.7 1.2-1.3.3-.5.5-1.2.5-1.9s-.2-1.3-.5-1.9c-.3-.5-.7-1-1.2-1.3-.5-.3-1.1-.5-1.8-.5s-1.2.2-1.8.5c-.5.3-.9.7-1.2 1.3-.3.5-.5 1.2-.5 1.9s.2 1.3.5 1.9c.3.5.7 1 1.2 1.3.6.4 1.2.5 1.8.5zm8.8 1.2-3.4-9.5h1.6l2.2 6.3.3 1.1h.1l.4-1.1 2.2-6.3h1.6l-3.5 9.5H479zm6.2 0v-9.5h5.8v1.4h-4.3V48h4.3v1.4h-5.8zm.8-4.1v-1.4h4.6v1.4H486zm6.8 4.1v-9.5h2l2.8 7.3h.1l2.8-7.3h2v9.5H501v-5.4l.1-1.7h-.1l-2.8 7.1H497l-2.8-7.1h-.1l.1 1.7v5.4h-1.4zm11.8 0v-9.5h5.8v1.4h-4.3V48h4.3v1.4h-5.8zm.8-4.1v-1.4h4.6v1.4h-4.6zm6.8 4.1v-9.5h1.7l4.3 7h.1l-.1-1.8v-5.2h1.5v9.5h-1.5l-4.5-7.4h-.1l.1 1.8v5.5h-1.5zm11.7 0v-8.8h1.5v8.8h-1.5zm-2.7-8.2v-1.4h6.8v1.4h-6.8z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st4&quot; d=&quot;M543.2 14.9h115.2v43.2H543.2V14.9z&quot;&gt;&lt;/path&gt;&lt;path class=&quot;st2&quot; d=&quot;M582.9 41.3v-9.5h3.3c.6 0 1.1.1 1.6.4.5.2.9.6 1.1 1 .3.4.4.9.4 1.5s-.1 1.1-.4 1.5c-.3.4-.7.8-1.1 1-.5.2-1 .4-1.6.4h-2.5v-1.4h2.5c.4 0 .7-.1.9-.2.2-.1.4-.3.5-.6.1-.2.2-.5.2-.8 0-.3-.1-.5-.2-.7-.1-.2-.3-.4-.5-.6-.2-.2-.5-.2-.9-.2h-1.8v8.2h-1.5zm12.3.2c-.7 0-1.4-.1-2-.4-.6-.3-1.1-.6-1.6-1.1-.4-.5-.8-1-1-1.6-.2-.6-.4-1.2-.4-1.9s.1-1.3.4-1.9c.2-.6.6-1.1 1-1.6s1-.8 1.6-1.1c.6-.3 1.3-.4 2-.4s1.4.1 2 .4c.6.2 1.1.6 1.6 1.1.5.5.8 1 1 1.6.2.6.4 1.2.4 1.9s-.1 1.3-.4 1.9c-.2.6-.6 1.1-1 1.6s-1 .8-1.6 1.1c-.7.3-1.3.4-2 .4zm0-1.4c.6 0 1.2-.2 1.8-.5.5-.3.9-.7 1.2-1.3.3-.5.5-1.2.5-1.9s-.2-1.3-.5-1.9c-.3-.5-.7-1-1.2-1.3-.5-.3-1.1-.5-1.8-.5-.6 0-1.2.2-1.8.5-.5.3-.9.7-1.2 1.3-.3.5-.5 1.2-.5 1.9s.2 1.3.5 1.9c.3.5.7 1 1.2 1.3.6.4 1.1.5 1.8.5zm10.9 1.4c-.7 0-1.4-.1-2-.4-.6-.3-1.1-.6-1.6-1.1-.4-.5-.8-1-1-1.6-.2-.6-.4-1.2-.4-1.9s.1-1.3.4-1.9c.2-.6.6-1.1 1-1.6s1-.8 1.6-1.1c.6-.3 1.3-.4 2-.4s1.4.1 2 .4c.6.2 1.1.6 1.6 1.1.5.5.8 1 1 1.6.2.6.4 1.2.4 1.9s-.1 1.3-.4 1.9c-.2.6-.6 1.1-1 1.6s-1 .8-1.6 1.1c-.6.3-1.3.4-2 .4zm0-1.4c.6 0 1.2-.2 1.8-.5.5-.3.9-.7 1.2-1.3.3-.5.5-1.2.5-1.9s-.2-1.3-.5-1.9c-.3-.5-.7-1-1.2-1.3-.5-.3-1.1-.5-1.8-.5s-1.2.2-1.8.5c-.5.3-.9.7-1.2 1.3-.3.5-.5 1.2-.5 1.9s.2 1.3.5 1.9c.3.5.7 1 1.2 1.3.6.4 1.2.5 1.8.5zm6.7 1.2v-9.5h3.3c.6 0 1.1.1 1.6.4.5.2.8.6 1.1 1s.4.9.4 1.5c0 .4-.1.8-.2 1.1s-.4.7-.7.9c-.3.3-.6.5-1 .6-.4.1-.9.2-1.4.2h-2.3v-1.3h2.5c.3 0 .6-.1.8-.2.2-.1.4-.3.6-.6.2-.2.2-.5.2-.8 0-.3-.1-.5-.2-.7-.1-.2-.3-.4-.5-.6-.2-.1-.5-.2-.9-.2h-1.9v8.2h-1.4zm2.2-4.4h1.7l3 4.3v.1H618l-3-4.4zM517.7 102.9c-.6 0-1.1-.1-1.7-.3-.6-.2-1-.6-1.5-1-.4-.5-.7-1.1-.9-1.8l1.7-.7c.2.6.4 1.1.8 1.5.4.4.9.6 1.6.6.7 0 1.2-.2 1.6-.6.4-.4.7-1 .7-1.6 0-.7-.2-1.2-.6-1.6-.4-.4-1-.7-1.6-.7-.4 0-.8.1-1.1.2-.3.2-.6.4-.8.7l-1.8-.8.7-6h6.4v1.7h-4.9l-.4 3.2h.1c.3-.2.6-.4.9-.5.4-.1.8-.2 1.3-.2.7 0 1.3.2 1.9.5.6.3 1 .8 1.4 1.4.4.6.5 1.3.5 2 0 .8-.2 1.5-.5 2.1-.4.6-.8 1.1-1.5 1.4-.7.3-1.5.5-2.3.5zm10.8 0c-.7 0-1.4-.2-2-.5-.6-.3-1.1-.8-1.5-1.3-.4-.6-.8-1.2-1-2-.2-.8-.3-1.6-.3-2.4 0-.9.1-1.7.3-2.5.2-.8.6-1.4 1-2 .4-.6.9-1 1.5-1.3.6-.3 1.3-.5 2-.5.8 0 1.4.2 2 .5.6.3 1.1.8 1.5 1.3.4.6.8 1.2 1 2 .2.8.3 1.6.3 2.5 0 .9-.1 1.7-.3 2.4-.2.8-.5 1.4-1 2-.4.6-.9 1-1.5 1.3-.6.3-1.2.5-2 .5zm0-1.8c.6 0 1.1-.2 1.6-.6.4-.4.8-.9 1-1.6.2-.7.4-1.4.4-2.2 0-.8-.1-1.6-.4-2.3-.2-.7-.6-1.2-1-1.6-.4-.4-1-.6-1.6-.6-.6 0-1.2.2-1.6.6-.4.4-.8.9-1 1.6-.2.7-.4 1.4-.4 2.3 0 .8.1 1.6.4 2.2.2.7.6 1.2 1 1.6.5.4 1 .6 1.6.6zm11.4 1.8c-.7 0-1.4-.2-2-.5-.6-.3-1.1-.8-1.5-1.3-.4-.6-.8-1.2-1-2-.2-.8-.3-1.6-.3-2.4 0-.9.1-1.7.3-2.5.2-.8.6-1.4 1-2 .4-.6.9-1 1.5-1.3.6-.3 1.3-.5 2-.5.8 0 1.4.2 2 .5.6.3 1.1.8 1.5 1.3.4.6.8 1.2 1 2 .2.8.3 1.6.3 2.5 0 .9-.1 1.7-.3 2.4-.2.8-.5 1.4-1 2-.4.6-.9 1-1.5 1.3-.5.3-1.2.5-2 .5zm.1-1.8c.6 0 1.1-.2 1.6-.6.4-.4.8-.9 1-1.6.2-.7.4-1.4.4-2.2 0-.8-.1-1.6-.4-2.3-.2-.7-.6-1.2-1-1.6-.4-.4-1-.6-1.6-.6-.6 0-1.2.2-1.6.6-.4.4-.8.9-1 1.6-.2.7-.4 1.4-.4 2.3 0 .8.1 1.6.4 2.2.2.7.6 1.2 1 1.6.4.4.9.6 1.6.6zm10.8 1.5v-8.8h1.8V95h.1c.2-.3.4-.5.7-.8s.6-.4.9-.5c.4-.1.7-.2 1.1-.2.7 0 1.2.2 1.7.5s.8.7 1 1.2c.3-.5.7-.8 1.2-1.2.5-.3 1.1-.5 1.8-.5 1 0 1.8.3 2.3 1 .5.6.8 1.5.8 2.5v5.7h-1.9v-5.4c0-.7-.2-1.2-.5-1.5-.3-.3-.7-.5-1.3-.5-.4 0-.8.1-1.1.4-.3.2-.6.6-.8 1s-.3.9-.3 1.3v4.7h-1.9v-5.4c0-.7-.2-1.2-.5-1.5-.3-.3-.8-.5-1.3-.5-.4 0-.8.1-1.1.4-.3.2-.6.6-.7 1s-.3.9-.3 1.3v4.7h-1.7zm18.6.3c-.7 0-1.3-.1-1.8-.3-.5-.2-.9-.5-1.3-.9-.3-.4-.6-.8-.7-1.2l1.7-.7c.2.5.5.8.9 1.1.4.2.8.4 1.3.4s.9-.1 1.2-.2c.3-.2.5-.4.5-.8 0-.2-.1-.5-.2-.6s-.4-.3-.6-.4l-.9-.3-1.1-.2c-.4-.1-.8-.3-1.2-.5-.4-.2-.7-.5-.9-.9-.2-.4-.3-.8-.3-1.2 0-.5.2-1 .5-1.4.3-.4.7-.7 1.2-.9.5-.2 1.1-.3 1.7-.3.6 0 1.1.1 1.5.2.5.1.9.3 1.2.6.3.3.6.6.8 1l-1.6.7c-.2-.4-.5-.7-.8-.8-.3-.2-.7-.2-1.1-.2-.4 0-.8.1-1.1.3-.3.2-.4.4-.4.7 0 .3.1.5.4.7.3.2.6.3 1 .4l1.3.3c.9.2 1.5.6 2 1 .4.4.7 1 .7 1.6 0 .6-.2 1.1-.5 1.5-.3.4-.8.7-1.3 1-.8.2-1.5.3-2.1.3zM389.2 102.6v-1.8l.4-.4.9-.9c.4-.4.7-.8 1.1-1.2l1.1-1.1.8-.8c.3-.3.6-.6.8-.9.2-.3.3-.5.4-.8.1-.2.1-.5.1-.8 0-.3-.1-.6-.2-.8-.1-.3-.4-.5-.7-.6-.3-.2-.6-.2-1.1-.2-.4 0-.7.1-1 .2-.3.2-.5.4-.7.6-.2.2-.3.5-.3.7l-1.7-.7c.1-.3.2-.6.4-.9.2-.3.4-.6.8-.9.3-.3.7-.5 1.1-.7.4-.2.9-.3 1.5-.3.8 0 1.4.2 2 .5.6.3 1 .7 1.3 1.2.3.5.5 1.1.5 1.7 0 .5-.1 1-.3 1.5-.2.5-.4.9-.7 1.3-.3.4-.6.7-.9 1-.2.1-.4.3-.6.6-.2.2-.5.5-.7.8l-.8.8-.7.7-.4.4h5.2v1.7h-7.6zm14.2.3c-.7 0-1.4-.2-2-.5-.6-.3-1.1-.8-1.5-1.3-.4-.6-.8-1.2-1-2-.2-.8-.3-1.6-.3-2.4 0-.9.1-1.7.3-2.5.2-.8.6-1.4 1-2 .4-.6.9-1 1.5-1.3.6-.3 1.3-.5 2-.5.8 0 1.4.2 2 .5.6.3 1.1.8 1.5 1.3.4.6.8 1.2 1 2 .2.8.3 1.6.3 2.5 0 .9-.1 1.7-.3 2.4-.2.8-.5 1.4-1 2-.4.6-.9 1-1.5 1.3-.5.3-1.2.5-2 .5zm0-1.8c.6 0 1.1-.2 1.6-.6.4-.4.8-.9 1-1.6.2-.7.4-1.4.4-2.2 0-.8-.1-1.6-.4-2.3-.2-.7-.6-1.2-1-1.6-.4-.4-1-.6-1.6-.6-.6 0-1.2.2-1.6.6-.4.4-.8.9-1 1.6-.2.7-.4 1.4-.4 2.3 0 .8.1 1.6.4 2.2.2.7.6 1.2 1 1.6.5.4 1 .6 1.6.6zm11.5 1.8c-.7 0-1.4-.2-2-.5-.6-.3-1.1-.8-1.5-1.3-.4-.6-.8-1.2-1-2-.2-.8-.3-1.6-.3-2.4 0-.9.1-1.7.3-2.5.2-.8.6-1.4 1-2 .4-.6.9-1 1.5-1.3.6-.3 1.3-.5 2-.5.8 0 1.4.2 2 .5.6.3 1.1.8 1.5 1.3.4.6.8 1.2 1 2 .2.8.3 1.6.3 2.5 0 .9-.1 1.7-.3 2.4-.2.8-.5 1.4-1 2-.4.6-.9 1-1.5 1.3-.6.3-1.3.5-2 .5zm0-1.8c.6 0 1.1-.2 1.6-.6.4-.4.8-.9 1-1.6.2-.7.4-1.4.4-2.2 0-.8-.1-1.6-.4-2.3-.2-.7-.6-1.2-1-1.6-.4-.4-1-.6-1.6-.6-.6 0-1.2.2-1.6.6-.4.4-.8.9-1 1.6-.2.7-.4 1.4-.4 2.3 0 .8.1 1.6.4 2.2.2.7.6 1.2 1 1.6.4.4.9.6 1.6.6zm10.8 1.5v-8.8h1.8V95h.1c.2-.3.4-.5.7-.8s.6-.4.9-.5c.4-.1.7-.2 1.1-.2.7 0 1.2.2 1.7.5s.8.7 1 1.2c.3-.5.7-.8 1.2-1.2.5-.3 1.1-.5 1.8-.5 1 0 1.8.3 2.3 1 .5.6.8 1.5.8 2.5v5.7h-1.9v-5.4c0-.7-.2-1.2-.5-1.5-.3-.3-.7-.5-1.3-.5-.4 0-.8.1-1.1.4-.3.2-.6.6-.8 1s-.3.9-.3 1.3v4.7h-1.9v-5.4c0-.7-.2-1.2-.5-1.5-.3-.3-.8-.5-1.3-.5-.4 0-.8.1-1.1.4-.3.2-.6.6-.7 1s-.3.9-.3 1.3v4.7h-1.7zm18.6.3c-.7 0-1.3-.1-1.8-.3-.5-.2-.9-.5-1.3-.9-.3-.4-.6-.8-.7-1.2l1.7-.7c.2.5.5.8.9 1.1.4.2.8.4 1.3.4s.9-.1 1.2-.2c.3-.2.5-.4.5-.8 0-.2-.1-.5-.2-.6-.2-.2-.4-.3-.6-.4l-.9-.3-1.1-.2c-.4-.1-.8-.3-1.2-.5-.4-.2-.7-.5-.9-.9-.2-.4-.3-.8-.3-1.2 0-.5.2-1 .5-1.4.3-.4.7-.7 1.2-.9.5-.2 1.1-.3 1.7-.3.6 0 1.1.1 1.5.2.5.1.9.3 1.2.6.3.3.6.6.8 1l-1.6.7c-.2-.4-.5-.7-.8-.8-.3-.2-.7-.2-1.1-.2-.4 0-.8.1-1.1.3-.3.2-.4.4-.4.7 0 .3.1.5.4.7.3.2.6.3 1 .4l1.3.3c.9.2 1.5.6 2 1 .4.4.7 1 .7 1.6 0 .6-.2 1.1-.5 1.5-.3.4-.8.7-1.3 1-.8.2-1.4.3-2.1.3zM0 113.3V99h1.7v14.3H0zm4.6 0v-10.2h1.6v1.5h.1c.3-.5.7-.9 1.3-1.3.6-.4 1.3-.5 2-.5 1.2 0 2.2.4 2.8 1.1.6.7 1 1.7 1 2.9v6.5h-1.7V107c0-1-.2-1.7-.7-2.1-.5-.4-1.1-.6-1.8-.6-.6 0-1.1.2-1.5.5-.4.3-.8.7-1 1.2-.2.5-.4 1-.4 1.6v5.7H4.6zm10-10.2h6v1.5h-6v-1.5zm1.8 7.5v-10.4h1.7v10c0 .5.1.9.3 1.2.2.3.6.4 1.1.4.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.5-.5-.7-1.2-.7-2.1zm10.3 3c-1 0-1.9-.2-2.6-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.6-1.7-.6-2.8 0-1 .2-1.9.6-2.7.4-.8 1-1.5 1.7-2s1.6-.8 2.6-.8 1.9.2 2.6.7c.7.4 1.3 1.1 1.7 1.8.4.8.6 1.7.6 2.7v.5h-8.8V107h7c0-.3-.1-.6-.2-.9-.1-.3-.3-.6-.5-.9-.2-.3-.6-.5-.9-.7-.4-.2-.8-.3-1.4-.3-.7 0-1.2.2-1.7.5s-.8.8-1.1 1.4c-.3.6-.4 1.2-.4 2 0 .9.2 1.6.5 2.2.3.6.8 1 1.3 1.3.5.3 1.1.4 1.7.4.8 0 1.4-.2 1.8-.5.5-.4.9-.8 1.2-1.3l1.4.7c-.4.8-1 1.4-1.7 1.9-1 .5-1.9.8-3 .8zm6.7-.3v-10.2H35v1.6h.1c.1-.4.4-.7.7-1 .3-.3.7-.5 1.1-.7.4-.2.8-.2 1.2-.2h.7c.2 0 .3.1.5.2v1.8c-.2-.1-.5-.2-.7-.2-.2-.1-.5-.1-.7-.1-.5 0-1 .1-1.4.4-.4.3-.7.7-1 1.1-.2.5-.4 1-.4 1.5v5.7h-1.7zm10.4.3c-.8 0-1.4-.1-2-.4-.6-.3-1-.7-1.3-1.2-.3-.5-.5-1.1-.5-1.8 0-.8.2-1.4.6-1.9.4-.5.9-.9 1.5-1.2.7-.3 1.4-.4 2.2-.4.4 0 .9 0 1.2.1.4.1.7.2 1 .3.3.1.5.2.7.3v-.6c0-.8-.3-1.4-.8-1.8-.5-.5-1.2-.7-2-.7-.6 0-1.1.1-1.6.4-.5.2-.9.6-1.1 1l-1.3-1c.3-.4.6-.7 1-1 .4-.3.9-.5 1.4-.7.5-.2 1.1-.2 1.6-.2 1.4 0 2.5.4 3.2 1.1.8.7 1.2 1.7 1.2 3v6.5h-1.6v-1.5h-.1c-.2.3-.4.6-.7.8-.3.3-.7.5-1.1.7-.5.1-1 .2-1.5.2zm.1-1.5c.6 0 1.1-.1 1.6-.4.5-.3.9-.7 1.2-1.2.3-.5.5-1 .5-1.6-.3-.2-.7-.4-1.2-.5-.5-.1-1-.2-1.5-.2-1 0-1.7.2-2.1.6-.4.4-.7.9-.7 1.5s.2 1 .6 1.4c.4.2 1 .4 1.6.4zm11.7 1.5c-1 0-1.9-.2-2.7-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.7-1.7-.7-2.8 0-1 .2-2 .7-2.8.4-.8 1.1-1.5 1.8-1.9.8-.5 1.7-.7 2.7-.7 1.1 0 2.1.3 2.8.8.7.5 1.3 1.2 1.6 2l-1.5.6c-.2-.6-.6-1.1-1.1-1.4-.5-.3-1.1-.5-1.8-.5-.6 0-1.2.2-1.7.5s-.9.8-1.2 1.4c-.3.6-.5 1.3-.5 2 0 .8.2 1.4.5 2 .3.6.7 1 1.2 1.4.5.3 1.1.5 1.7.5.7 0 1.3-.2 1.9-.5.5-.3.9-.8 1.2-1.4l1.5.6c-.3.8-.9 1.5-1.6 2-.9.5-1.8.8-3 .8zm5.5-10.5h6v1.5h-6v-1.5zm1.8 7.5v-10.4h1.7v10c0 .5.1.9.3 1.2.2.3.6.4 1.1.4.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.4-.5-.7-1.2-.7-2.1zm6.5 2.7v-10.2h1.7v10.2h-1.7zm.9-12.1c-.3 0-.6-.1-.9-.4-.2-.2-.4-.5-.4-.9 0-.3.1-.6.4-.9.2-.2.5-.4.9-.4.3 0 .6.1.9.4.2.2.4.5.4.9 0 .3-.1.6-.4.9-.3.3-.6.4-.9.4zm8.1 12.4c-1 0-1.9-.2-2.7-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.7-1.7-.7-2.8 0-1 .2-1.9.7-2.8.4-.8 1.1-1.5 1.8-2 .8-.5 1.7-.7 2.7-.7 1 0 1.9.2 2.7.7.8.5 1.4 1.1 1.9 2 .4.8.7 1.7.7 2.7 0 1-.2 1.9-.7 2.8-.4.8-1.1 1.5-1.9 1.9-.8.5-1.7.8-2.7.8zm0-1.6c.6 0 1.2-.2 1.7-.5s1-.8 1.3-1.3c.3-.6.5-1.3.5-2.1s-.2-1.5-.5-2.1c-.3-.6-.8-1-1.3-1.3-.5-.3-1.1-.5-1.7-.5-.6 0-1.2.2-1.7.5s-1 .7-1.3 1.3c-.3.6-.5 1.3-.5 2.1s.2 1.5.5 2.1c.3.6.8 1 1.3 1.3.5.4 1.1.5 1.7.5zm7 1.3v-10.2H87v1.5h.1c.3-.5.7-.9 1.3-1.3.6-.4 1.3-.5 2-.5 1.2 0 2.2.4 2.8 1.1.6.7 1 1.7 1 2.9v6.5h-1.7V107c0-1-.2-1.7-.7-2.1-.5-.4-1.1-.6-1.8-.6-.6 0-1.1.2-1.5.5-.4.3-.8.7-1 1.2-.2.5-.4 1-.4 1.6v5.7h-1.7zm15.1-10.2h6v1.5h-6v-1.5zm1.8 7.5v-10.4h1.7v10c0 .5.1.9.3 1.2.2.3.6.4 1.1.4.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.4-.5-.7-1.2-.7-2.1zm10.5 3c-1 0-1.9-.2-2.7-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.7-1.7-.7-2.8 0-1 .2-1.9.7-2.8.4-.8 1.1-1.5 1.8-2 .8-.5 1.7-.7 2.7-.7 1 0 1.9.2 2.7.7.8.5 1.4 1.1 1.9 2 .4.8.7 1.7.7 2.7 0 1-.2 1.9-.7 2.8-.4.8-1.1 1.5-1.9 1.9-.8.5-1.7.8-2.7.8zm0-1.6c.6 0 1.2-.2 1.7-.5s1-.8 1.3-1.3c.3-.6.5-1.3.5-2.1s-.2-1.5-.5-2.1c-.3-.6-.8-1-1.3-1.3-.5-.3-1.1-.5-1.7-.5-.6 0-1.2.2-1.7.5s-1 .7-1.3 1.3c-.3.6-.5 1.3-.5 2.1s.2 1.5.5 2.1c.3.6.8 1 1.3 1.3.5.4 1.1.5 1.7.5zm12.2 1.3V99h2.1l7.2 11.4h.1l-.1-2.8V99h1.7v14.3h-1.8l-7.5-11.9h-.1l.1 2.8v9.2H125zm18.2.3c-1 0-1.9-.2-2.6-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.6-1.7-.6-2.8 0-1 .2-1.9.6-2.7.4-.8 1-1.5 1.7-2s1.6-.8 2.6-.8 1.9.2 2.6.7c.7.4 1.3 1.1 1.7 1.8.4.8.6 1.7.6 2.7v.5h-8.8V107h7c0-.3-.1-.6-.2-.9-.1-.3-.3-.6-.5-.9-.2-.3-.6-.5-.9-.7-.4-.2-.8-.3-1.4-.3-.7 0-1.2.2-1.7.5s-.8.8-1.1 1.4c-.3.6-.4 1.2-.4 2 0 .9.2 1.6.5 2.2.3.6.8 1 1.3 1.3.5.3 1.1.4 1.7.4.8 0 1.4-.2 1.8-.5.5-.4.9-.8 1.2-1.3l1.4.7c-.4.8-1 1.4-1.7 1.9-1 .5-1.9.8-3 .8zm5.5-.3 4.1-5.9h.2l2.9-4.3h2l-4 5.6h-.1l-3.1 4.6h-2zm.1-10.2h1.9l3.2 4.5h.1l4 5.7h-2l-3-4.5h-.1l-4.1-5.7zm9.9 0h6v1.5h-6v-1.5zm1.8 7.5v-10.4h1.7v10c0 .5.1.9.3 1.2.2.3.6.4 1.1.4.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.4-.5-.7-1.2-.7-2.1zm11.2 2.7V99h4.8c.8 0 1.5.2 2.2.5.7.4 1.2.8 1.6 1.5.4.6.6 1.4.6 2.2 0 .8-.2 1.6-.6 2.2-.4.6-.9 1.1-1.6 1.5-.7.4-1.4.5-2.2.5h-3.9v-1.6h4c.6 0 1-.1 1.4-.4.4-.3.7-.6.9-1 .2-.4.3-.8.3-1.2 0-.4-.1-.8-.3-1.2-.2-.4-.5-.7-.9-1-.4-.3-.9-.4-1.4-.4h-3.2v12.7h-1.7zm14 .3c-.8 0-1.4-.1-2-.4-.6-.3-1-.7-1.3-1.2-.3-.5-.5-1.1-.5-1.8 0-.8.2-1.4.6-1.9.4-.5.9-.9 1.5-1.2.7-.3 1.4-.4 2.2-.4.4 0 .9 0 1.2.1.4.1.7.2 1 .3.3.1.5.2.7.3v-.6c0-.8-.3-1.4-.8-1.8-.5-.5-1.2-.7-2-.7-.6 0-1.1.1-1.6.4-.5.2-.9.6-1.1 1l-1.3-1c.3-.4.6-.7 1-1 .4-.3.9-.5 1.4-.7.5-.2 1.1-.2 1.6-.2 1.4 0 2.5.4 3.2 1.1.8.7 1.2 1.7 1.2 3v6.5h-1.6v-1.5h-.1c-.2.3-.4.6-.7.8-.3.3-.7.5-1.1.7-.5.1-.9.2-1.5.2zm.2-1.5c.6 0 1.1-.1 1.6-.4.5-.3.9-.7 1.2-1.2.3-.5.5-1 .5-1.6-.3-.2-.7-.4-1.2-.5-.5-.1-1-.2-1.5-.2-1 0-1.7.2-2.1.6-.4.4-.7.9-.7 1.5s.2 1 .6 1.4c.4.2 1 .4 1.6.4zm7.4 1.2v-10.2h1.7v10.2h-1.7zm.8-12.1c-.3 0-.6-.1-.9-.4-.2-.2-.4-.5-.4-.9 0-.3.1-.6.4-.9.2-.2.5-.4.9-.4.3 0 .6.1.9.4.2.2.4.5.4.9 0 .3-.1.6-.4.9-.3.3-.5.4-.9.4zm3.5 12.1v-10.2h1.6v1.5h.1c.3-.5.7-.9 1.3-1.3.6-.4 1.3-.5 2-.5 1.2 0 2.2.4 2.8 1.1.6.7 1 1.7 1 2.9v6.5h-1.7V107c0-1-.2-1.7-.7-2.1-.5-.4-1.1-.6-1.8-.6-.6 0-1.1.2-1.5.5-.4.3-.8.7-1 1.2-.2.5-.4 1-.4 1.6v5.7h-1.7zm10-10.2h6v1.5h-6v-1.5zm1.8 7.5v-10.4h1.7v10c0 .5.1.9.3 1.2.2.3.6.4 1.1.4.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.4-.5-.7-1.2-.7-2.1zM418.1 55.2c-1.8 0-3.3 1.5-3.3 3.3 0 1.5 1 2.7 2.3 3.1V83h2V61.6c1.3-.4 2.3-1.7 2.3-3.1 0-1.8-1.5-3.3-3.3-3.3zM546.5 58.6c0-1.8-1.5-3.3-3.3-3.3s-3.3 1.5-3.3 3.3c0 1.5 1 2.7 2.3 3.1v21.2h2V61.7c1.3-.4 2.3-1.6 2.3-3.1z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;whats-in-an-interaction&quot;&gt;What&#39;s in an interaction? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/inp/#whats-in-an-interaction&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A diagram depicting an interaction on the main thread. The user makes an input while blocking tasks run. The input is delayed until those tasks complete, after which the pointerup, mouseup, and click event handlers run, then rendering and painting work is kicked off until the next frame is presented.&quot; decoding=&quot;async&quot; height=&quot;193&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Ng0j5yaGYZX9Bm3VQ70c.svg&quot; width=&quot;736&quot; /&gt;
  &lt;figcaption&gt;
    The life of an interaction. An input delay occurs until event handlers begin running, which may be caused by factors such as long tasks on the main thread. The interaction&#39;s event handlers then run, and a delay occurs before the next frame is presented.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The primary driver of interactivity is often JavaScript, though browsers do provide interactivity through controls &lt;em&gt;not&lt;/em&gt; powered by JavaScript, such as checkboxes, radio buttons, and controls powered by CSS.&lt;/p&gt;
&lt;p&gt;As far as INP goes, &lt;strong&gt;only the following interaction types are observed:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Clicking with a mouse.&lt;/li&gt;
&lt;li&gt;Tapping on a device with a touchscreen.&lt;/li&gt;
&lt;li&gt;Pressing a key on either a physical or onscreen keyboard.&lt;/li&gt;
&lt;/ul&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; Hovering and scrolling does not factor into INP. However, scrolling with the keyboard (space bar, page up, page down, and so forth) involves a keystroke, which may trigger other events that INP &lt;em&gt;does&lt;/em&gt; measure. Any resulting scrolling is &lt;em&gt;not&lt;/em&gt; factored into how INP is calculated. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Interactions happen in the main document or in iframes embedded in the document—for example clicking play on an embedded video. End users will not be aware what is in an iframe or not. Therefore, INP within iframes are needed to measure the user experience for the top level page. Note JavaScript Web APIs will not have access to the iframe contents so may not be able to measure INP within an iframe and 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;.&lt;/p&gt;
&lt;p&gt;Interactions may consist of two parts, each with multiple events. For example, a keystroke consists of the &lt;code&gt;keydown&lt;/code&gt;, &lt;code&gt;keypress&lt;/code&gt;, and &lt;code&gt;keyup&lt;/code&gt; events. Tap interactions contain &lt;code&gt;pointerup&lt;/code&gt; and &lt;code&gt;pointerdown&lt;/code&gt; events. The event with the longest duration within the interaction is chosen as the interaction&#39;s latency.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A depiction of more complex interaction containing two interactions. The first is a mousedown event, which produces a frame before the mouse button is let up, which kicks off more work until yet another frame is presented as the result.&quot; decoding=&quot;async&quot; height=&quot;164&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/vNosnKDYgBRnFmEvwm0c.svg&quot; width=&quot;736&quot; /&gt;
  &lt;figcaption&gt;
    A depiction of an interaction with multiple event handlers. The first part of the interaction receives an input when the user clicks down on a mouse button. However, before they release the mouse button, a frame is presented. When the user releases the mouse button, another series of event handlers must run before the next frame is presented.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;INP is calculated when the user leaves the page, resulting in a single value that is representative of the page&#39;s overall responsiveness throughout the entire page&#39;s lifecycle. &lt;strong&gt;A low INP means that a page is reliably responsive to user input.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;how-is-inp-different-from-first-input-delay-fid&quot;&gt;How is INP different from First Input Delay (FID)? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/inp/#how-is-inp-different-from-first-input-delay-fid&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Where INP considers &lt;em&gt;all&lt;/em&gt; page interactions, &lt;a href=&quot;https://web.dev/fid/&quot;&gt;First Input Delay (FID)&lt;/a&gt; only accounts for the &lt;em&gt;first&lt;/em&gt; interaction. It also only measures the first interaction&#39;s &lt;em&gt;input delay&lt;/em&gt;, not the time it takes to run event handlers, or the delay in presenting the next frame.&lt;/p&gt;
&lt;p&gt;Given that FID is also a &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/#types-of-metrics&quot;&gt;load responsiveness metric&lt;/a&gt;, the rationale behind it is that if the first interaction made with a page in the loading phase has little to no perceptible input delay, the page has made a good first impression.&lt;/p&gt;
&lt;p&gt;INP is more than about first impressions. By sampling all interactions, responsiveness can be assessed comprehensively, making INP a more reliable indicator of overall responsiveness than FID.&lt;/p&gt;
&lt;h3 id=&quot;what-if-no-inp-value-is-reported&quot;&gt;What if no INP value is reported? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/inp/#what-if-no-inp-value-is-reported&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It&#39;s possible that a page can return no INP value. This can happen for a number of reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The page was loaded, but the user never clicked, tapped, or pressed a key on their keyboard.&lt;/li&gt;
&lt;li&gt;The page was loaded, but the user interacted with the page using gestures that did not involve clicking, tapping, or using the keyboard. For example, scrolling or hovering over elements does not factor into how INP is calculated.&lt;/li&gt;
&lt;li&gt;The page is being accessed by a bot such as a search crawler or headless browser that has not been scripted to interact with the page.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-to-measure-inp&quot;&gt;How to measure INP &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/inp/#how-to-measure-inp&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;INP can be measured both in &lt;a href=&quot;https://web.dev/lab-and-field-data-differences/#field-data&quot;&gt;the field&lt;/a&gt; and in &lt;a href=&quot;https://web.dev/lab-and-field-data-differences/#lab-data&quot;&gt;the lab&lt;/a&gt; through a variety of tools.&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; The best way to measure your website&#39;s INP is by gathering metrics from actual users in the field. If you&#39;re accustomed to relying on lab data for assessing performance, take some time to read &lt;a href=&quot;https://web.dev/lab-and-field-data-differences/&quot;&gt;Why lab and field data can be different (and what to do about it)&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;in-the-field&quot;&gt;In the field &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/inp/#in-the-field&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Ideally, your journey in optimizing INP will start with field data. At its best, field data from Real User Monitoring (RUM) will give you not only a page&#39;s INP value, but also contextual data that highlights what specific interaction was responsible for the INP value itself, whether the interaction occurred during or after page load, the type of interaction (click, keypress, or tap), and other valuable information.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-good-bg color-state-good-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; height=&quot;24&quot; width=&quot;24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Check&quot;&gt;   &lt;path d=&quot;M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Objective&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Read to learn more:&lt;/strong&gt; &lt;a href=&quot;https://web.dev/find-slow-interactions-in-the-field/&quot;&gt;Find slow interactions in the field&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;If your website qualifies for inclusion in 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;, you can quickly get field data for INP &lt;a href=&quot;https://web.dev/find-slow-interactions-in-the-field/#get-field-data-quickly-with-crux&quot;&gt;via CrUX in PageSpeed Insights&lt;/a&gt; (and other Core Web Vitals). At a minimum, you can get an origin-level picture of your website&#39;s INP, but in some cases, you can also get page-level data as well.&lt;/p&gt;
&lt;p&gt;However, while CrUX is useful to tell you that there &lt;em&gt;is&lt;/em&gt; a problem at a high level, it often doesn&#39;t provide enough detail to help fully understand what the problem is. A RUM solution can help you drill down into more detail as to the pages, users or user interactions which are experiencing slow interactions. Being able to attribute INP to individual interactions avoids guesswork and wasted effort.&lt;/p&gt;
&lt;h3 id=&quot;in-the-lab&quot;&gt;In the lab &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/inp/#in-the-lab&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Optimally, you&#39;ll want to start testing in the lab once you have field data that suggests you have slow interactions. In the absence of field data, however, there are some strategies for reproducing slow interactions in the lab. Such strategies include following common user flows and testing interactions along the way, as well as interacting with the page during load—when the main thread is often busiest—in order to surface slow interactions during that crucial part of the user experience.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-good-bg color-state-good-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; height=&quot;24&quot; width=&quot;24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Check&quot;&gt;   &lt;path d=&quot;M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Objective&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Read to learn more:&lt;/strong&gt; &lt;a href=&quot;https://web.dev/diagnose-slow-interactions-in-the-lab/&quot;&gt;Diagnose slow interactions in the lab&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;how-to-improve-inp&quot;&gt;How to improve INP &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/inp/#how-to-improve-inp&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A &lt;a href=&quot;https://web.dev/optimize-inp/&quot;&gt;collection of guides on optimizing INP&lt;/a&gt; is available to guide you through the process of identifying slow interactions in the field and using lab data to drill down and optimize them in a variety of ways.&lt;/p&gt;
&lt;h2 id=&quot;changelog&quot;&gt;CHANGELOG &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/inp/#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>Jeremy Wagner</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>Assessing loading performance in the field with Navigation Timing and Resource Timing</title>
    <link href="https://web.dev/navigation-and-resource-timing/"/>
    <updated>2021-10-08T00:00:00Z</updated>
    <id>https://web.dev/navigation-and-resource-timing/</id>
    <content type="html" mode="escaped">&lt;p&gt;If you&#39;ve used connection throttling in the network panel in a browser&#39;s developer tools (or &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt; in Chrome) to assess loading performance, you know how convenient those tools are for performance tuning. You can quickly measure the impact of performance optimizations with a consistent and stable baseline connection speed. The only problem is that this is synthetic testing, which yields &lt;a href=&quot;https://web.dev/lab-and-field-data-differences/#lab-data&quot;&gt;lab data&lt;/a&gt;, not &lt;a href=&quot;https://web.dev/lab-and-field-data-differences/#field-data&quot;&gt;field data&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Synthetic testing isn&#39;t inherently &lt;em&gt;bad&lt;/em&gt;, but it&#39;s not representative of how fast your website is loading for real users. That requires field data, which you can collect from the Navigation Timing and Resource Timing APIs.&lt;/p&gt;
&lt;h2 id=&quot;apis-to-help-you-assess-loading-performance-in-the-field&quot;&gt;APIs to help you assess loading performance in the field &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/navigation-and-resource-timing/#apis-to-help-you-assess-loading-performance-in-the-field&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Navigation Timing and Resource Timing are two similar APIs with significant overlap that measure two distinct things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://w3c.github.io/navigation-timing/&quot; rel=&quot;noopener&quot;&gt;Navigation Timing&lt;/a&gt; measures the speed of requests for HTML documents (that is, navigation requests).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://w3c.github.io/resource-timing/&quot; rel=&quot;noopener&quot;&gt;Resource Timing&lt;/a&gt; measures the speed of requests for document-dependent resources such as CSS, JavaScript, images, and so on.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These APIs expose their data in a &lt;em&gt;performance entry buffer&lt;/em&gt;, which can be accessed in the browser with JavaScript. There are multiple ways to query a performance buffer, but a common way is by using &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Performance/getEntriesByType&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;performance.getEntriesByType&lt;/code&gt;&lt;/a&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 comment&quot;&gt;// Get Navigation Timing entries:&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;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;&lt;span class=&quot;token comment&quot;&gt;// Get Resource Timing entries:&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;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;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;performance.getEntriesByType&lt;/code&gt; accepts a string describing the type of entries you want to retrieve from the performance entry buffer. &lt;code&gt;&#39;navigation&#39;&lt;/code&gt; and &lt;code&gt;&#39;resource&#39;&lt;/code&gt; retrieve timings for the Navigation Timing and Resource Timing APIs, respectively.&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; Try loading a website and then enter either of the commands in the above code snippet in your browser&#39;s console to see actual timings captured by your browser. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The amount of information these APIs provide can be overwhelming, but they&#39;re your key to measuring loading performance in the field, as you can gather these timings from users as they visit your website.&lt;/p&gt;
&lt;h2 id=&quot;the-life-and-timings-of-a-network-request&quot;&gt;The life and timings of a network request &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/navigation-and-resource-timing/#the-life-and-timings-of-a-network-request&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Gathering and analyzing navigation and resource timings is sort of like archeology in that you&#39;re reconstructing the fleeting life of a network request after the fact. Sometimes it helps to visualize concepts, and where network requests are concerned, your browser&#39;s developer tools can help.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;A diagram of network timings as shown in Chrome&amp;#x27;s DevTools. The timings depicted are for request queueing, connection negotiation, the request itself, and the response in color-coded bars.&quot; decoding=&quot;async&quot; height=&quot;583&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/BxgZyzAEyFnk846wvzNa.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/BxgZyzAEyFnk846wvzNa.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/BxgZyzAEyFnk846wvzNa.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/BxgZyzAEyFnk846wvzNa.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/BxgZyzAEyFnk846wvzNa.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/BxgZyzAEyFnk846wvzNa.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/BxgZyzAEyFnk846wvzNa.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/BxgZyzAEyFnk846wvzNa.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/BxgZyzAEyFnk846wvzNa.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/BxgZyzAEyFnk846wvzNa.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/BxgZyzAEyFnk846wvzNa.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/BxgZyzAEyFnk846wvzNa.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/BxgZyzAEyFnk846wvzNa.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/BxgZyzAEyFnk846wvzNa.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/BxgZyzAEyFnk846wvzNa.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/BxgZyzAEyFnk846wvzNa.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/BxgZyzAEyFnk846wvzNa.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/BxgZyzAEyFnk846wvzNa.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;A visualization of a network request in the &lt;a href=&quot;https://developer.chrome.com/docs/devtools/network/&quot; rel=&quot;noopener&quot;&gt;network panel of Chrome&#39;s DevTools&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The life of a network request has distinct phases, such as DNS lookup, connection establishment, TLS negotiation, and so on. These timings are represented as a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;DOMHighResTimestamp&lt;/code&gt;&lt;/a&gt;. Depending on your browser, the granularity of timings may be down to the microsecond, or be rounded up to milliseconds. Let&#39;s examine these phases in detail, and how they relate to Navigation Timing and Resource Timing.&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 read this guide, &lt;a href=&quot;https://www.w3.org/TR/navigation-timing-2/#processing-model&quot;&gt;this diagram&lt;/a&gt; for both Navigation Timing and Resource Timing may help you to visualize the order of the timings they provide. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;dns-lookup&quot;&gt;DNS lookup &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/navigation-and-resource-timing/#dns-lookup&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When a user goes to a URL, the Domain Name System (DNS) is queried to translate a domain to an IP address. This process may take significant time—time you&#39;ll want to measure in the field, even. Navigation Timing and Resource Timing expose two DNS-related timings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;domainLookupStart&lt;/code&gt; is when DNS lookup begins.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;domainLookupEnd&lt;/code&gt; is when DNS lookup ends.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Calculating total DNS lookup time can be done by subtracting the start metric from the end metric:&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;// Measuring DNS lookup time&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; 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;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; totalLookupTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pageNav&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainLookupEnd &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; pageNav&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;domainLookupStart&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; You can&#39;t &lt;em&gt;always&lt;/em&gt; rely on timings to be populated. Timings provided in both APIs will have a value of &lt;code&gt;0&lt;/code&gt; in some cases. For example, a DNS lookup may be served by a local cache. Additionally, any timings for cross-origin requests may be unavailable if those origins don&#39;t 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; response header&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;connection-negotiation&quot;&gt;Connection negotiation &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/navigation-and-resource-timing/#connection-negotiation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Another contributing factor to loading performance is connection negotiation, which is latency incurred when connecting to a web server. If HTTPS is involved, this process will also include TLS negotiation time. The connection phase consists of three timings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;connectStart&lt;/code&gt; is when the browser starts to open a connection to a web server.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;secureConnectionStart&lt;/code&gt; marks when the client begins TLS negotiation.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;connectEnd&lt;/code&gt; is when the connection to the web server has been established.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Measuring total connection time is similar to measuring total DNS lookup time: you subtract the start timing from the end timing. However, there&#39;s an additional &lt;code&gt;secureConnectionStart&lt;/code&gt; property that may be &lt;code&gt;0&lt;/code&gt; if HTTPS isn&#39;t used or &lt;a href=&quot;https://en.wikipedia.org/wiki/HTTP_persistent_connection&quot; rel=&quot;noopener&quot;&gt;if the connection is persistent&lt;/a&gt;. If you want to measure TLS negotiation time, you&#39;ll need to keep that in mind:&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;// Quantifying total connection time&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; 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;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; connectionTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pageNav&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connectEnd &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; pageNav&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connectStart&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; tlsTime &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;span class=&quot;token comment&quot;&gt;// &amp;lt;-- Assume 0 to start with&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Was there TLS negotiation?&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;pageNav&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secureConnectionStart &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;  &lt;span class=&quot;token comment&quot;&gt;// Awesome! Calculate it!&lt;/span&gt;&lt;br /&gt;  tlsTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pageNav&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connectEnd &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; pageNav&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;secureConnectionStart&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;Once DNS lookup and connection negotiation ends, timings related to fetching documents and their dependent resources come into play.&lt;/p&gt;
&lt;h3 id=&quot;requests-and-responses&quot;&gt;Requests and responses &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/navigation-and-resource-timing/#requests-and-responses&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Loading performance is affected by two types of factors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Extrinsic factors:&lt;/strong&gt; These are things like latency and bandwidth. Beyond choosing a hosting company and a CDN, they&#39;re (mostly) out of our control, as users can access the web from anywhere.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Intrinsic factors:&lt;/strong&gt; These are things like server and client-side architectures, as well as resource size and our ability to optimize for those things, which are within our control.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both types of factors affect loading performance. Timings related to these factors are vital, as they describe how long resources take to download. Both Navigation Timing and Resource Timing describe loading performance with the following metrics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fetchStart&lt;/code&gt; marks when the browser begins to fetch a resource (Resource Timing) or a document for a navigation request (Navigation Timing). This precedes the actual request, and is the point at which the browser is checking caches (for example, HTTP and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Cache&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Cache&lt;/code&gt; instances&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;workerStart&lt;/code&gt; marks when a request starts being handled within a service worker&#39;s &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/FetchEvent&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;fetch&lt;/code&gt; event handler&lt;/a&gt;. This will be &lt;code&gt;0&lt;/code&gt; when no service worker is controlling the current page.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;requestStart&lt;/code&gt; is when the browser makes the request.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;responseStart&lt;/code&gt; is when the first byte of the response arrives.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;responseEnd&lt;/code&gt; is when the last byte of the response arrives.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These timings allow you to measure multiple aspects of loading performance, such as cache lookup within a service worker &lt;em&gt;and&lt;/em&gt; download time:&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;// Cache seek plus response time of the current document&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; 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;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fetchTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pageNav&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;responseEnd &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; pageNav&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;fetchStart&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;// Service worker time plus response time&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; workerTime &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;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;pageNav&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;workerStart &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;  workerTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pageNav&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;responseEnd &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; pageNav&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;workerStart&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;You can also measure other aspects of request/response latency:&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;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; 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;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Request time only (excluding redirects, DNS, and connection/TLS time)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; requestTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pageNav&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;responseStart &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; pageNav&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;requestStart&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;// Response time only (download)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; responseTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pageNav&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;responseEnd &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; pageNav&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;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Request + response time&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; requestResponseTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pageNav&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;responseEnd &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; pageNav&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;requestStart&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;other-measurements-you-can-make&quot;&gt;Other measurements you can make &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/navigation-and-resource-timing/#other-measurements-you-can-make&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Navigation Timing and Resource Timing is useful for more than what the examples above outline. Here are some other situations with relevant timings that may be worth exploring:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Page redirects:&lt;/strong&gt; Redirects are an overlooked source of added latency, especially redirect chains. Latency gets added in a number of ways, such as HTTP-to-HTTPs hops, as well as 302/uncached 301 redirects. The &lt;code&gt;redirectStart&lt;/code&gt;, &lt;code&gt;redirectEnd&lt;/code&gt;, and &lt;code&gt;redirectCount&lt;/code&gt; timings are helpful in assessing redirect latency.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Document unloading:&lt;/strong&gt; In pages that run code in an &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Events/unload&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;unload&lt;/code&gt; event handler&lt;/a&gt;, the browser must execute that code before it can navigate to the next page. &lt;code&gt;unloadEventStart&lt;/code&gt; and &lt;code&gt;unloadEventEnd&lt;/code&gt; measure document unloading.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Document processing:&lt;/strong&gt; Document processing time may not be consequential unless your website sends very large HTML payloads. If this describes your situation, the &lt;code&gt;domInteractive&lt;/code&gt;, &lt;code&gt;domContentLoadedEventStart&lt;/code&gt;, &lt;code&gt;domContentLoadedEventEnd&lt;/code&gt;, and &lt;code&gt;domComplete&lt;/code&gt; timings may be of interest.&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; Timings related to document unloading and processing are available only in Navigation Timing, as they only apply to navigation requests. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;acquiring-timings-in-application-code&quot;&gt;Acquiring timings in application code &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/navigation-and-resource-timing/#acquiring-timings-in-application-code&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;All of the examples shown so far use &lt;code&gt;performance.getEntriesByType&lt;/code&gt;, but there are other ways to query the performance entry buffer, such as &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Performance/getEntriesByName&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;performance.getEntriesByName&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Performance/getEntries&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;performance.getEntries&lt;/code&gt;&lt;/a&gt;. These methods are fine when only light analysis is needed. In other situations, though, they can introduce excessive main thread work by iterating over a large number of entries, or even repeatedly polling the performance buffer to find new entries.&lt;/p&gt;
&lt;p&gt;The recommended approach for collecting entries from the performance entry buffer is to use 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;. &lt;code&gt;PerformanceObserver&lt;/code&gt; listens for performance entries, and provides them as they&#39;re added to the buffer:&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;// Create the performance observer:&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; perfObserver &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;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;observedEntries&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;// Get all resource entries collected so far:&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; observedEntries&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 comment&quot;&gt;// Iterate over entries:&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;let&lt;/span&gt; i &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; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; entries&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&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 comment&quot;&gt;// Do the work!&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;// Run the observer for Navigation Timing entries:&lt;/span&gt;&lt;br /&gt;perfObserver&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;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Run the observer for Resource Timing entries:&lt;/span&gt;&lt;br /&gt;perfObserver&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;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Adding the &lt;code&gt;buffered&lt;/code&gt; option to a performance observer&#39;s &lt;code&gt;observe&lt;/code&gt; event ensures that performance entries added to the buffer prior to the instantiation of the performance observer are observable. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;This method of collecting timings may feel awkward when compared to directly accessing the performance entry buffer, but it&#39;s preferable to tying up the main thread with work that doesn&#39;t serve a critical and user-facing purpose.&lt;/p&gt;
&lt;h2 id=&quot;phoning-home&quot;&gt;Phoning home &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/navigation-and-resource-timing/#phoning-home&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once you&#39;ve collected all the timings you need, you can send them to an endpoint for further analysis. Two ways to do this are with either &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; or a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/fetch&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;fetch&lt;/code&gt; with the &lt;code&gt;keepalive&lt;/code&gt; option&lt;/a&gt; set. Both methods will send a request to a specified endpoint in a non-blocking way, and the request will be queued in a way that outlives the current page session if need be:&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;// Caution: If you have lots of performance entries, don&#39;t&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// do this. This is an example for illustrative purposes.&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;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 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;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// The endpoint to transmit the encoded data to&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; endpoint &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/analytics&#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;// Check for fetch keepalive support&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 string&quot;&gt;&#39;keepalive&#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;Request&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 function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;endpoint&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;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;headers&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-property property&quot;&gt;&#39;Content-Type&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#39;&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;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 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;sendBeacon&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; navigator&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;// Use sendBeacon as a fallback&lt;/span&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;endpoint&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;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 this example, the JSON string will arrive in a &lt;code&gt;POST&lt;/code&gt; payload that you can decode and process/store in an application backend as needed.&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/navigation-and-resource-timing/#wrapping-up&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once you have metrics collected, it&#39;s up to you to figure out how to analyze that field data. When analyzing field data, there are a few general rules to follow to ensure you&#39;re drawing meaningful conclusions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.igvita.com/2016/01/12/the-average-page-is-a-myth/&quot; rel=&quot;noopener&quot;&gt;Avoid averages&lt;/a&gt;, as they&#39;re not representative of any one user&#39;s experience, and may be skewed by outliers.&lt;/li&gt;
&lt;li&gt;Rely on percentiles. In datasets of time-based performance metrics, lower is better. This means that when you prioritize low percentiles, you&#39;re only paying attention to the fastest experiences.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://timkadlec.com/remembers/2018-06-07-prioritizing-the-long-tail-of-performance/&quot; rel=&quot;noopener&quot;&gt;Prioritize the long tail of values&lt;/a&gt;. When you prioritize experiences at the 75th percentile or higher, you&#39;re putting your focus where it belongs: on the slowest experiences.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This guide isn&#39;t meant to be an exhaustive resource on Navigation or Resource Timing, but a starting point. Below are some additional resources you may find helpful:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/navigation-timing-2/&quot; rel=&quot;noopener&quot;&gt;Navigation Timing Spec&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 Spec&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nicj.net/resourcetiming-in-practice/&quot; rel=&quot;noopener&quot;&gt;ResourceTiming in Practice&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Navigation_timing_API&quot; rel=&quot;noopener&quot;&gt;Navigation Timing API (MDN)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Resource_Timing_API&quot; rel=&quot;noopener&quot;&gt;Resource Timing API (MDN)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With these APIs and the data they provide, you&#39;ll be better equipped to understand how loading performance is experienced by real users, which will give you more confidence in diagnosing and addressing loading performance problems in the field.&lt;/p&gt;
</content>
    <author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Using AVIF to compress images on your site</title>
    <link href="https://web.dev/compress-images-avif/"/>
    <updated>2021-06-07T00:00:00Z</updated>
    <id>https://web.dev/compress-images-avif/</id>
    <content type="html" mode="escaped">&lt;p&gt;We
&lt;a href=&quot;https://web.dev/fast/#optimize-your-images&quot;&gt;frequently write&lt;/a&gt;
about the bloat on websites from images,
and tools like
&lt;a href=&quot;https://web.dev/optimize-vitals-lighthouse/&quot;&gt;Lighthouse&lt;/a&gt;
highlight when image loading is having a negative impact on user experience,
such as increasing load time,
or taking bandwidth away from more important resources.
One way to fix this is to use modern compression to reduce the file size of images,
and a new option for web developers is the
&lt;a href=&quot;https://aomediacodec.github.io/av1-avif/&quot; rel=&quot;noopener&quot;&gt;AVIF image format&lt;/a&gt;.
This blog post talks about recent updates to open source tooling for AVIF,
introduces the libaom and libavif encoding libraries,
and includes a tutorial for using these libraries to encode AVIF images efficiently.&lt;/p&gt;
&lt;p&gt;AVIF is an image format based on the AV1 video codec,
and standardized by the
&lt;a href=&quot;https://aomedia.org/&quot; rel=&quot;noopener&quot;&gt;Alliance for Open Media&lt;/a&gt;.
AVIF offers significant compression gains over other image formats like JPEG and WebP.
While the exact savings will depend on the content, encoding settings, and quality target,
&lt;a href=&quot;https://jakearchibald.com/2020/avif-has-landed/&quot; rel=&quot;noopener&quot;&gt;we&lt;/a&gt; and
&lt;a href=&quot;https://netflixtechblog.com/avif-for-next-generation-image-coding-b1d75675fe4&quot; rel=&quot;noopener&quot;&gt;others&lt;/a&gt;
have seen greater than 50% savings vs. JPEG.&lt;/p&gt;
&lt;div class=&quot;switcher&quot;&gt;
&lt;figure&gt;
  &lt;a href=&quot;https://storage.googleapis.com/web-dev-uploads/image/foR0vJZKULb5AGJExlazy1xYDgI2/kVqh1xli2O6mqKF3fQNx.avif&quot; target=&quot;_blank&quot;&gt;
    &lt;img src=&quot;https://storage.googleapis.com/web-dev-uploads/image/foR0vJZKULb5AGJExlazy1xYDgI2/kVqh1xli2O6mqKF3fQNx.avif&quot; width=&quot;1120&quot; height=&quot;840&quot; alt=&quot;The image using AVIF&quot; /&gt;
  &lt;/a&gt;
  &lt;figcaption&gt;
  1120 by 840 AVIF at 18,769 bytes (click to enlarge)
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
  &lt;a href=&quot;https://storage.googleapis.com/web-dev-uploads/image/foR0vJZKULb5AGJExlazy1xYDgI2/Jy0O0q0mLXl668HAo43n.jpeg&quot; target=&quot;_blank&quot;&gt;
    &lt;img src=&quot;https://storage.googleapis.com/web-dev-uploads/image/foR0vJZKULb5AGJExlazy1xYDgI2/Jy0O0q0mLXl668HAo43n.jpeg&quot; width=&quot;1120&quot; height=&quot;840&quot; alt=&quot;The image using JPEG&quot; /&gt;
  &lt;/a&gt;
  &lt;figcaption&gt;
  1120 by 840 JPEG at 20,036 bytes (click to enlarge)
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;Additionally, AVIF adds codec and container support for new image features such as
&lt;a href=&quot;https://w3c.github.io/ColorWeb-CG/&quot; rel=&quot;noopener&quot;&gt;High Dynamic Range and Wide Color Gamut&lt;/a&gt;,
&lt;a href=&quot;https://norkin.org/research/film_grain/&quot; rel=&quot;noopener&quot;&gt;film grain synthesis&lt;/a&gt;,
and progressive decoding.&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; AVIF supports two types of progressive decoding. Spatial scalability can be used to offer a lower resolution image for network constrained users and &#39;progressively&#39; provide a higher resolution image by sending just the additional data required to fill in the high frequency details. Quality scalability offers a similar progression by steadily improving visual quality with each render. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;whats-new&quot;&gt;What&#39;s New &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/compress-images-avif/#whats-new&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Since landing AVIF support in Chrome M85,
AVIF support in the open source ecosystem has improved on a number of fronts.&lt;/p&gt;
&lt;h3 id=&quot;libaom&quot;&gt;Libaom &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/compress-images-avif/#libaom&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://aomedia.googlesource.com/aom/&quot; rel=&quot;noopener&quot;&gt;Libaom&lt;/a&gt;
is an open source AV1 encoder and decoder maintained by the companies in the Alliance for Open Media,
and used in many production services at Google and other member companies.
Between the libaom 2.0.0 release—around the same time Chrome added AVIF support—and the recent 3.1.0 release,
there have been significant still image encoding optimizations added to the codebase.
These include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Optimizations for multi-threading and tiled encoding.&lt;/li&gt;
&lt;li&gt;5x reduction in memory usage.&lt;/li&gt;
&lt;li&gt;6.5x reduction in CPU usage, as shown in the chart below.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
  &lt;img alt=&quot;&quot; decoding=&quot;async&quot; height=&quot;467&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/mJJfiNsC7Qgl98IJ1LNi.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/mJJfiNsC7Qgl98IJ1LNi.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/mJJfiNsC7Qgl98IJ1LNi.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/mJJfiNsC7Qgl98IJ1LNi.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/mJJfiNsC7Qgl98IJ1LNi.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/mJJfiNsC7Qgl98IJ1LNi.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/mJJfiNsC7Qgl98IJ1LNi.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/mJJfiNsC7Qgl98IJ1LNi.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/mJJfiNsC7Qgl98IJ1LNi.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/mJJfiNsC7Qgl98IJ1LNi.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/mJJfiNsC7Qgl98IJ1LNi.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/mJJfiNsC7Qgl98IJ1LNi.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/mJJfiNsC7Qgl98IJ1LNi.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/mJJfiNsC7Qgl98IJ1LNi.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/mJJfiNsC7Qgl98IJ1LNi.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/mJJfiNsC7Qgl98IJ1LNi.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/mJJfiNsC7Qgl98IJ1LNi.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/mJJfiNsC7Qgl98IJ1LNi.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Using speed=6, cq-level=18, for 8.1 MP images&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;These changes massively reduce the cost of encoding AVIF—
particularly the most frequently loaded,
or highest priority images on your site.
As &lt;a href=&quot;https://www.cnet.com/news/google-supercharges-youtube-with-a-custom-video-chip/&quot; rel=&quot;noopener&quot;&gt;hardware-accelerated encoding&lt;/a&gt;
of AV1 becomes more available on servers and cloud services,
the cost to create AVIF images will continue to drop.&lt;/p&gt;
&lt;h3 id=&quot;libavif&quot;&gt;Libavif &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/compress-images-avif/#libavif&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/AOMediaCodec/libavif&quot; rel=&quot;noopener&quot;&gt;Libavif&lt;/a&gt;,
the reference implementation of AVIF,
is an open source AVIF muxer and parser which is used in Chrome for decoding AVIF images.
It can also be used with libaom for creating AVIF images from your existing uncompressed images,
or transcoding from existing web images (JPEG, PNG, etc).&lt;/p&gt;
&lt;p&gt;Libavif recently added support for a wider range of encoder settings,
including integration with more advanced libaom encoder settings.
Optimizations in the processing pipeline like fast YUV-to-RGB conversion using libyuv
and premultiplied alpha support further speed up the decoding process.
And finally, support for the all-intra encoding mode newly added in libaom 3.1.0
brings all the libaom improvements mentioned in the above.&lt;/p&gt;
&lt;p&gt;Note: &lt;a href=&quot;https://github.com/strukturag/libheif&quot; rel=&quot;noopener&quot;&gt;libheif&lt;/a&gt;
is another popular open source AVIF muxer and parser,
used in
&lt;a href=&quot;https://imagemagick.org/&quot; rel=&quot;noopener&quot;&gt;ImageMagick&lt;/a&gt;,
&lt;a href=&quot;https://github.com/libvips/libvips&quot; rel=&quot;noopener&quot;&gt;libvips&lt;/a&gt;, and the
&lt;a href=&quot;https://sharp.pixelplumbing.com/&quot; rel=&quot;noopener&quot;&gt;sharp&lt;/a&gt; Node.js module.&lt;/p&gt;
&lt;h2 id=&quot;encoding-avif-images-with-avifenc&quot;&gt;Encoding AVIF images with avifenc &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/compress-images-avif/#encoding-avif-images-with-avifenc&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A quick way to experiment with AVIF is
&lt;a href=&quot;https://squoosh.app/&quot; rel=&quot;noopener&quot;&gt;Squoosh.app&lt;/a&gt;.
Squoosh runs a WebAssembly version of libavif,
and exposes many of the same features as the command line tools.
It&#39;s an easy way to compare AVIF to other formats old and new.
There&#39;s also a
&lt;a href=&quot;https://www.npmjs.com/package/@squoosh/cli&quot; rel=&quot;noopener&quot;&gt;CLI version&lt;/a&gt;
of Squoosh aimed at Node apps.&lt;/p&gt;
&lt;p&gt;However, WebAssembly doesn&#39;t yet have access to all the performance primitives of CPUs,
so if you want to run libavif at its fastest,
we recommend the command line encoder, avifenc.&lt;/p&gt;
&lt;p&gt;To understand how to encode AVIF images,
we will present a tutorial using the
&lt;a href=&quot;https://codelabs.developers.google.com/codelabs/avif/images/happy_dog.jpg&quot; rel=&quot;noopener&quot;&gt;same source image&lt;/a&gt;
used in our example above.  To start, you will need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.google.com/chrome/&quot; rel=&quot;noopener&quot;&gt;Chrome&lt;/a&gt; version 85 or later&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cmake.org/&quot; rel=&quot;noopener&quot;&gt;cmake&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://git-scm.com/&quot; rel=&quot;noopener&quot;&gt;git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ninja-build.org/&quot; rel=&quot;noopener&quot;&gt;ninja&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You will also need to install the development packages for zlib, libpng, and libjpeg.
The commands for the Debian and Ubuntu Linux distributions are:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; zlib1g-dev&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; libpng-dev&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; libjpeg-dev&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;building-command-line-encoder-avifenc&quot;&gt;Building command line encoder avifenc &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/compress-images-avif/#building-command-line-encoder-avifenc&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;1-get-the-code&quot;&gt;1. Get the code &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/compress-images-avif/#1-get-the-code&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Check out a release tag of libavif.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; clone -b v0.9.1 https://github.com/AOMediaCodec/libavif.git&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h4 id=&quot;2-change-directory-to-libavif&quot;&gt;2. Change directory to libavif &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/compress-images-avif/#2-change-directory-to-libavif&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; libavif&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;There are many different ways you can configure avifenc and libavif to build.
You can find more information at &lt;a href=&quot;https://github.com/AOMediaCodec/libavif&quot; rel=&quot;noopener&quot;&gt;libavif&lt;/a&gt;.
We are going to build avifenc so that it is statically linked to the
AV1 encoder and decoder library, &lt;a href=&quot;https://aomedia.googlesource.com/aom&quot; rel=&quot;noopener&quot;&gt;libaom&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;3-get-and-build-libaom&quot;&gt;3. Get and build libaom &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/compress-images-avif/#3-get-and-build-libaom&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Change to the libavif external dependencies directory.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; ext&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The next command will pull the libaom source code
and build libaom statically.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;./aom.cmd&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Change directory to libavif.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&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;4-build-the-command-line-encoding-tool,-avifenc&quot;&gt;4. Build the command line encoding tool, avifenc &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/compress-images-avif/#4-build-the-command-line-encoding-tool,-avifenc&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;It is a good idea to create a build directory for avifenc.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; build&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Change to the build directory.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; build&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Create the build files for avifenc.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;cmake -DCMAKE_BUILD_TYPE&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;Release -DBUILD_SHARED_LIBS&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; -DAVIF_CODEC_AOM&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; -DAVIF_LOCAL_AOM&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; -DAVIF_BUILD_APPS&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Build avifenc.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You have successfully built avifenc!&lt;/p&gt;
&lt;h3 id=&quot;understanding-the-avifenc-command-line-parameters&quot;&gt;Understanding the avifenc command line parameters &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/compress-images-avif/#understanding-the-avifenc-command-line-parameters&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;avifenc uses the command-line structure:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;./avifenc &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;options&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; input.file output.avif&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The basic parameters for avifenc used in this tutorial are:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;avifenc&lt;/th&gt;
      &lt;th&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;--min 0&lt;/td&gt;&lt;td&gt;Set min quantizer for color to 0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;--max 63&lt;/td&gt;&lt;td&gt;Set max quantizer for color to 63&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;--minalpha 0&lt;/td&gt;&lt;td&gt;Set min quantizer for alpha to 0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;--maxalpha 63&lt;/td&gt;&lt;td&gt;Set max quantizer for alpha to 63&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;-a end-usage=q&lt;/td&gt;&lt;td&gt;Set the rate control mode to Constant Quality (Q) mode&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;-a cq-level=Q&lt;/td&gt;&lt;td&gt;Set quantize level for both color and alpha to Q&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;-a color:cq-level=Q&lt;/td&gt;&lt;td&gt;Set quantize level for color to Q&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;-a alpha:cq-level=Q&lt;/td&gt;&lt;td&gt;Set quantize level for alpha to Q&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;-a tune=ssim&lt;/td&gt;&lt;td&gt;Tune for SSIM (default is to tune for PSNR)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;--jobs J&lt;/td&gt;&lt;td&gt;Use J worker threads (default: 1)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;--speed S&lt;/td&gt;&lt;td&gt;Set encoder speed from 0-10 (slowest-fastest. default: 6)&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The cq-level option sets the quantize level (0-63) to control the quality for color or alpha.&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 think of cq-level as the &amp;quot;amount&amp;quot; of quantization, so a lower value yields higher quality &lt;/div&gt;&lt;/aside&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Higher speed settings will run faster, but produce worse compression efficiency and quality. This tutorial uses the default setting of Speed 6, which we recommend as a balance of encode speed, compression efficiency, and quality. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;create-an-avif-image-with-default-settings&quot;&gt;Create an AVIF image with default settings &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/compress-images-avif/#create-an-avif-image-with-default-settings&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The most basic parameters for avifenc to run, are setting the input and output files.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;./avifenc happy_dog.jpg happy_dog.avif&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;We recommend the following command line to encode an image, say at quantize level 18:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;./avifenc --min &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; --max &lt;span class=&quot;token number&quot;&gt;63&lt;/span&gt; -a end-usage&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;q -a cq-level&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;18&lt;/span&gt; -a &lt;span class=&quot;token assign-left variable&quot;&gt;tune&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;ssim happy_dog.jpg happy_dog.avif&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;code&gt;&amp;quot;--min 0 --max 63 -a end-usage=q -a cq-level=18 -a tune=ssim&amp;quot;&lt;/code&gt; are the recommended settings for AVIF images. If the image has an alpha channel, add &lt;code&gt;&amp;quot;--minalpha 0 --maxalpha 63&amp;quot;&lt;/code&gt;. To specify different quantize levels for color and alpha, replace &lt;code&gt;&amp;quot;-a cq-level=18&amp;quot;&lt;/code&gt; with, say, &lt;code&gt;&amp;quot;-a color:cq-level=18 -a alpha:cq-level=10&amp;quot;&lt;/code&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Avifenc has a lot of options that will affect both quality and speed.
If you want to see the options and learn more about them just run &lt;code&gt;./avifenc&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You now have your very own AVIF image!&lt;/p&gt;
&lt;h3 id=&quot;speeding-up-the-encoder&quot;&gt;Speeding up the encoder &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/compress-images-avif/#speeding-up-the-encoder&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One parameter that may be good to change
depending on how many cores you have on your machine is the &lt;code&gt;--jobs&lt;/code&gt; parameter.
This parameter sets how many threads avifenc will use to create AVIF images.
Try running this at the command line.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;./avifenc --min &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; --max &lt;span class=&quot;token number&quot;&gt;63&lt;/span&gt; -a end-usage&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;q -a cq-level&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;18&lt;/span&gt; -a &lt;span class=&quot;token assign-left variable&quot;&gt;tune&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;ssim --jobs &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt; happy_dog.jpg happy_dog.avif&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This tells avifenc to use 8 threads when creating the AVIF image,
which speeds up AVIF encoding by roughly 5x.&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/compress-images-avif/#effects-on-largest-contentful-paint-lcp&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Images are a common candidate for the &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP) metric&lt;/a&gt;. One common recommendation for improving the loading speed of LCP images is to ensure that an image is optimized. By reducing a resource&#39;s transfer size, you&#39;re improving its &lt;em&gt;resource load time&lt;/em&gt;, which is &lt;a href=&quot;https://web.dev/optimize-lcp/#3-reduce-resource-load-time&quot;&gt;one of the four key phases&lt;/a&gt; to target when dealing with LCP candidates that are images.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/image-cdns/&quot;&gt;Using an image CDN&lt;/a&gt; is strongly recommended when optimizing images, as it requires much less effort than setting up image optimization pipelines in your website&#39;s build process or manually using encoder binaries to optimize images by hand. However, image CDNs may be cost-prohibitive for some projects. If this is the case for you, consider the following when optimizing with the avifenc encoder:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Familiarize yourself with &lt;a href=&quot;https://github.com/AOMediaCodec/libavif/blob/main/doc/avifenc.1.md&quot; rel=&quot;noopener&quot;&gt;the options the encoder offers&lt;/a&gt;. You may find additional savings while still retaining sufficient image quality by experimenting with some of AVIF&#39;s available encoding features.&lt;/li&gt;
&lt;li&gt;AVIF provides both lossy and lossless encoding. Depending on an image&#39;s contents, one type of encoding may perform better than another. For example, photographs which are normally served as JPEGs will probably do best with lossy encoding, whereas lossless encoding is likely best for images containing simple details or line art normally served as PNG.&lt;/li&gt;
&lt;li&gt;If using a bundler with community support for imagemin, consider using the &lt;a href=&quot;https://www.npmjs.com/package/imagemin-avif&quot; rel=&quot;noopener&quot;&gt;imagemin-avif&lt;/a&gt; package to enable your bundler to output AVIF image variants.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By experimenting with AVIF, you may be able to realize an improvement for your website&#39;s LCP times in cases where the LCP candidate is an image. For more information on optimizing LCP, read &lt;a href=&quot;https://web.dev/optimize-lcp/&quot;&gt;the guide on optimizing LCP&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/compress-images-avif/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Using libaom, libavif and other open source tooling,
you can get the best image quality and performance for your website using AVIF.
The format is still relatively new,
and optimizations and tooling integrations are actively being developed.
If you have questions, comments, or feature requests,
reach out on the
&lt;a href=&quot;https://groups.google.com/a/aomedia.org/g/av1-discuss&quot; rel=&quot;noopener&quot;&gt;av1-discuss mailing list&lt;/a&gt;,
&lt;a href=&quot;https://github.com/AOMediaCodec/community/wiki&quot; rel=&quot;noopener&quot;&gt;AOM GitHub community&lt;/a&gt;, and
&lt;a href=&quot;https://github.com/AOMediaCodec/av1-avif/wiki&quot; rel=&quot;noopener&quot;&gt;AVIF wiki&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Jai Krishnan</name>
    </author><author>
      <name>Wan-Teh Chang</name>
    </author><author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>`content-visibility`: the new CSS property that boosts your rendering performance</title>
    <link href="https://web.dev/content-visibility/"/>
    <updated>2020-08-05T00:00:00Z</updated>
    <id>https://web.dev/content-visibility/</id>
    <content type="html" mode="escaped">&lt;p&gt;The
&lt;a href=&quot;https://drafts.csswg.org/css-contain/#propdef-content-visibility&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;content-visibility&lt;/code&gt;&lt;/a&gt;
property, launching in Chromium 85, might be one of the most impactful new CSS
properties for improving page load performance. &lt;code&gt;content-visibility&lt;/code&gt; enables the
user agent to skip an element&#39;s rendering work, including layout and painting,
until it is needed. Because rendering is skipped, if a large portion of your
content is off-screen, leveraging the &lt;code&gt;content-visibility&lt;/code&gt; property makes the
initial user load much faster. It also allows for faster interactions with the
on-screen content. Pretty neat.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;demo with figures representing network results&quot; decoding=&quot;async&quot; height=&quot;554&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/v6WcSx9Fq76lCD0iqFCQ.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/v6WcSx9Fq76lCD0iqFCQ.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/v6WcSx9Fq76lCD0iqFCQ.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/v6WcSx9Fq76lCD0iqFCQ.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/v6WcSx9Fq76lCD0iqFCQ.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/v6WcSx9Fq76lCD0iqFCQ.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/v6WcSx9Fq76lCD0iqFCQ.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/v6WcSx9Fq76lCD0iqFCQ.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/v6WcSx9Fq76lCD0iqFCQ.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/v6WcSx9Fq76lCD0iqFCQ.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/v6WcSx9Fq76lCD0iqFCQ.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/v6WcSx9Fq76lCD0iqFCQ.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/v6WcSx9Fq76lCD0iqFCQ.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/v6WcSx9Fq76lCD0iqFCQ.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/v6WcSx9Fq76lCD0iqFCQ.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/v6WcSx9Fq76lCD0iqFCQ.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/v6WcSx9Fq76lCD0iqFCQ.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/v6WcSx9Fq76lCD0iqFCQ.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;In our article demo, applying &lt;code&gt;content-visibility: auto&lt;/code&gt; to chunked content areas gives a &lt;b&gt;7x&lt;/b&gt; rendering performance boost on initial load. Read on to learn more.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;support&quot;&gt;Browser support &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-visibility/#support&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 85, 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;
      85
    &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 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;/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 85, 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;
85
&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/CSS/content-visibility#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;content-visibility&lt;/code&gt; relies on primitives within the &lt;a href=&quot;http://drafts.csswg.org/css-contain/&quot; rel=&quot;noopener&quot;&gt;the CSS Containment
Spec&lt;/a&gt;. While &lt;code&gt;content-visibility&lt;/code&gt; is only
supported in Chromium 85 for now (and deemed &lt;a href=&quot;https://github.com/mozilla/standards-positions/issues/135&quot; rel=&quot;noopener&quot;&gt;&amp;quot;worth
prototyping&amp;quot;&lt;/a&gt; for
Firefox), the Containment Spec is supported in &lt;a href=&quot;https://caniuse.com/#feat=css-containment&quot; rel=&quot;noopener&quot;&gt;most modern
browsers&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;containment&quot;&gt;CSS Containment &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-visibility/#containment&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The key and overarching goal of CSS containment is to enable rendering
performance improvements of web content by providing &lt;strong&gt;predictable isolation of
a DOM subtree&lt;/strong&gt; from the rest of the page.&lt;/p&gt;
&lt;p&gt;Basically a developer can tell a browser what parts of the page are encapsulated
as a set of content, allowing the browsers to reason about the content without
needing to consider state outside of the subtree. Knowing which bits of content
(subtrees) contain isolated content means the browser can make optimization
decisions for page rendering.&lt;/p&gt;
&lt;p&gt;There are four types of &lt;a href=&quot;https://web.dev/css-containment/&quot;&gt;CSS
containment&lt;/a&gt;,
each a potential value for the &lt;code&gt;contain&lt;/code&gt; CSS property, which can be combined
in a space-separated list of values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;size&lt;/code&gt;: Size containment on an element ensures that the element&#39;s box can be
laid out without needing to examine its descendants. This means we can
potentially skip layout of the descendants if all we need is the size of the
element.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;layout&lt;/code&gt;: Layout containment means that the descendants do not affect the
external layout of other boxes on the page. This allows us to potentially skip
layout of the descendants if all we want to do is lay out other boxes.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;style&lt;/code&gt;: Style containment ensures that properties which can have effects on
more than just its descendants don&#39;t escape the element (e.g. counters). This
allows us to potentially skip style computation for the descendants if all we
want is to compute styles on other elements.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;paint&lt;/code&gt;: Paint containment ensures that the descendants of the containing box
don&#39;t display outside its bounds. Nothing can visibly overflow the element,
and if an element is off-screen or otherwise not visible, its descendants will
also not be visible. This allows us to potentially skip painting the
descendants if the element is offscreen.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;skipping-rendering-work-with-content-visibility&quot;&gt;Skipping rendering work with &lt;code&gt;content-visibility&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-visibility/#skipping-rendering-work-with-content-visibility&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It may be hard to figure out which containment values to use, since browser
optimizations may only kick in when an appropriate set is specified. You can
play around with the values to see &lt;a href=&quot;https://web.dev/css-containment/&quot;&gt;what works
best&lt;/a&gt;, or you
can use another CSS property called &lt;code&gt;content-visibility&lt;/code&gt; to apply the needed
containment automatically. &lt;code&gt;content-visibility&lt;/code&gt; ensures that you get the largest
performance gains the browser can provide with minimal effort from you as a
developer.&lt;/p&gt;
&lt;p&gt;The content-visibility property accepts several values, but &lt;code&gt;auto&lt;/code&gt; is the one
that provides immediate performance improvements. An element that has
&lt;code&gt;content-visibility: auto&lt;/code&gt; gains &lt;code&gt;layout&lt;/code&gt;, &lt;code&gt;style&lt;/code&gt; and &lt;code&gt;paint&lt;/code&gt; containment. If
the element is off-screen (and not otherwise relevant to the user—relevant
elements would be the ones that have focus or selection in their subtree), it
also gains &lt;code&gt;size&lt;/code&gt; containment (and it stops
&lt;a href=&quot;https://developer.chrome.com/blog/inside-browser-part3/#paint&quot; rel=&quot;noopener&quot;&gt;painting&lt;/a&gt;
and
&lt;a href=&quot;https://developer.chrome.com/blog/inside-browser-part4/#finding-the-event-target&quot; rel=&quot;noopener&quot;&gt;hit-testing&lt;/a&gt;
its contents).&lt;/p&gt;
&lt;p&gt;What does this mean? In short, if the element is off-screen its descendants are
not rendered. The browser determines the size of the element without considering
any of its contents, and it stops there. Most of the rendering, such as styling
and layout of the element&#39;s subtree are skipped.&lt;/p&gt;
&lt;p&gt;As the element approaches the viewport, the browser no longer adds the &lt;code&gt;size&lt;/code&gt;
containment and starts painting and hit-testing the element&#39;s content. This
enables the rendering work to be done just in time to be seen by the user.&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 browser is only able to skip rendering work if you are also careful not to call any DOM API that &lt;a href=&quot;https://gist.github.com/paulirish/5d52fb081b3570c81e3a#file-what-forces-layout-md&quot;&gt;forces some of rendering to occur&lt;/a&gt; on one of the skipped subtrees. If you&#39;re using &lt;code&gt;content-visibility&lt;/code&gt; to improve performance, audit your code to make sure these APIs are not getting called. To help find them, Chromium will print console messages if you call one of these APIs for a subtree of an element with &lt;code&gt;content-visibility:hidden&lt;/code&gt;. To see the messages, &lt;a href=&quot;https://developer.chrome.com/docs/devtools/console/log/&quot;&gt;turn on verbose logging&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;a-note-on-accessibility&quot;&gt;A note on accessibility &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-visibility/#a-note-on-accessibility&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One of the features of &lt;code&gt;content-visibility: auto&lt;/code&gt; is that the off-screen content remains available in the document object model and therefore, the accessibility tree (unlike with &lt;code&gt;visibility: hidden&lt;/code&gt;). This means, that content can be searched for on the page, and navigated to, without waiting for it to load or sacrificing rendering performance.&lt;/p&gt;
&lt;p&gt;The flip-side of this, however, is that &lt;a href=&quot;https://www.w3.org/TR/wai-aria-1.1/#landmark_roles&quot; rel=&quot;noopener&quot;&gt;landmark&lt;/a&gt; elements with style features such as &lt;code&gt;display: none&lt;/code&gt; or &lt;code&gt;visibility: hidden&lt;/code&gt; will also appear in the accessibility tree when off-screen, since the browser will not render these styles until they enter the viewport. To prevent these from being visible in the accessibility tree, potentially causing clutter, be sure to also add &lt;code&gt;aria-hidden=&amp;quot;true&amp;quot;&lt;/code&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; In Chromium 85-89, off-screen children within &lt;code&gt;content-visibility: auto&lt;/code&gt; were marked as invisible. In particular, &lt;a href=&quot;https://marcysutton.com/content-visibility-accessible-semantics&quot;&gt;headings&lt;/a&gt; and landmark roles were not exposed to accessibility tools. In Chromium 90 this was updated so that they are exposed. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;example&quot;&gt;Example: a travel blog &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-visibility/#example&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure&gt;
  &lt;video controls=&quot;&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; playsinline=&quot;&quot;&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/content-visibility/travel_blog.mp4&quot; /&gt;
  &lt;/video&gt;
  &lt;figcaption&gt;In this example, we baseline our travel blog on the right, and apply &lt;code&gt;content-visibility: auto&lt;/code&gt; to chunked areas on the left. The results show rendering times going from &lt;b&gt;232ms&lt;/b&gt; to &lt;b&gt;30ms&lt;/b&gt; on initial page load.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;A travel blog typically contains a set of stories with a few pictures, and some
descriptive text. Here is what happens in a typical browser when it navigates to
a travel blog:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A part of the page is downloaded from the network, along with any needed
resources.&lt;/li&gt;
&lt;li&gt;The browser styles and lays out all of the contents of the page, without
considering if the content is visible to the user.&lt;/li&gt;
&lt;li&gt;The browser goes back to step 1 until all of the page and resources are
downloaded.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In step 2, the browser processes all of the contents looking for things that may
have changed. It updates the style and layout of any new elements, along with
the elements that may have shifted as a result of new updates. This is rendering
work. This takes time.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of a travel blog.&quot; decoding=&quot;async&quot; height=&quot;563&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/57Zh2hjcXJjJIBSE648j.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/57Zh2hjcXJjJIBSE648j.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/57Zh2hjcXJjJIBSE648j.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/57Zh2hjcXJjJIBSE648j.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/57Zh2hjcXJjJIBSE648j.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/57Zh2hjcXJjJIBSE648j.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/57Zh2hjcXJjJIBSE648j.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/57Zh2hjcXJjJIBSE648j.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/57Zh2hjcXJjJIBSE648j.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/57Zh2hjcXJjJIBSE648j.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/57Zh2hjcXJjJIBSE648j.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/57Zh2hjcXJjJIBSE648j.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/57Zh2hjcXJjJIBSE648j.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/57Zh2hjcXJjJIBSE648j.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/57Zh2hjcXJjJIBSE648j.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/57Zh2hjcXJjJIBSE648j.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/57Zh2hjcXJjJIBSE648j.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/57Zh2hjcXJjJIBSE648j.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;An example of a travel blog. See &lt;a href=&quot;https://codepen.io/una/pen/rNxEWLo&quot;&gt;Demo on Codepen&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Now consider what happens if you put &lt;code&gt;content-visibility: auto&lt;/code&gt; on each of the
individual stories in the blog. The general loop is the same: the browser
downloads and renders chunks of the page. However, the difference is in the
amount of work that it does in step 2.&lt;/p&gt;
&lt;p&gt;With content-visibility, it will style and layout all of the contents that are
currently visible to the user (they are on-screen). However, when processing the
story that is fully off-screen, the browser will skip the rendering work and
only style and layout the element box itself.&lt;/p&gt;
&lt;p&gt;The performance of loading this page would be as if it contained full on-screen
stories and empty boxes for each of the off-screen stories. This performs much
better, with &lt;em&gt;expected reduction of 50% or more&lt;/em&gt; from the rendering cost of
loading. In our example, we see a boost from a &lt;strong&gt;232ms&lt;/strong&gt; rendering time to a
&lt;strong&gt;30ms&lt;/strong&gt; rendering time. That&#39;s a &lt;strong&gt;7x&lt;/strong&gt; performance boost.&lt;/p&gt;
&lt;p&gt;What is the work that you need to do in order to reap these benefits? First, we
chunk the content into sections:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;An annotated screenshot of chunking content into sections with a CSS class.&quot; decoding=&quot;async&quot; height=&quot;563&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/29uexe2kBwIsrAuILPnp.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/29uexe2kBwIsrAuILPnp.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/29uexe2kBwIsrAuILPnp.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/29uexe2kBwIsrAuILPnp.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/29uexe2kBwIsrAuILPnp.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/29uexe2kBwIsrAuILPnp.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/29uexe2kBwIsrAuILPnp.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/29uexe2kBwIsrAuILPnp.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/29uexe2kBwIsrAuILPnp.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/29uexe2kBwIsrAuILPnp.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/29uexe2kBwIsrAuILPnp.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/29uexe2kBwIsrAuILPnp.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/29uexe2kBwIsrAuILPnp.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/29uexe2kBwIsrAuILPnp.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/29uexe2kBwIsrAuILPnp.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/29uexe2kBwIsrAuILPnp.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/29uexe2kBwIsrAuILPnp.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/29uexe2kBwIsrAuILPnp.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Example of chunking content into sections with the &lt;code&gt;story&lt;/code&gt; class applied, to receive &lt;code&gt;content-visibility: auto&lt;/code&gt;. See &lt;a href=&quot;https://codepen.io/vmpstr/pen/xxZoyMb&quot;&gt;Demo on Codepen&lt;/a&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Then, we apply the following style rule to the sections:&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;.story&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;content-visibility&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;contain-intrinsic-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1000px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* Explained in the next section. */&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;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Note that as content moves in and out of visibility, it will start and stop being rendered as needed. However, this does not mean that the browser will have to render and re-render the same content over and over again, since the rendering work is saved when possible. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;specifying-the-natural-size-of-an-element-with-contain-intrinsic-size&quot;&gt;Specifying the natural size of an element with &lt;code&gt;contain-intrinsic-size&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-visibility/#specifying-the-natural-size-of-an-element-with-contain-intrinsic-size&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In order to realize the potential benefits of &lt;code&gt;content-visibility&lt;/code&gt;, the browser
needs to apply size containment to ensure that the rendering results of contents
do not affect the size of the element in any way. This means that the element
will lay out as if it was empty. If the element does not have a height specified
in a regular block layout, then it will be of 0 height.&lt;/p&gt;
&lt;p&gt;This might not be ideal, since the size of the scrollbar will shift, being
reliant on each story having a non-zero height.&lt;/p&gt;
&lt;p&gt;Thankfully, CSS provides another property, &lt;code&gt;contain-intrinsic-size&lt;/code&gt;, which
effectively specifies the natural size of the element &lt;em&gt;if the element is
affected by size containment&lt;/em&gt;. In our example, we are setting it to &lt;code&gt;1000px&lt;/code&gt; as
an estimate for the height and width of the sections.&lt;/p&gt;
&lt;p&gt;This means it will lay out as if it had a single child of &amp;quot;intrinsic-size&amp;quot;
dimensions, ensuring that your unsized divs still occupy space.
&lt;code&gt;contain-intrinsic-size&lt;/code&gt; acts as a placeholder size in lieu of rendered content.&lt;/p&gt;
&lt;p&gt;In Chromium 98 and onward, there is a new &lt;a href=&quot;https://drafts.csswg.org/css-sizing-4/#valdef-contain-intrinsic-width-auto-length&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;auto&lt;/code&gt;&lt;/a&gt;
keyword for &lt;code&gt;contain-intrinsic-size&lt;/code&gt;. When specified, the browser will remember
the last-rendered size, if any, and use that instead of the developer-provided placeholder
size. For example, if you specified &lt;code&gt;contain-intrinsic-size: auto 300px&lt;/code&gt;, the
element will start out with a &lt;code&gt;300px&lt;/code&gt; intrinsic sizing in each dimension, but once
the element&#39;s contents are rendered, it will retain the rendered intrinsic size.
Any subsequent rendering size changes will also be remembered. In practice, this means that if you
scroll an element with &lt;code&gt;content-visibility: auto&lt;/code&gt; applied, and then scroll it back
offscreen, it will automatically retain its ideal width and height, and not revert
to the placeholder sizing. This feature is especially useful for infinite scrollers,
which can now automatically improve sizing estimation over time as the user
explores the page.&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 can use &lt;code&gt;IntersectionObserver&lt;/code&gt; and &lt;code&gt;MutationObserver&lt;/code&gt; to set the correct sizes inline for each element. &lt;a href=&quot;https://twitter.com/slightlylate&quot;&gt;Alex Russell&lt;/a&gt; explains how this works in &lt;a href=&quot;https://infrequently.org/2020/12/content-visibility-scroll-fix/&quot;&gt;&lt;code&gt;content-visibility&lt;/code&gt; without jittery scrollbars&lt;/a&gt;, and &lt;a href=&quot;https://infrequently.org/2020/12/resize-resilient-deferred-rendering/&quot;&gt;Resize-Resilient &lt;code&gt;content-visibility&lt;/code&gt; Fixes&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;hiding-content-with-content-visibility-hidden&quot;&gt;Hiding content with &lt;code&gt;content-visibility: hidden&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-visibility/#hiding-content-with-content-visibility-hidden&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What if you want to keep the content unrendered regardless of whether or not it
is on-screen, while leveraging the benefits of cached rendering state? Enter:
&lt;code&gt;content-visibility: hidden&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;content-visibility: hidden&lt;/code&gt; property gives you all of the same benefits of
unrendered content and cached rendering state as &lt;code&gt;content-visibility: auto&lt;/code&gt; does
off-screen. However, unlike with &lt;code&gt;auto&lt;/code&gt;, it does not automatically start to
render on-screen.&lt;/p&gt;
&lt;p&gt;This gives you more control, allowing you to hide an element&#39;s contents and
later unhide them quickly.&lt;/p&gt;
&lt;p&gt;Compare it to other common ways of hiding element&#39;s contents:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;display: none&lt;/code&gt;: hides the element and destroys its rendering state. This
means unhiding the element is as expensive as rendering a new element with the
same contents.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;visibility: hidden&lt;/code&gt;: hides the element and keeps its rendering state. This
doesn&#39;t truly remove the element from the document, as it (and it&#39;s subtree)
still takes up geometric space on the page and can still be clicked on. It
also updates the rendering state any time it is needed even when hidden.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;content-visibility: hidden&lt;/code&gt;, on the other hand, hides the element while
preserving its rendering state, so, if there are any changes that need to
happen, they only happen when the element is shown again (i.e. the
&lt;code&gt;content-visibility: hidden&lt;/code&gt; property is removed).&lt;/p&gt;
&lt;p&gt;Some great use cases for &lt;code&gt;content-visibility: hidden&lt;/code&gt; are when implementing
advanced virtual scrollers, and measuring layout. They&#39;re also great for
single-page applications (SPA&#39;s). Inactive app views can be left in the DOM with
&lt;code&gt;content-visibility: hidden&lt;/code&gt; applied to prevent their display but maintain their
cached state. This makes the view quick to render when it becomes active again.&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 an experiment, Facebook engineers observed an up to 250ms improvement in navigation times when going back to previously cached views. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;effects-on-interaction-to-next-paint-inp&quot;&gt;Effects on Interaction to Next Paint (INP) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-visibility/#effects-on-interaction-to-next-paint-inp&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/inp/&quot;&gt;INP&lt;/a&gt; is a metric that evaluates a page&#39;s ability to be reliably responsive to user input. Responsiveness can be affected by any excessive amount of work that occurs on the main thread, including rendering work.&lt;/p&gt;
&lt;p&gt;Whenever you can reduce rendering work on any given page, you&#39;re giving the main thread an opportunity to respond to user inputs more quickly. This includes rendering work, and using the &lt;code&gt;content-visiblity&lt;/code&gt; CSS property where appropriate can reduce rendering work—especially during startup, when most rendering and layout work is done.&lt;/p&gt;
&lt;p&gt;Reducing rendering work has a direct effect on INP. When users attempt to interact with a page that uses the &lt;code&gt;content-visibility&lt;/code&gt; property properly to defer layout and rendering of offscreen elements, you&#39;re giving the main thread a chance to respond to critical user-visible work. This can improve your page&#39;s INP in some situations.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-visibility/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;content-visibility&lt;/code&gt; and the CSS Containment Spec mean some exciting performance
boosts are coming right to your CSS file. For more information on these
properties, check out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://drafts.csswg.org/css-contain/&quot; rel=&quot;noopener&quot;&gt;The CSS Containment Spec&lt;/a&gt;&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;MDN Docs on CSS
Containment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/w3c/csswg-drafts&quot; rel=&quot;noopener&quot;&gt;CSSWG Drafts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Una Kravets</name>
    </author><author>
      <name>Vladimir Levin</name>
    </author><author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Prefetch resources to speed up future navigations</title>
    <link href="https://web.dev/link-prefetch/"/>
    <updated>2019-09-12T00:00:00Z</updated>
    <id>https://web.dev/link-prefetch/</id>
    <content type="html" mode="escaped">&lt;p&gt;Research shows that &lt;a href=&quot;https://wpostats.com/&quot; rel=&quot;noopener&quot;&gt;faster load times result in higher conversion rates&lt;/a&gt; and better user experiences. If you have insight into how users move through your website and which pages they will likely visit next, you can improve load times of future navigations by downloading the resources for those pages ahead of time.&lt;/p&gt;
&lt;p&gt;This guide explains how to achieve that with &lt;code&gt;&amp;lt;link rel=prefetch&amp;gt;&lt;/code&gt;, a &lt;a href=&quot;https://www.w3.org/TR/resource-hints/&quot; rel=&quot;noopener&quot;&gt;resource hint&lt;/a&gt; that enables you to implement prefetching in an easy and efficient way.&lt;/p&gt;
&lt;h2 id=&quot;improve-navigations-with-rel=prefetch&quot;&gt;Improve navigations with &lt;code&gt;rel=prefetch&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/link-prefetch/#improve-navigations-with-rel=prefetch&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Adding &lt;code&gt;&amp;lt;link rel=prefetch&amp;gt;&lt;/code&gt; to a web page tells the browser to download entire pages, or some of the resources (like scripts or CSS files), that the user might need in the future:&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;prefetch&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;/articles/&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;document&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;img alt=&quot;A diagram showing how link prefetch works.&quot; decoding=&quot;async&quot; height=&quot;413&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;The &lt;code&gt;prefetch&lt;/code&gt; hint consumes extra bytes for resources that are not immediately needed, so this technique needs to be applied thoughtfully; only prefetch resources when you are confident that users will need them. Consider not prefetching when users are on slow connections. You can detect that with the &lt;a href=&quot;https://web.dev/adaptive-serving-based-on-network-quality/&quot;&gt;Network Information API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are different ways to determine which links to prefetch. The simplest one is to prefetch the first link or the first few links on the current page. There are also libraries that use more sophisticated approaches, explained later in this post.&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/link-prefetch/#use-cases&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;prefetching-subsequent-pages&quot;&gt;Prefetching subsequent pages &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/link-prefetch/#prefetching-subsequent-pages&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Prefetch HTML documents when subsequent pages are predictable, so that when a link is clicked, the page is loaded instantly.&lt;/p&gt;
&lt;p&gt;For example, in a product listing page, you can prefetch the page for the most popular product in the list. In some cases, the next navigation is even easier to anticipate—on a shopping cart page, the likelihood of a user visiting the checkout page is usually high which makes it a good candidate for prefetching.&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; eBay implemented prefetching for the first five results on a search page to speed up future pages loads and saw a positive impact on conversion rates. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;While prefetching resources does use additional bandwidth, it can improve most performance metrics. &lt;a href=&quot;https://web.dev/ttfb/&quot;&gt;Time to First Byte (TTFB)&lt;/a&gt; will often be much lower, as the document request results in a cache hit. Because TTFB will be lower, subsequent time-based metrics will often be lower as well, including &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/fcp/&quot;&gt;First Contentful Paint (FCP)&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;prefetching-static-assets&quot;&gt;Prefetching static assets &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/link-prefetch/#prefetching-static-assets&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Prefetch static assets, like scripts or stylesheets, when subsequent sections the user might visit can be predicted. This is especially useful when those assets are shared across many pages.&lt;/p&gt;
&lt;p&gt;For example, Netflix takes advantage of the time users spend on logged-out pages, to prefetch React, which will be used once users log in. Thanks to this, they &lt;a href=&quot;https://medium.com/dev-channel/a-netflix-web-performance-case-study-c0bcde26a9d9&quot; rel=&quot;noopener&quot;&gt;reduced Time to Interactive by 30% for future navigations&lt;/a&gt;.&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; At the time of this writing, it is possible to share prefetched resources among pages served from different origins. When &lt;a href=&quot;https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/6KKXv1PqPZ0/oguPntMGDgAJ&quot;&gt;Double-keyed HTTP cache&lt;/a&gt; ships, this will only work for top-level navigations and same-origin subresources, but it won&#39;t be possible to reuse prefetched subresources among different origins. This means that, if &lt;code&gt;a.com&lt;/code&gt; prefetches the resource &lt;code&gt;b.com/library.js&lt;/code&gt;, it won&#39;t be available in &lt;code&gt;c.com&lt;/code&gt; cache. Some browsers, such as WebKit-based ones, already &lt;a href=&quot;https://webkit.org/blog/7675/intelligent-tracking-prevention/&quot;&gt;partition caches and HTML5 storage&lt;/a&gt; for all third-party domains. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The effect of prefetching static assets on performance metrics depends on the resource being prefetched:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prefetching images can significantly lower LCP times for LCP image elements.&lt;/li&gt;
&lt;li&gt;Prefetching stylesheets can improve both FCP and LCP, as the network time to download the stylesheet will be eliminated. Since stylesheets are render blocking, they can reduce LCP when prefetched. In cases where the subsequent page&#39;s LCP element is a CSS background image requested via the &lt;code&gt;background-image&lt;/code&gt; property, the image will also be prefetched as a dependent resource of the prefetched stylesheet.&lt;/li&gt;
&lt;li&gt;Prefetching JavaScript will allow the processing of the prefetched script to occur much sooner than if it were required to be fetched by the network first during navigation. This can have an effect on responsiveness metrics such as &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;. In cases where markup is rendered on the client via JavaScript, LCP can be improved through reduced resource load delays, and client-side rendering of markup containing a page&#39;s LCP element can occur sooner.&lt;/li&gt;
&lt;li&gt;Prefetching web fonts that are not already used by the current page, can eliminate layout shifts. In cases where &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/font-display/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;font-display: swap;&lt;/code&gt; is used&lt;/a&gt;, the swap period for the font is eliminated, resulting in faster rendering of the text and eliminating layout shifts. If a future page uses the prefetched font and the page&#39;s LCP element is a block of text using a web font, LCP for that element will also be faster.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;prefetching-on-demand-javascript-chunks&quot;&gt;Prefetching on-demand JavaScript chunks &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/link-prefetch/#prefetching-on-demand-javascript-chunks&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/reduce-javascript-payloads-with-code-splitting&quot;&gt;Code-splitting&lt;/a&gt; your JavaScript bundles allows you to initially load only parts of an app and lazy-load the rest. If you&#39;re using this technique, you can apply prefetch to routes or components that are not immediately necessary but will likely be requested soon.&lt;/p&gt;
&lt;p&gt;For example, if you have a page that contains a button that opens a dialog box which contains an emoji picker, you can divide it into three JavaScript chunks—home, dialog and picker. Home and dialog could be initially loaded, while the picker could be loaded on-demand. Tools like webpack allow you to instruct the browser to prefetch these on-demand chunks.&lt;/p&gt;
&lt;h2 id=&quot;how-to-implement-rel=prefetch&quot;&gt;How to implement &lt;code&gt;rel=prefetch&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/link-prefetch/#how-to-implement-rel=prefetch&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The simplest way to implement &lt;code&gt;prefetch&lt;/code&gt; is adding a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag to the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of the document:&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;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;prefetch&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;/articles/&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;document&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;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;The &lt;code&gt;as&lt;/code&gt; attribute helps the browser set the right headers, and determine whether the resource is already in the cache. Example values for this attribute include: &lt;code&gt;document&lt;/code&gt;, &lt;code&gt;script&lt;/code&gt;, &lt;code&gt;style&lt;/code&gt;, &lt;code&gt;font&lt;/code&gt;, &lt;code&gt;image&lt;/code&gt;, and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/link#Attributes&quot; rel=&quot;noopener&quot;&gt;others&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can also initiate prefetching via the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Link&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Link&lt;/code&gt; HTTP header&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Link: &amp;lt;/css/style.css&amp;gt;; rel=prefetch&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;A benefit of specifying a prefetch hint in the HTTP Header is that the browser doesn&#39;t need to parse the document to find the resource hint, which can offer small improvements in some cases.&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;code&gt;prefetch&lt;/code&gt; is supported in &lt;a href=&quot;https://caniuse.com/#search=prefetch&quot;&gt;all modern browsers except Safari&lt;/a&gt;. You can implement a fallback technique for Safari with &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/XMLHttpRequest&quot;&gt;XHR&lt;/a&gt; requests or the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Fetch_API&quot;&gt;Fetch API&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;prefetching-javascript-modules-with-webpack-magic-comments&quot;&gt;Prefetching JavaScript modules with webpack magic comments &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/link-prefetch/#prefetching-javascript-modules-with-webpack-magic-comments&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;webpack enables you to prefetch scripts for routes or functionality you&#39;re reasonably certain users will visit or use soon.&lt;/p&gt;
&lt;p&gt;The following code snippet lazy-loads a sorting functionality from the &lt;a href=&quot;https://lodash.com/&quot; rel=&quot;noopener&quot;&gt;lodash&lt;/a&gt; library to sort a group of numbers that will be submitted by a form:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;form&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;&quot;submit&quot;&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 operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  e&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;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;lodash.sortby&#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;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;default&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 function&quot;&gt;sortInput&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;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&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 function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&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;&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;Instead of waiting for the &#39;submit&#39; event to take place to load this functionality, you can prefetch this resource to increase the chances of having it available in the cache by the time the user submits the form. webpack allows that using the &lt;a href=&quot;https://webpack.js.org/api/module-methods/#magic-comments&quot; rel=&quot;noopener&quot;&gt;magic comments&lt;/a&gt; inside &lt;code&gt;import()&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;highlight-line&quot;&gt;form&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;&quot;submit&quot;&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 operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;   e&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&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&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;&lt;span class=&quot;token comment&quot;&gt;/* webpackPrefetch: true */&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;lodash.sortby&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&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;module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;default&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&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 function&quot;&gt;sortInput&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&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;         &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&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 function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&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;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&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&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This tells webpack to inject the &lt;code&gt;&amp;lt;link rel=&amp;quot;prefetch&amp;quot;&amp;gt;&lt;/code&gt; tag into the HTML document:&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;prefetch&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;1.bundle.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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The performance benefits of prefetching on-demand chunks is a bit nuanced, but generally speaking, you could expect to see faster responses to interactions which depend on those on-demand chunks, as they will be immediately available. Depending on the nature of the interaction, this could impart a benefit to a page&#39;s INP.&lt;/p&gt;
&lt;p&gt;Prefetching in general also factors into overall &lt;a href=&quot;https://web.dev/prioritize-resources/&quot;&gt;resource prioritization&lt;/a&gt;. When a resource is prefetched, it is done so at the lowest possible priority. Thus, any prefetched resources will not contend for bandwidth for resources needed by the current page.&lt;/p&gt;
&lt;h3 id=&quot;smart-prefetching-with-quicklink-and-guessjs&quot;&gt;Smart prefetching with quicklink and Guess.js &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/link-prefetch/#smart-prefetching-with-quicklink-and-guessjs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You can also implement smarter prefetching with libraries that use &lt;code&gt;prefetch&lt;/code&gt; under the hood:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/GoogleChromeLabs/quicklink&quot; rel=&quot;noopener&quot;&gt;quicklink&lt;/a&gt; uses &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Intersection_Observer_API&quot; rel=&quot;noopener&quot;&gt;Intersection Observer API&lt;/a&gt; to detect when links come into the viewport and prefetches linked resources during &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Window/requestIdleCallback&quot; rel=&quot;noopener&quot;&gt;idle time&lt;/a&gt;. Bonus: quicklink weighs less than 1 KB!&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/guess-js&quot; rel=&quot;noopener&quot;&gt;Guess.js&lt;/a&gt; uses analytics reports to build a predictive model that is used to &lt;a href=&quot;https://web.dev/predictive-prefetching/&quot;&gt;smartly prefetch&lt;/a&gt; only what the user is likely to need.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both quicklink and Guess.js use 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; to avoid prefetching if a user is on a slow network or has &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; turned on.&lt;/p&gt;
&lt;h2 id=&quot;prefetching-under-the-hood&quot;&gt;Prefetching under the hood &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/link-prefetch/#prefetching-under-the-hood&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Resource hints are not mandatory instructions and it&#39;s up to the browser to decide if, and when, they get executed.&lt;/p&gt;
&lt;p&gt;You can use prefetch multiple times in the same page. The browser queues up all hints and requests each resource when it&#39;s &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Link_prefetching_FAQ#How_is_browser_idle_time_determined.3F&quot; rel=&quot;noopener&quot;&gt;idle&lt;/a&gt;. In Chrome, if a prefetch has not finished loading and the user navigates to the destined prefetch resource, the in-flight load is picked up as the navigation by the browser (other browser vendors might implement this differently).&lt;/p&gt;
&lt;p&gt;Prefetching takes place at the &lt;a href=&quot;https://docs.google.com/document/d/1bCDuq9H1ih9iNjgzyAL0gpwNFiEP4TZS-YLRp_RuMlc/edit&quot; rel=&quot;noopener&quot;&gt;&#39;Lowest&#39; priority&lt;/a&gt;, so prefetched resources do not compete for bandwidth with the resources required in the current page.&lt;/p&gt;
&lt;p&gt;Prefetched files are stored in the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Caching&quot; rel=&quot;noopener&quot;&gt;HTTP Cache&lt;/a&gt;, or the &lt;a href=&quot;https://calendar.perfplanet.com/2016/a-tale-of-four-caches/&quot; rel=&quot;noopener&quot;&gt;memory cache&lt;/a&gt; (depending on whether the resource is cacheable or not), for an amount of time that varies by browsers. For example, in Chrome resources are kept around for five minutes, after which the normal &lt;code&gt;Cache-Control&lt;/code&gt; rules for the resource apply.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/link-prefetch/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Using &lt;code&gt;prefetch&lt;/code&gt; can greatly improve load times of future navigations and even make pages appear to load instantly. &lt;code&gt;prefetch&lt;/code&gt; is widely supported in modern browsers, which makes it an attractive technique to improve the navigation experience for many users. This technique requires loading extra bytes that might not be used, so be mindful when you use it; only do it when necessary, and ideally, only on fast networks.&lt;/p&gt;
</content>
    <author>
      <name>Demian Renzulli</name>
    </author><author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Reduce web font size</title>
    <link href="https://web.dev/reduce-webfont-size/"/>
    <updated>2019-08-16T00:00:00Z</updated>
    <id>https://web.dev/reduce-webfont-size/</id>
    <content type="html" mode="escaped">&lt;p&gt;Typography is fundamental to good design, branding, readability, and accessibility. Web fonts enable all of the above and more: the text is selectable, searchable, zoomable, and high-DPI friendly, providing consistent and sharp text rendering regardless of the screen size and resolution. WebFonts are critical to good design, UX, and performance.&lt;/p&gt;
&lt;p&gt;Web font optimization is a critical piece of the overall performance strategy. Each font is an additional resource, and some fonts may block rendering of the text, but just because the page is using WebFonts doesn&#39;t mean that it has to render slower. On the contrary, optimized fonts, combined with a judicious strategy for how they are loaded and applied on the page, can help reduce the total page size and improve page rendering times.&lt;/p&gt;
&lt;h2 id=&quot;anatomy-of-a-web-font&quot;&gt;Anatomy of a web font &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-webfont-size/#anatomy-of-a-web-font&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A &lt;em&gt;web font&lt;/em&gt; is a collection of glyphs, and each glyph is a vector shape that describes a letter or symbol. As a result, two simple variables determine the size of a particular font file: the complexity of the vector paths of each glyph and the number of glyphs in a particular font. For example, Open Sans, which is one of the most popular WebFonts, contains 897 glyphs, which include Latin, Greek, and Cyrillic characters.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Font glyph table&quot; decoding=&quot;async&quot; height=&quot;309&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/B92rhiBJD9sx88a5CvVy.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/B92rhiBJD9sx88a5CvVy.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/B92rhiBJD9sx88a5CvVy.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/B92rhiBJD9sx88a5CvVy.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/B92rhiBJD9sx88a5CvVy.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/B92rhiBJD9sx88a5CvVy.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/B92rhiBJD9sx88a5CvVy.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/B92rhiBJD9sx88a5CvVy.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/B92rhiBJD9sx88a5CvVy.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/B92rhiBJD9sx88a5CvVy.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/B92rhiBJD9sx88a5CvVy.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/B92rhiBJD9sx88a5CvVy.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/B92rhiBJD9sx88a5CvVy.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/B92rhiBJD9sx88a5CvVy.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/B92rhiBJD9sx88a5CvVy.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/B92rhiBJD9sx88a5CvVy.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/B92rhiBJD9sx88a5CvVy.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/B92rhiBJD9sx88a5CvVy.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;When picking a font, it&#39;s important to consider which character sets are supported. If you need to localize your page content to multiple languages, you should use a font that can deliver a consistent look and experience to your users. For example, &lt;a href=&quot;https://www.google.com/get/noto/&quot; rel=&quot;noopener&quot;&gt;Google&#39;s Noto font family&lt;/a&gt; aims to support all the world&#39;s languages. Note, however, that the total size of Noto, with all languages included, results in a 1.1GB+ ZIP download.&lt;/p&gt;
&lt;p&gt;In this post you will find out how to reduce the delivered filesize of your web fonts.&lt;/p&gt;
&lt;h3 id=&quot;web-font-formats&quot;&gt;Web font formats &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-webfont-size/#web-font-formats&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Today there are two recommeded font container formats in use on the web:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Embedded_OpenType&quot; rel=&quot;noopener&quot;&gt;EOT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/TrueType&quot; rel=&quot;noopener&quot;&gt;TTF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Web_Open_Font_Format&quot; rel=&quot;noopener&quot;&gt;WOFF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/WOFF2/&quot; rel=&quot;noopener&quot;&gt;WOFF2&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;http://caniuse.com/#feat=woff&quot; rel=&quot;noopener&quot;&gt;WOFF&lt;/a&gt; and &lt;a href=&quot;http://caniuse.com/#feat=woff2&quot; rel=&quot;noopener&quot;&gt;WOFF 2.0&lt;/a&gt; enjoy wide support and are supported by all modern browsers.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Serve WOFF 2.0 variant to modern browsers.&lt;/li&gt;
&lt;li&gt;If absolutely necessary—such as if you still need to support Internet Explorer 11, for example—serve the WOFF as a fallback.&lt;/li&gt;
&lt;li&gt;Alternatively, consider not using web fonts for legacy browsers and falling back to the system fonts. This may be more performant for older, more constrained, devices as well.&lt;/li&gt;
&lt;li&gt;Since WOFF and WOFF 2.0 cover all bases for modern and legacy browsers still in use, use of EOT and TTF are no longer necessary and can result in longer web font download times.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;web-fonts-and-compression&quot;&gt;Web fonts and compression &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-webfont-size/#web-fonts-and-compression&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Both WOFF and WOFF 2.0 have built-in compression. WOFF 2.0&#39;s internal compression uses Brotli, and offers up to 30% better compression than WOFF. For more information, see &lt;a href=&quot;http://www.w3.org/TR/WOFF20ER/&quot; rel=&quot;noopener&quot;&gt;the WOFF 2.0 evaluation report&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Finally, it&#39;s worth noting that some font formats contain additional metadata, such as &lt;a href=&quot;https://en.wikipedia.org/wiki/Font_hinting&quot; rel=&quot;noopener&quot;&gt;font hinting&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Kerning&quot; rel=&quot;noopener&quot;&gt;kerning&lt;/a&gt; information that may not be necessary on some platforms, which allows for further file-size optimization. For example, &lt;a href=&quot;https://fonts.google.com/&quot; rel=&quot;noopener&quot;&gt;Google Fonts&lt;/a&gt; maintains 30+ optimized variants for each font and automatically detects and delivers the optimal variant for each platform and browser.&lt;/p&gt;
&lt;h2 id=&quot;define-a-font-family-with-font-face&quot;&gt;Define a font family with &lt;code&gt;@font-face&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-webfont-size/#define-a-font-family-with-font-face&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;@font-face&lt;/code&gt; CSS at-rule allows you to define the location of a particular font resource, its style characteristics, and the Unicode codepoints for which it should be used. A combination of such &lt;code&gt;@font-face&lt;/code&gt; declarations can be used to construct a &amp;quot;font family,&amp;quot; which the browser will use to evaluate which font resources need to be downloaded and applied to the current page.&lt;/p&gt;
&lt;h3 id=&quot;consider-a-variable-font&quot;&gt;Consider a variable font &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-webfont-size/#consider-a-variable-font&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Variable fonts can significantly reduce the filesize of your fonts in cases where you need multiple variants of a font. Instead of needing to load the regular and bold styles plus their italic versions, you can load a single file that contains all of the information. However, variable font file sizes will be larger than an individual font variant—though smaller than the combination of many variants. Rather than one large variable font, it may be better to serve critical font variants first, with other variants downloaded later.&lt;/p&gt;
&lt;p&gt;Variable fonts are now supported by all modern browsers, find out more in the &lt;a href=&quot;https://web.dev/variable-fonts/&quot;&gt;Introduction to variable fonts on the web&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;select-the-right-format&quot;&gt;Select the right format &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-webfont-size/#select-the-right-format&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Each &lt;code&gt;@font-face&lt;/code&gt; declaration provides the name of the font family, which acts as a logical group of multiple declarations, &lt;a href=&quot;http://www.w3.org/TR/css3-fonts/#font-prop-desc&quot; rel=&quot;noopener&quot;&gt;font properties&lt;/a&gt; such as style, weight, and stretch, and the &lt;a href=&quot;http://www.w3.org/TR/css3-fonts/#src-desc&quot; rel=&quot;noopener&quot;&gt;src descriptor&lt;/a&gt;, which specifies a prioritized list of locations for the font resource.&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;&#39;Awesome Font&#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;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Awesome Font&#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 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;&#39;/fonts/awesome.woff2&#39;&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;&#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 comment&quot;&gt;/* Only serve WOFF if necessary. Otherwise,&lt;br /&gt;          WOFF 2.0 is fine by itself. */&lt;/span&gt;&lt;br /&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;&#39;/fonts/awesome.woff&#39;&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;&#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 punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&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;Awesome Font&#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; italic&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;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Awesome Font Italic&#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 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;&#39;/fonts/awesome-i.woff2&#39;&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;&#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 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;&#39;/fonts/awesome-i.woff&#39;&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;&#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 punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;First, note that the above examples define a single &lt;em&gt;Awesome Font&lt;/em&gt; family with two styles (normal and &lt;em&gt;italic&lt;/em&gt;), each of which points to a different set of font resources. In turn, each &lt;code&gt;src&lt;/code&gt; descriptor contains a prioritized, comma-separated list of resource variants:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;local()&lt;/code&gt; directive allows you to reference, load, and use locally installed fonts. If the user already has the font installed on their system, this bypasses the network entirely, and is the fastest.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;url()&lt;/code&gt; directive allows you to load external fonts, and are allowed to contain an optional &lt;code&gt;format()&lt;/code&gt; hint indicating the format of the font referenced by the provided URL.&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; Unless you&#39;re referencing one of the default system fonts, it is rare for the user to have it locally installed, especially on mobile devices, where it is effectively impossible to &amp;quot;install&amp;quot; additional fonts. Even if you start with a &lt;code&gt;local()&lt;/code&gt; entry, you should always provide a list of &lt;code&gt;url()&lt;/code&gt; entries for those that do not have it downloaded. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;When the browser determines that the font is needed, it iterates through the provided resource list in the specified order and tries to load the appropriate resource. For example, following the example above:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The browser performs page layout and determines which font variants are required to render the specified text on the page. Fonts that aren&#39;t part of the page&#39;s &lt;a href=&quot;https://web.dev/critical-rendering-path-constructing-the-object-model/#css-object-model-cssom&quot;&gt;CSS Object Model (CSSOM)&lt;/a&gt; are not downloaded by the browser, as they are not required.&lt;/li&gt;
&lt;li&gt;For each required font, the browser checks if the font is available locally.&lt;/li&gt;
&lt;li&gt;If the font is not available locally, the browser iterates over external definitions:&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;If a format hint is present, the browser checks if it supports the hint before initiating the download. If the browser doesn&#39;t support the hint, the browser advances to the next one.&lt;/li&gt;
&lt;li&gt;If no format hint is present, the browser downloads the resource.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The combination of local and external directives with appropriate format hints allows you to specify all of the available font formats and let the browser handle the rest. The browser determines which resources are required and selects the optimal format.&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; If you&#39;re shipping more formats than just WOFF 2.0, the order in which the font variants are specified matters. The browser picks the first format it supports. Therefore, if you want the newer browsers to use WOFF 2.0, then you should place the WOFF 2.0 declaration above WOFF. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;unicode-range-subsetting&quot;&gt;Unicode-range subsetting &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-webfont-size/#unicode-range-subsetting&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In addition to font properties such as style, weight, and stretch, the &lt;code&gt;@font-face&lt;/code&gt; rule allows you to define a set of Unicode codepoints supported by each resource. This enables you to split a large Unicode font into smaller subsets (for example, Latin, Cyrillic, and Greek subsets) and only download the glyphs required to render the text on a particular page.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;http://www.w3.org/TR/css3-fonts/#descdef-unicode-range&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;unicode-range&lt;/code&gt; descriptor&lt;/a&gt; allows you to specify a comma-delimited list of range values, each of which can be in one of three different forms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Single codepoint (for example, &lt;code&gt;U+416&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Interval range (for example, &lt;code&gt;U+400-4ff&lt;/code&gt;): indicates the start and end codepoints of a range&lt;/li&gt;
&lt;li&gt;Wildcard range (for example, &lt;code&gt;U+4??&lt;/code&gt;): &lt;code&gt;?&lt;/code&gt; characters indicate any hexadecimal digit&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, you can split your &lt;em&gt;Awesome Font&lt;/em&gt; family into Latin and Japanese
subsets, each of which the browser downloads on an as-needed basis:&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;&#39;Awesome Font&#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;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Awesome Font&#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 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;&#39;/fonts/awesome-l.woff2&#39;&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;&#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 comment&quot;&gt;/* Latin glyphs */&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+000-5FF&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 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;Awesome Font&#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;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Awesome Font&#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 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;&#39;/fonts/awesome-jp.woff2&#39;&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;&#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 comment&quot;&gt;/* Japanese glyphs */&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+3000-9FFF&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+ff??&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;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; Unicode-range subsetting is particularly important for Asian languages, where the number of glyphs is much larger than in Western languages and a typical &amp;quot;full&amp;quot; font is often measured in megabytes instead of tens of kilobytes. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The use of Unicode range subsets and separate files for each stylistic variant of the font allows you to define a composite font family that is both faster and more efficient to download. Visitors only download the variants and subsets they need, and they aren&#39;t forced to download subsets that they may never see or use on the page.&lt;/p&gt;
&lt;p&gt;Nearly all browsers &lt;a href=&quot;https://caniuse.com/font-unicode-range&quot; rel=&quot;noopener&quot;&gt;support &lt;code&gt;unicode-range&lt;/code&gt;&lt;/a&gt;. For compatibility with older browsers you may need to fall back to &amp;quot;manual subsetting&amp;quot;. In this case you have to fall back to providing a single font resource that contains all the necessary subsets and hide the rest from the browser. For example, if the page is only using Latin characters, then you can strip other glyphs and serve that particular subset as a standalone resource.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Determine which subsets are needed:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;If the browser supports unicode-range subsetting, then it will automatically select the right subset. The page just needs to provide the subset files and specify appropriate unicode-ranges in the &lt;code&gt;@font-face&lt;/code&gt; rules.&lt;/li&gt;
&lt;li&gt;If the browser doesn&#39;t support unicode-range subsetting, then the page needs to hide all unnecessary subsets; that is, the developer must specify the required subsets.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generate font subsets:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Use the open-source &lt;a href=&quot;https://github.com/behdad/fonttools/&quot; rel=&quot;noopener&quot;&gt;pyftsubset tool&lt;/a&gt; to subset and optimize your fonts.&lt;/li&gt;
&lt;li&gt;Some font servers—such as Google Font—will automatically subset by default.&lt;/li&gt;
&lt;li&gt;Some font services allow manual subsetting via custom query parameters, which you can use to manually specify the required subset for your page. Consult the documentation from your font provider.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;font-selection-and-synthesis&quot;&gt;Font selection and synthesis &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-webfont-size/#font-selection-and-synthesis&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Each font family may be composed of multiple stylistic variants (regular, bold, italic) and multiple weights for each style. Each of which, in turn, may contain very different glyph shapes—for example, different spacing, sizing, or a different shape altogether.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Font weights&quot; decoding=&quot;async&quot; height=&quot;127&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 697px) 697px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/FNtAc2xRmx2MuUt2MADj.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/FNtAc2xRmx2MuUt2MADj.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/FNtAc2xRmx2MuUt2MADj.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/FNtAc2xRmx2MuUt2MADj.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/FNtAc2xRmx2MuUt2MADj.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/FNtAc2xRmx2MuUt2MADj.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/FNtAc2xRmx2MuUt2MADj.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/FNtAc2xRmx2MuUt2MADj.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/FNtAc2xRmx2MuUt2MADj.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/FNtAc2xRmx2MuUt2MADj.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/FNtAc2xRmx2MuUt2MADj.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/FNtAc2xRmx2MuUt2MADj.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/FNtAc2xRmx2MuUt2MADj.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/FNtAc2xRmx2MuUt2MADj.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/FNtAc2xRmx2MuUt2MADj.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/FNtAc2xRmx2MuUt2MADj.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/FNtAc2xRmx2MuUt2MADj.png?auto=format&amp;w=1394 1394w&quot; width=&quot;697&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;The above diagram illustrates a font family that offers three different bold weights:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;400 (regular).&lt;/li&gt;
&lt;li&gt;700 (bold).&lt;/li&gt;
&lt;li&gt;900 (extra bold).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All other in-between variants (indicated in gray) are automatically mapped to the closest variant by the browser.&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;
    When a weight is specified for which no face exists, a face with a nearby weight is used. In general, bold weights map to faces with heavier weights and light weights map to faces with lighter weights.
  &lt;/p&gt;
  &lt;cite&gt;
    &lt;a href=&quot;http://www.w3.org/TR/css3-fonts/#font-matching-algorithm&quot; rel=&quot;noopener&quot;&gt;CSS font matching algorithm&lt;/a&gt;
  &lt;/cite&gt;
&lt;/blockquote&gt;
&lt;p&gt;Similar logic applies to &lt;em&gt;italic&lt;/em&gt; variants. The font designer controls which variants they will produce, and you control which variants you&#39;ll use on the page. Because each variant is a separate download, it&#39;s a good idea to keep the number of variants small. For example, you can define two bold variants for the &lt;em&gt;Awesome Font&lt;/em&gt; 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 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;Awesome Font&#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;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Awesome Font&#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 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;&#39;/fonts/awesome-l.woff2&#39;&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;&#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 comment&quot;&gt;/* Latin glyphs */&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+000-5FF&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 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;Awesome Font&#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; 700&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 function&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Awesome Font&#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 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;&#39;/fonts/awesome-l-700.woff2&#39;&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;&#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 comment&quot;&gt;/* Latin glyphs */&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+000-5FF&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;The above example declares the &lt;em&gt;Awesome Font&lt;/em&gt; family that is composed of two resources that cover the same set of Latin glyphs (&lt;code&gt;U+000-5FF&lt;/code&gt;) but offer two different &amp;quot;weights&amp;quot;: normal (400) and bold (700). However, what happens if one of your CSS rules specifies a different font weight, or sets the &lt;code&gt;font-style&lt;/code&gt; property to &lt;code&gt;italic&lt;/code&gt;?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If an exact font match isn&#39;t available, the browser substitutes the closest match.&lt;/li&gt;
&lt;li&gt;If no stylistic match is found (for example, no italic variants were declared in the example above), then the browser synthesizes its own font variant.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Font synthesis&quot; decoding=&quot;async&quot; height=&quot;356&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/a8Jo2cIO1tPsj71AzftS.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/a8Jo2cIO1tPsj71AzftS.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/a8Jo2cIO1tPsj71AzftS.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/a8Jo2cIO1tPsj71AzftS.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/a8Jo2cIO1tPsj71AzftS.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/a8Jo2cIO1tPsj71AzftS.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/a8Jo2cIO1tPsj71AzftS.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/a8Jo2cIO1tPsj71AzftS.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/a8Jo2cIO1tPsj71AzftS.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/a8Jo2cIO1tPsj71AzftS.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/a8Jo2cIO1tPsj71AzftS.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/a8Jo2cIO1tPsj71AzftS.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/a8Jo2cIO1tPsj71AzftS.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/a8Jo2cIO1tPsj71AzftS.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/a8Jo2cIO1tPsj71AzftS.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/a8Jo2cIO1tPsj71AzftS.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/a8Jo2cIO1tPsj71AzftS.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/a8Jo2cIO1tPsj71AzftS.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&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; Be aware that synthesized approaches may not be suitable for scripts like Cyrillic, where italic forms are very different in shape. For proper fidelity in those scripts, use an actual italic font. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The example above illustrates the difference between the actual vs. synthesized font results for Open Sans. All synthesized variants are generated from a single 400-weight font. As you can see, there&#39;s a noticeable difference in the results. The details of how to generate the bold and oblique variants are not specified. Therefore, the results vary from browser to browser, and are highly dependent on the font.&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 best consistency and visual results, don&#39;t rely on font synthesis. Instead, minimize the number of used font variants and specify their locations, such that the browser can download them when they are used on the page. Or, choose to use a variable font. That said, in some cases a synthesized variant &lt;a href=&quot;https://www.igvita.com/2014/09/16/optimizing-webfont-selection-and-synthesis/&quot;&gt;may be a viable option&lt;/a&gt;, but be cautious in using synthesized variants. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;web-font-size-optimization-checklist&quot;&gt;Web font size optimization checklist &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-webfont-size/#web-font-size-optimization-checklist&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Audit and monitor your font use:&lt;/strong&gt; don&#39;t use too many fonts on your pages, and, for each font, minimize the number of used variants. This helps produce a more consistent and a faster experience for your users.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Avoid legacy formats if possible:&lt;/strong&gt; EOT, TTF, and WOFF formats are larger than WOFF 2.0. EOT and TTF are strictly unnecessary formats, where as WOFF may be acceptable if you need to support Internet Explorer 11. If you&#39;re only target modern browsers, using WOFF 2.0 only is the simplest and most performant option.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Subset your font resources:&lt;/strong&gt; many fonts can be subset, or split into multiple unicode-ranges to deliver just the glyphs that a particular page requires. This reduces the file size and improves the download speed of the resource. However, when defining the subsets, be careful to optimize for font re-use. For example, don&#39;t download a different but overlapping set of characters on each page. A
good practice is to subset based on script: for example, Latin, and Cyrillic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Give precedence to &lt;code&gt;local()&lt;/code&gt; in your &lt;code&gt;src&lt;/code&gt; list:&lt;/strong&gt; listing &lt;code&gt;local(&#39;Font Name&#39;)&lt;/code&gt; first in your &lt;code&gt;src&lt;/code&gt; list ensures that HTTP requests aren&#39;t made for fonts that are already installed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt;&lt;/strong&gt; to test for &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/uses-text-compression/&quot; rel=&quot;noopener&quot;&gt;text compression&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;effects-on-largest-contentful-paint-lcp-and-cumulative-layout-shift-cls&quot;&gt;Effects on Largest Contentful Paint (LCP) and Cumulative Layout Shift (CLS) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-webfont-size/#effects-on-largest-contentful-paint-lcp-and-cumulative-layout-shift-cls&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Depending on your page&#39;s content, text nodes can be considered candidates for &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt;. It&#39;s therefore vital to ensure your web fonts are as small as possible by following the advice in this article so that your users will see the text on your page &lt;a href=&quot;https://web.dev/optimize-webfont-loading/&quot;&gt;as soon as they possibly can&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you&#39;re concerned that, despite your optimization efforts, page text might take too long to appear because of large web font resource, the &lt;code&gt;font-display&lt;/code&gt; property has a number of settings that can help you to &lt;a href=&quot;https://web.dev/avoid-invisible-text&quot;&gt;avoid invisible text&lt;/a&gt; while a font is downloading. However, using the &lt;code&gt;swap&lt;/code&gt; value may cause significant layout shifts that affect your site&#39;s &lt;a href=&quot;https://web.dev/cls/&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt;. Consider using the &lt;code&gt;optional&lt;/code&gt; or &lt;code&gt;fallback&lt;/code&gt; values if possible.&lt;/p&gt;
&lt;p&gt;If your web fonts are crucial to your branding—and by extension, the user experience— consider preloading your fonts so that the browser has a head start on requesting them. This can reduce both the swap period if you use &lt;code&gt;font-display: swap&lt;/code&gt;, or the blocking period if you&#39;re not using &lt;code&gt;font-display&lt;/code&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Ilya Grigorik</name>
    </author><author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Use lazy loading to improve loading speed</title>
    <link href="https://web.dev/lazy-loading/"/>
    <updated>2019-08-16T00:00:00Z</updated>
    <id>https://web.dev/lazy-loading/</id>
    <content type="html" mode="escaped">&lt;p&gt;The portion of
&lt;a href=&quot;http://beta.httparchive.org/reports/state-of-images?start=earliest&amp;amp;end=latest&quot; rel=&quot;noopener&quot;&gt;images&lt;/a&gt;
and &lt;a href=&quot;http://beta.httparchive.org/reports/page-weight#bytesVideo&quot; rel=&quot;noopener&quot;&gt;video&lt;/a&gt; in the
typical payload of a website can be significant.
Unfortunately, project stakeholders may be unwilling to cut any media resources from their existing
applications.
Such impasses are frustrating,
especially when all parties involved want to improve site performance,
but can&#39;t agree on how to get there.
Fortunately, lazy loading is a solution that lowers initial page payload &lt;em&gt;and&lt;/em&gt;
load time, but doesn&#39;t skimp on content.&lt;/p&gt;
&lt;h2 id=&quot;what&quot;&gt;What is lazy loading? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lazy-loading/#what&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Lazy loading is a technique that defers loading of non-critical resources at page
load time. Instead, these non-critical resources are loaded at the moment of
need. Where images are concerned, &amp;quot;non-critical&amp;quot; is often synonymous with
&amp;quot;off-screen&amp;quot;. If you&#39;ve used Lighthouse and examined some opportunities for
improvement, you may have seen some guidance in this realm in the form of the
&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/offscreen-images/&quot; rel=&quot;noopener&quot;&gt;Defer offscreen images audit&lt;/a&gt;:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of the Defer offscreen images audit in Lighthouse.&quot; decoding=&quot;async&quot; height=&quot;102&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/63NnMISWUUWD3mvAliwe.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/63NnMISWUUWD3mvAliwe.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/63NnMISWUUWD3mvAliwe.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/63NnMISWUUWD3mvAliwe.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/63NnMISWUUWD3mvAliwe.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/63NnMISWUUWD3mvAliwe.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/63NnMISWUUWD3mvAliwe.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/63NnMISWUUWD3mvAliwe.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/63NnMISWUUWD3mvAliwe.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/63NnMISWUUWD3mvAliwe.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/63NnMISWUUWD3mvAliwe.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/63NnMISWUUWD3mvAliwe.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/63NnMISWUUWD3mvAliwe.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/63NnMISWUUWD3mvAliwe.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/63NnMISWUUWD3mvAliwe.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/63NnMISWUUWD3mvAliwe.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/63NnMISWUUWD3mvAliwe.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/63NnMISWUUWD3mvAliwe.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;One of Lighthouse&#39;s performance audits is to
identify off screen images, which are candidates for lazy loading.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You&#39;ve probably already seen lazy loading in action, and it goes something like
this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You arrive at a page, and begin to scroll as you read content.&lt;/li&gt;
&lt;li&gt;At some point, you scroll a placeholder image into the viewport.&lt;/li&gt;
&lt;li&gt;The placeholder image is suddenly replaced by the final image.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An example of image lazy loading can be found on the popular publishing platform
&lt;a href=&quot;https://medium.com/&quot; rel=&quot;noopener&quot;&gt;Medium&lt;/a&gt;, which loads lightweight placeholder images at
page load, and replaces them with lazily-loaded images as they&#39;re scrolled into
the viewport.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of the website Medium in the browsing, demonstrating lazy loading in action. The blurry placeholder is on the left, and the loaded resource is on the right.&quot; decoding=&quot;async&quot; height=&quot;493&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/p5ahQ67QtZ20bgto7Kpy.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/p5ahQ67QtZ20bgto7Kpy.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/p5ahQ67QtZ20bgto7Kpy.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/p5ahQ67QtZ20bgto7Kpy.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/p5ahQ67QtZ20bgto7Kpy.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/p5ahQ67QtZ20bgto7Kpy.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/p5ahQ67QtZ20bgto7Kpy.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/p5ahQ67QtZ20bgto7Kpy.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/p5ahQ67QtZ20bgto7Kpy.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/p5ahQ67QtZ20bgto7Kpy.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/p5ahQ67QtZ20bgto7Kpy.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/p5ahQ67QtZ20bgto7Kpy.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/p5ahQ67QtZ20bgto7Kpy.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/p5ahQ67QtZ20bgto7Kpy.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/p5ahQ67QtZ20bgto7Kpy.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/p5ahQ67QtZ20bgto7Kpy.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/p5ahQ67QtZ20bgto7Kpy.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/p5ahQ67QtZ20bgto7Kpy.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;An example of image lazy loading in action. A
placeholder image is loaded at page load (left), and when scrolled into the
viewport, the final image loads at the time of need.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If you&#39;re unfamiliar with lazy loading, you might be wondering just how useful
the technique is, and what its benefits are. Read on to find out!&lt;/p&gt;
&lt;h2 id=&quot;why&quot;&gt;Why lazy-load images or video instead of just &lt;em&gt;loading&lt;/em&gt; them? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lazy-loading/#why&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Because it&#39;s possible you&#39;re loading stuff the user may never see. This is
problematic for a couple reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It wastes data. On unmetered connections, this isn&#39;t the worst thing that could
happen (although you could be using that precious bandwidth for downloading
other resources that are indeed going to be seen by the user). On limited data
plans, however, loading stuff the user never sees could effectively be a waste
of their money.&lt;/li&gt;
&lt;li&gt;It wastes processing time, battery, and other system resources. After a media
resource is downloaded, the browser must decode it and render its content in the
viewport.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lazy loading images and video reduces initial page load time, initial
page weight, and system resource usage, all of which have positive impacts on
performance.&lt;/p&gt;
&lt;h2 id=&quot;implementing&quot;&gt;Implementing lazy loading &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lazy-loading/#implementing&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are a number of ways to implement lazy loading.
Your choice of solution must take into account the browsers you support,
and also what you are trying to lazy-load.&lt;/p&gt;
&lt;p&gt;Modern browsers implement &lt;a href=&quot;https://web.dev/browser-level-image-lazy-loading/&quot;&gt;browser-level lazy loading&lt;/a&gt;,
which can be enabled using the &lt;code&gt;loading&lt;/code&gt; attribute on images and iframes.
To provide compatibility with older browsers
or to perform lazy loading on elements without built-in lazy loading
you can implement a solution with your own JavaScript.
There are also a number of existing libraries to help you to do this.
See the posts on this site for full details of all of these approaches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/lazy-loading-images/&quot;&gt;Lazy loading images&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/lazy-loading-video/&quot;&gt;Lazy loading video&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also, we have compiled a list of &lt;a href=&quot;https://web.dev/lazy-loading-best-practices&quot;&gt;potential issues with lazy loading&lt;/a&gt;,
and things to watch out for in your implementation.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lazy-loading/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Used with care, lazy loading images and video can seriously lower the initial
load time and page payloads on your site, including &lt;a href=&quot;https://web.dev/vitals/&quot;&gt;Core Web Vitals&lt;/a&gt;. Users won&#39;t incur unnecessary network
activity—including network contention on slower connections—and processing costs of media resources they may never see, but they
can still view those resources if they want.&lt;/p&gt;
&lt;p&gt;As far as performance improvement techniques go, lazy loading is reasonably
uncontroversial. If you have a lot of inline imagery in your site, it&#39;s a
perfectly fine way to cut down on unnecessary downloads. Your site&#39;s users and
project stakeholders will appreciate it!&lt;/p&gt;
</content>
    <author>
      <name>Jeremy Wagner</name>
    </author><author>
      <name>Rachel Andrew</name>
    </author>
  </entry>
  
  <entry>
    <title>Lazy loading video</title>
    <link href="https://web.dev/lazy-loading-video/"/>
    <updated>2019-08-16T00:00:00Z</updated>
    <id>https://web.dev/lazy-loading-video/</id>
    <content type="html" mode="escaped">&lt;p&gt;As with &lt;a href=&quot;https://web.dev/lazy-loading-images&quot;&gt;image elements&lt;/a&gt;, you can also lazy-load video. Videos are commonly loaded with the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element (although &lt;a href=&quot;https://calendar.perfplanet.com/2017/animated-gif-without-the-gif/&quot; rel=&quot;noopener&quot;&gt;an
alternate method using
&lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;&lt;/a&gt; has
emerged with limited implementation). &lt;em&gt;How&lt;/em&gt; to lazy-load &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; depends on the
use case, though. Let&#39;s discuss a couple of scenarios that each require a
different solution.&lt;/p&gt;
&lt;h2 id=&quot;video-no-autoplay&quot;&gt;For video that doesn&#39;t autoplay &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lazy-loading-video/#video-no-autoplay&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For videos where playback is initiated by the user (that is, videos that &lt;em&gt;don&#39;t&lt;/em&gt;
autoplay), specifying the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/video#attr-preload&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;preload&lt;/code&gt;
attribute&lt;/a&gt;
on the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element may be desirable:&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;video&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;controls&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;preload&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;none&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;poster&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;one-does-not-simply-placeholder.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;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;one-does-not-simply.webm&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;video/webm&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;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;one-does-not-simply.mp4&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;video/mp4&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;video&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-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; A video &lt;code&gt;poster&lt;/code&gt; image can qualify as an &lt;a href=&quot;https://web.dev/lcp/#what-elements-are-considered&quot;&gt;LCP candidates&lt;/a&gt;. If your &lt;code&gt;poster&lt;/code&gt; image is an LCP candidate, you should &lt;a href=&quot;https://web.dev/preload-critical-assets/&quot;&gt;preload it&lt;/a&gt; with a &lt;a href=&quot;https://web.dev/fetch-priority/#the-fetchpriority-attribute&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; so the user sees it as soon as possible. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The example above uses a &lt;code&gt;preload&lt;/code&gt; attribute with a value of &lt;code&gt;none&lt;/code&gt; to prevent browsers
from preloading &lt;em&gt;any&lt;/em&gt; video data. The &lt;code&gt;poster&lt;/code&gt;
attribute gives the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element a placeholder that will occupy the space while the video loads. The reason for this is
that default behaviors for loading video can vary from browser to browser:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In Chrome, the default for &lt;code&gt;preload&lt;/code&gt; used to be &lt;code&gt;auto&lt;/code&gt;, but as of Chrome 64, it now
defaults to &lt;code&gt;metadata&lt;/code&gt;. Even so, on the desktop version of Chrome, a portion of
the video may be preloaded using the &lt;code&gt;Content-Range&lt;/code&gt; header. Other Chromium-based browsers and Firefox behave similarly.&lt;/li&gt;
&lt;li&gt;As with Chrome on desktop, 11.0 desktop versions of Safari will preload a range
of the video.
From version 11.2, only the video metadata is preloaded. &lt;a href=&quot;https://developer.apple.com/library/content/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/AudioandVideoTagBasics/AudioandVideoTagBasics.html#//apple_ref/doc/uid/TP40009523-CH2-SW9&quot; rel=&quot;noopener&quot;&gt;In Safari on iOS, videos are never
preloaded&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;When &lt;a href=&quot;https://support.google.com/chrome/answer/2392284&quot; rel=&quot;noopener&quot;&gt;Data Saver mode&lt;/a&gt; is
enabled, &lt;code&gt;preload&lt;/code&gt; defaults to &lt;code&gt;none&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because browser default behaviors with regard to &lt;code&gt;preload&lt;/code&gt; are not set in stone,
being explicit is probably your best bet. In this cases where the user initiates
playback, using &lt;code&gt;preload=&amp;quot;none&amp;quot;&lt;/code&gt; is the easiest way to defer loading of video on
all platforms. The &lt;code&gt;preload&lt;/code&gt; attribute isn&#39;t the only way to defer the loading
of video content. &lt;a href=&quot;https://web.dev/fast-playback-with-preload/&quot;&gt;&lt;em&gt;Fast Playback with Video
Preload&lt;/em&gt;&lt;/a&gt; may give you
some ideas and insight into working with video playback in JavaScript.&lt;/p&gt;
&lt;p&gt;Unfortunately, it doesn&#39;t prove useful when you want to use video in place of
animated GIFs, which is covered next.&lt;/p&gt;
&lt;h2 id=&quot;video-gif-replacement&quot;&gt;For video acting as an animated GIF replacement &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lazy-loading-video/#video-gif-replacement&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While animated GIFs enjoy wide use, they&#39;re subpar to video equivalents in a
number of ways, particularly in file size. Animated GIFs can stretch into
the range of several megabytes of data. Videos of similar visual quality tend to
be far smaller.&lt;/p&gt;
&lt;p&gt;Using the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element as a replacement for animated GIF is not as
straightforward as the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element. Animated GIFs have three characteristics:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;They play automatically when loaded.&lt;/li&gt;
&lt;li&gt;They loop continuously (&lt;a href=&quot;https://davidwalsh.name/prevent-gif-loop&quot; rel=&quot;noopener&quot;&gt;though that&#39;s not always the
case&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;They don&#39;t have an audio track.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Achieving this with the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element looks something like this:&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;video&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autoplay&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;muted&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;playsinline&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;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;one-does-not-simply.webm&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;video/webm&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;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;one-does-not-simply.mp4&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;video/mp4&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;video&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;autoplay&lt;/code&gt;, &lt;code&gt;muted&lt;/code&gt;, and &lt;code&gt;loop&lt;/code&gt; attributes are self-explanatory.
&lt;a href=&quot;https://webkit.org/blog/6784/new-video-policies-for-ios/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;playsinline&lt;/code&gt; is necessary for autoplaying to occur in
iOS&lt;/a&gt;. Now you have a
serviceable video-as-GIF replacement that works across platforms. But how to go
about lazy loading it? To start, modify your &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; markup accordingly:&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;video&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;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;autoplay&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;muted&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;playsinline&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;610&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;254&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;poster&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;one-does-not-simply.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;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;one-does-not-simply.webm&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;video/webm&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;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;one-does-not-simply.mp4&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;video/mp4&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;video&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&#39;ll notice the addition of the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/video#attr-poster&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;poster&lt;/code&gt;
attribute&lt;/a&gt;,
which lets you specify a placeholder to occupy the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element&#39;s space
until the video is lazy-loaded. As with the &lt;a href=&quot;https://web.dev/lazy-loading-images/&quot;&gt;&lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; lazy-loading examples&lt;/a&gt;,
stash the video URL in the &lt;code&gt;data-src&lt;/code&gt; attribute on each &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt;
element. From there, use JavaScript code similar to the
Intersection Observer-based image lazy loading examples:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;document&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;&quot;DOMContentLoaded&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&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;var&lt;/span&gt; lazyVideos &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 punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&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;&quot;video.lazy&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 punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&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;&quot;IntersectionObserver&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; window&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;var&lt;/span&gt; lazyVideoObserver &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;IntersectionObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entries&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; observer&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;      entries&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 keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;video&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;video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isIntersecting&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;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; source &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children&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;var&lt;/span&gt; videoSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;source&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 keyword&quot;&gt;typeof&lt;/span&gt; videoSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tagName &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;string&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; videoSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tagName &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;SOURCE&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;              videoSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; videoSource&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;br /&gt;          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;          video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load&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;          video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;lazy&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;          lazyVideoObserver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unobserve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&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;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;    lazyVideos&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 keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;lazyVideo&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;      lazyVideoObserver&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;lazyVideo&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 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;When you lazy-load a &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element, you need to iterate through all of the child
&lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; elements and flip their &lt;code&gt;data-src&lt;/code&gt; attributes to &lt;code&gt;src&lt;/code&gt; attributes. Once
you&#39;ve done that, you need to trigger loading of the video by calling the
element&#39;s &lt;code&gt;load&lt;/code&gt; method, after which the media will begin playing automatically
per the &lt;code&gt;autoplay&lt;/code&gt; attribute.&lt;/p&gt;
&lt;p&gt;Using this method, you have a video solution that emulates animated GIF behavior,
but doesn&#39;t incur the same intensive data usage as animated GIFs do,
and you can lazy-load that content.&lt;/p&gt;
&lt;h2 id=&quot;libraries&quot;&gt;Lazy loading libraries &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lazy-loading-video/#libraries&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The following libraries can help you to lazy-load video:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/verlok/vanilla-lazyload&quot; rel=&quot;noopener&quot;&gt;vanilla-lazyload&lt;/a&gt; and
&lt;a href=&quot;https://github.com/ApoorvSaxena/lozad.js&quot; rel=&quot;noopener&quot;&gt;lozad.js&lt;/a&gt; are super lightweight options
that use Intersection Observer only. As such, they are highly performant, but
will need to be polyfilled before you can use them on older browsers.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/malchata/yall.js&quot; rel=&quot;noopener&quot;&gt;yall.js&lt;/a&gt; is a library that uses
Intersection Observer and falls back to event handlers. It can also lazy load video &lt;code&gt;poster&lt;/code&gt; images using a &lt;code&gt;data-poster&lt;/code&gt; attribute.&lt;/li&gt;
&lt;li&gt;If you need a React-specific lazy loading library, you might consider
&lt;a href=&quot;https://github.com/jasonslyvia/react-lazyload&quot; rel=&quot;noopener&quot;&gt;react-lazyload&lt;/a&gt;. While it
doesn&#39;t use Intersection Observer, it &lt;em&gt;does&lt;/em&gt; provide a familiar method of lazy
loading images for those accustomed to developing applications with React.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these lazy loading libraries is well documented, with plenty of markup
patterns for your various lazy loading endeavors.&lt;/p&gt;
</content>
    <author>
      <name>Jeremy Wagner</name>
    </author><author>
      <name>Rachel Andrew</name>
    </author>
  </entry>
  
  <entry>
    <title>Lazy loading images</title>
    <link href="https://web.dev/lazy-loading-images/"/>
    <updated>2019-08-16T00:00:00Z</updated>
    <id>https://web.dev/lazy-loading-images/</id>
    <content type="html" mode="escaped">&lt;p&gt;Images can appear on a webpage due to being inline in the HTML as &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements
or as CSS background images. In this post you will find out how to lazy-load both types of image.&lt;/p&gt;
&lt;h2 id=&quot;images-inline&quot;&gt;Inline images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lazy-loading-images/#images-inline&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The most common lazy loading candidates are images as used in &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements.
With inline images we have three options for lazy loading,
which may be used in combination for the best compatibility across browsers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/lazy-loading-images/#images-inline-browser-level&quot;&gt;Using browser-level lazy loading&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/lazy-loading-images/#images-inline-intersection-observer&quot;&gt;Using Intersection Observer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;images-inline-browser-level&quot;&gt;Using browser-level lazy loading &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lazy-loading-images/#images-inline-browser-level&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Chrome and Firefox both support lazy loading with the &lt;code&gt;loading&lt;/code&gt; attribute.
This attribute can be added to &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements, and also to &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; elements.
A value of &lt;code&gt;lazy&lt;/code&gt; tells the browser to load the image immediately if it is in the viewport,
and to fetch other images when the user scrolls near them.&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; Note &lt;code&gt;&amp;lt;iframe loading=&amp;quot;lazy&amp;quot;&amp;gt;&lt;/code&gt; is currently non-standard. While implemented in Chromium, it does not yet have a specification and is subject to future change when this does happen. We suggest not to lazy-load iframes using the &lt;code&gt;loading&lt;/code&gt; attribute until it becomes part of the specification. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;See the &lt;code&gt;loading&lt;/code&gt; field of MDN&#39;s
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/img#Browser_compatibility&quot; rel=&quot;noopener&quot;&gt;browser compatibility&lt;/a&gt;
table for details of browser support.
If the browser does not support lazy loading then the attribute will be ignored
and images will load immediately, as normal.&lt;/p&gt;
&lt;p&gt;For most websites, adding this attribute to inline images will be a performance boost
and save users loading images that they may not ever scroll to.
If you have large numbers of images and want to be sure that users of browsers without support for lazy loading benefit
you will need to combine this with one of the methods explained next.&lt;/p&gt;
&lt;p&gt;To learn more, check out &lt;a href=&quot;https://web.dev/browser-level-image-lazy-loading/&quot;&gt;Browser-level lazy loading for the web&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;images-inline-intersection-observer&quot;&gt;Using Intersection Observer &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lazy-loading-images/#images-inline-intersection-observer&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To polyfill lazy loading of &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements, we use JavaScript to check if they&#39;re in the
viewport. If they are, their &lt;code&gt;src&lt;/code&gt; (and sometimes &lt;code&gt;srcset&lt;/code&gt;) attributes are
populated with URLs to the desired image content.&lt;/p&gt;
&lt;p&gt;If you&#39;ve written lazy loading code before, you may have accomplished your task
by using event handlers such as &lt;code&gt;scroll&lt;/code&gt; or &lt;code&gt;resize&lt;/code&gt;. While this approach is the
most compatible across browsers, modern browsers offer a more performant and
efficient way to do the work of checking element visibility via &lt;a href=&quot;https://developer.chrome.com/blog/intersectionobserver/&quot; rel=&quot;noopener&quot;&gt;the
Intersection Observer API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Intersection Observer is easier to use and read than code relying on various
event handlers, because you only need to register an observer to watch
elements rather than writing tedious element visibility detection code. All
that&#39;s left to do is to decide what to do when an element is visible.
Let&#39;s assume this basic markup pattern for your lazily loaded &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements:&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;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;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;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;placeholder-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;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;image-to-lazy-load-1x.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;data-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;image-to-lazy-load-2x.jpg 2x, image-to-lazy-load-1x.jpg 1x&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 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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;There are three relevant pieces of this markup that you should focus on:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;class&lt;/code&gt; attribute, which is what you&#39;ll select the element with in
JavaScript.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;src&lt;/code&gt; attribute, which references a placeholder image that will appear when
the page first loads.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;data-src&lt;/code&gt; and &lt;code&gt;data-srcset&lt;/code&gt; attributes, which are placeholder attributes
containing the URL for the image you&#39;ll load once the element is in the viewport.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now let&#39;s see how to use Intersection Observer in JavaScript to lazy-load
images using this markup pattern:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;document&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;&quot;DOMContentLoaded&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&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;var&lt;/span&gt; lazyImages &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 punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&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;&quot;img.lazy&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 punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&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;&quot;IntersectionObserver&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; window&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;let&lt;/span&gt; lazyImageObserver &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;IntersectionObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entries&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; observer&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;      entries&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 keyword&quot;&gt;function&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 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;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isIntersecting&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;let&lt;/span&gt; lazyImage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;          lazyImage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lazyImage&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;          lazyImage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;srcset &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lazyImage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataset&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;srcset&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;          lazyImage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;lazy&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;          lazyImageObserver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unobserve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lazyImage&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;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;    lazyImages&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 keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;lazyImage&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;      lazyImageObserver&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;lazyImage&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;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;// Possibly fall back to event handlers here&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;On the document&#39;s &lt;code&gt;DOMContentLoaded&lt;/code&gt; event, this script queries the DOM for all
&lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements with a class of &lt;code&gt;lazy&lt;/code&gt;. If Intersection Observer is available,
create a new observer that runs a callback when &lt;code&gt;img.lazy&lt;/code&gt; elements enter the
viewport.&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 420px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/lazy-intersection-observer?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=index.html&amp;previewSize=0&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;lazy-intersection-observer on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Intersection Observer is available in all modern browsers.
Therefore using it as a polyfill for &lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt; will ensure that lazy loading is available for most visitors.&lt;/p&gt;
&lt;h2 id=&quot;images-css&quot;&gt;Images in CSS &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lazy-loading-images/#images-css&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags are the most common way of using images on web pages, images
can also be invoked via the CSS
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/background-image&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;background-image&lt;/code&gt;&lt;/a&gt;
property (and other properties). Browser-level lazy loading does not apply to CSS background images,
so you need to consider other methods if you have background images to lazy-load.&lt;/p&gt;
&lt;p&gt;Unlike &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements which load regardless
of their visibility, image loading behavior in CSS is done with more
speculation. When &lt;a href=&quot;https://web.dev/critical-rendering-path-constructing-the-object-model/&quot;&gt;the document and CSS object
models&lt;/a&gt;
and &lt;a href=&quot;https://web.dev/critical-rendering-path-render-tree-construction/&quot;&gt;render
tree&lt;/a&gt;
are built, the browser examines how CSS is applied to a document before
requesting external resources. If the browser has determined a CSS rule
involving an external resource doesn&#39;t apply to the document as it&#39;s currently
constructed, the browser doesn&#39;t request it.&lt;/p&gt;
&lt;p&gt;This speculative behavior can be used to defer the loading of images in CSS by
using JavaScript to determine when an element is within the viewport, and
subsequently applying a class to that element that applies styling invoking a
background image. This causes the image to be downloaded at the time of need
instead of at initial load. For example, let&#39;s take an element that contains a
large hero background image:&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;div&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;lazy-background&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;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Here&#39;s a hero heading to get your attention!&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;h1&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;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Here&#39;s hero copy to convince you to buy a thing!&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;p&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;a&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;/buy-a-thing&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;Buy a thing!&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;a&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;div&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;div.lazy-background&lt;/code&gt; element would normally contain the hero background
image invoked by some CSS. In this lazy loading example, however, you can isolate
the &lt;code&gt;div.lazy-background&lt;/code&gt; element&#39;s &lt;code&gt;background-image&lt;/code&gt; property via a &lt;code&gt;visible&lt;/code&gt;
class added to the element when it&#39;s in the viewport:&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;.lazy-background&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;background-image&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;hero-placeholder.jpg&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* Placeholder image */&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;.lazy-background.visible&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;background-image&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;hero.jpg&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* The final image */&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;From here, use JavaScript to check if the element is in the viewport (with
Intersection Observer!), and add the &lt;code&gt;visible&lt;/code&gt; class to the
&lt;code&gt;div.lazy-background&lt;/code&gt; element at that time, which loads the image:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;document&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;&quot;DOMContentLoaded&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&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;var&lt;/span&gt; lazyBackgrounds &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 punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&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;&quot;.lazy-background&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 punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&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;&quot;IntersectionObserver&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; window&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;let&lt;/span&gt; lazyBackgroundObserver &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;IntersectionObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entries&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; observer&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;      entries&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 keyword&quot;&gt;function&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 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;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isIntersecting&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;          entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;visible&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;          lazyBackgroundObserver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unobserve&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;target&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;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;    lazyBackgrounds&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 keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;lazyBackground&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;      lazyBackgroundObserver&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;lazyBackground&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 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;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 420px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/lazy-background?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=index.html&amp;previewSize=0&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;lazy-background on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h3 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/lazy-loading-images/#effects-on-largest-contentful-paint-lcp&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Lazy loading is a great optimization that reduces both overall data usage and network contention during startup by deferring the loading of images to when they&#39;re actually needed. This can improve startup time, and reduce processing on the main thread by reducing time needed for image decodes.&lt;/p&gt;
&lt;p&gt;However, lazy loading is a technique that can affect your website&#39;s &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint LCP&lt;/a&gt; in a negative way if you&#39;re too eager with the technique. One thing you will want to avoid is lazy loading images that are in the viewport during startup.&lt;/p&gt;
&lt;p&gt;When using JavaScript-based lazy loaders, you will want to avoid lazy loading in-viewport images because these solutions often use a &lt;code&gt;data-src&lt;/code&gt; or &lt;code&gt;data-srcset&lt;/code&gt; attribute as a placeholder for &lt;code&gt;src&lt;/code&gt; and &lt;code&gt;srcset&lt;/code&gt; attributes. The problem here is that the loading of these images will be delayed because &lt;a href=&quot;https://web.dev/preload-scanner/#lazy-loading-with-javascript&quot;&gt;the browser preload scanner can&#39;t find them during startup&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Even using browser-level lazy loading to lazy load an in-viewport image can backfire. When &lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt; is applied to an in-viewport image, &lt;a href=&quot;https://web.dev/optimize-lcp/#optimize-the-priority-the-resource-is-given&quot;&gt;that image will be delayed until the browser knows for sure it&#39;s in the viewport&lt;/a&gt;, which can affect a page&#39;s LCP.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Never&lt;/em&gt; lazy load images that are visible in the viewport during startup. It&#39;s a pattern that will affect your site&#39;s LCP negatively, and therefore the user experience. If you need an image at startup, load it at startup as quickly as possible by not lazy loading it!&lt;/p&gt;
&lt;h2 id=&quot;libraries&quot;&gt;Lazy loading libraries &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lazy-loading-images/#libraries&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You should use browser-level lazy loading whenever possible, but if you find yourself in a situation where that isn&#39;t an option—such as a significant group of users still reliant on older browsers—the following libraries can be used to lazy-load images:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/aFarkas/lazysizes&quot; rel=&quot;noopener&quot;&gt;lazysizes&lt;/a&gt; is a full-featured lazy
loading library that lazy-loads images and iframes. The pattern it uses is quite
similar to the code examples shown here in that it automatically binds to a
&lt;code&gt;lazyload&lt;/code&gt; class on &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements, and requires you to specify image URLs in
&lt;code&gt;data-src&lt;/code&gt; and/or &lt;code&gt;data-srcset&lt;/code&gt; attributes, the contents of which are swapped
into &lt;code&gt;src&lt;/code&gt; and/or &lt;code&gt;srcset&lt;/code&gt; attributes, respectively. It uses Intersection
Observer (which you can polyfill), and can be extended with &lt;a href=&quot;https://github.com/aFarkas/lazysizes#available-plugins-in-this-repo&quot; rel=&quot;noopener&quot;&gt;a number of
plugins&lt;/a&gt; to
do things like lazy-load video. &lt;a href=&quot;https://web.dev/use-lazysizes-to-lazyload-images/&quot;&gt;Find out more about using lazysizes&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/verlok/vanilla-lazyload&quot; rel=&quot;noopener&quot;&gt;vanilla-lazyload&lt;/a&gt; is a
lightweight option for lazy loading images, background images, videos, iframes,
and scripts. It leverages Intersection Observer, supports responsive images, and
enables browser-level lazy loading.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ApoorvSaxena/lozad.js&quot; rel=&quot;noopener&quot;&gt;lozad.js&lt;/a&gt; is a another lightweight
option that uses Intersection Observer only. As such, it&#39;s highly performant,
but will need to be polyfilled before you can use it on older browsers.&lt;/li&gt;
&lt;li&gt;If you need a React-specific lazy loading library, consider
&lt;a href=&quot;https://github.com/jasonslyvia/react-lazyload&quot; rel=&quot;noopener&quot;&gt;react-lazyload&lt;/a&gt;. While it
doesn&#39;t use Intersection Observer, it &lt;em&gt;does&lt;/em&gt; provide a familiar method of lazy
loading images for those accustomed to developing applications with React.&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Jeremy Wagner</name>
    </author><author>
      <name>Rachel Andrew</name>
    </author>
  </entry>
  
  <entry>
    <title>Lazy loading best practices</title>
    <link href="https://web.dev/lazy-loading-best-practices/"/>
    <updated>2019-08-16T00:00:00Z</updated>
    <id>https://web.dev/lazy-loading-best-practices/</id>
    <content type="html" mode="escaped">&lt;p&gt;While lazy loading images and video have positive and measurable performance
benefits, it&#39;s not a task to be taken lightly. If you get it wrong, there could
be unintended consequences. As such, it&#39;s important to keep the following
concerns in mind.&lt;/p&gt;
&lt;h2 id=&quot;wrong-fold&quot;&gt;Mind the fold &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lazy-loading-best-practices/#wrong-fold&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It may be tempting to lazy-load every single media resource on the page with
JavaScript, but you need to resist this temptation. Anything resting above the
fold shouldn&#39;t be lazy-loaded. Such resources should be considered critical
assets, and thus should be loaded normally.&lt;/p&gt;
&lt;p&gt;Lazy loading delays the loading of resources until after the DOM is interactive
when scripts have finished loading and begin execution. For images below the
fold, this is fine, but critical resources above the fold should be loaded with
the standard &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element so they&#39;re displayed as soon as possible.&lt;/p&gt;
&lt;p&gt;Of course, where the fold lies is not so clear these days when websites are
viewed on so many screens of varying sizes. What lies above the fold on a laptop
may well lie &lt;em&gt;below&lt;/em&gt; it on mobile devices. There&#39;s no bulletproof advice for
addressing this optimally in every situation. You&#39;ll need to conduct an
inventory of your page&#39;s critical assets, and load those images in typical
fashion.&lt;/p&gt;
&lt;p&gt;Additionally, you may not want to be so strict about the fold line as the
threshold for triggering lazy loading. It may be more ideal for your purposes to
establish a buffer zone some distance below the fold so that images begin
loading well before the user scrolls them into the viewport. For example, the
Intersection Observer API allows you to specify a &lt;code&gt;rootMargin&lt;/code&gt; property in an
options object when you create a new &lt;code&gt;IntersectionObserver&lt;/code&gt; instance. This
effectively gives elements a buffer, which triggers lazy loading behavior before
the element is in the viewport:&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;let&lt;/span&gt; lazyImageObserver &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;IntersectionObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entries&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; observer&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;// lazy-loading image code goes here&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 literal-property property&quot;&gt;rootMargin&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0px 0px 256px 0px&quot;&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 the value for &lt;code&gt;rootMargin&lt;/code&gt; looks similar to values you&#39;d specify for a CSS
&lt;code&gt;margin&lt;/code&gt; property, that&#39;s because it is! In this case, the
bottom margin of the observed element (the browser viewport by default, but
this can be changed to a specific element using the &lt;code&gt;root&lt;/code&gt; property) is broadened by 256
pixels. That means the callback function will execute when an image element is
within 256 pixels of the viewport and the image will begin to load
before the user actually sees it.&lt;/p&gt;
&lt;p&gt;To achieve this same effect in browsers that don&#39;t support Intersection Observe, use scroll event handling code and adjust your
&lt;code&gt;getBoundingClientRect&lt;/code&gt; check to include a buffer.&lt;/p&gt;
&lt;h2 id=&quot;wrong-layout-shifting&quot;&gt;Layout shifting and placeholders &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lazy-loading-best-practices/#wrong-layout-shifting&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Lazy loading media can cause &lt;a href=&quot;https://web.dev/cls&quot;&gt;shifting in the layout&lt;/a&gt; if placeholders aren&#39;t used.
These changes can be disorienting for users and trigger expensive DOM layout
operations that consume system resources and contribute to jank. At a minimum,
consider using a solid color placeholder occupying the same dimensions as the
target image, or techniques such as
&lt;a href=&quot;http://www.guypo.com/introducing-lqip-low-quality-image-placeholders&quot; rel=&quot;noopener&quot;&gt;LQIP&lt;/a&gt; or
&lt;a href=&quot;https://github.com/technopagan/sqip&quot; rel=&quot;noopener&quot;&gt;SQIP&lt;/a&gt; that hint at the content of a media
item before it loads.&lt;/p&gt;
&lt;p&gt;For &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags, &lt;code&gt;src&lt;/code&gt; should initially point to a placeholder until that
attribute is updated with the final image URL. Use the &lt;code&gt;poster&lt;/code&gt; attribute in a
&lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element to point to a placeholder image. Additionally, use &lt;code&gt;width&lt;/code&gt; and
&lt;code&gt;height&lt;/code&gt; attributes on both &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tags. This ensures that
transitioning from placeholders to final images won&#39;t change the rendered size
of the element as media loads.&lt;/p&gt;
&lt;h2 id=&quot;wrong-decoding-delays&quot;&gt;Image decoding delays &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lazy-loading-best-practices/#wrong-decoding-delays&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Loading large images in JavaScript and dropping them into the DOM can tie up the
main thread, causing the user interface to be unresponsive for a short period of
time while decoding occurs. &lt;a href=&quot;https://medium.com/dailyjs/image-loading-with-image-decode-b03652e7d2d2&quot; rel=&quot;noopener&quot;&gt;Asynchronously decoding images using the &lt;code&gt;decode&lt;/code&gt;
method&lt;/a&gt;
prior to inserting them into the DOM can cut down on this sort of jank, but
beware: It&#39;s not available everywhere yet, and it adds complexity to lazy loading logic.
If you want to use it, you&#39;ll need to check for it. Below shows
how you might use &lt;code&gt;Image.decode()&lt;/code&gt; with a fallback:&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;var&lt;/span&gt; newImage &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;Image&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;newImage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;my-awesome-image.jpg&quot;&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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;decode&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; newImage&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;// Fancy decoding logic&lt;/span&gt;&lt;br /&gt;  newImage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decode&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;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&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;    imageContainer&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;newImage&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;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;// Regular image load&lt;/span&gt;&lt;br /&gt;  imageContainer&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;newImage&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;Check out &lt;a href=&quot;https://codepen.io/malchata/pen/WzeZGW&quot; rel=&quot;noopener&quot;&gt;this CodePen link&lt;/a&gt; to see
code similar to this example in action. If most of your images are fairly small,
this may not do much for you, but it can certainly help cut down on jank when
lazy loading large images and inserting them into the DOM.&lt;/p&gt;
&lt;h2 id=&quot;wrong-loading-failure&quot;&gt;When stuff doesn&#39;t load &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lazy-loading-best-practices/#wrong-loading-failure&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Sometimes media resources fail to load for one reason or another and errors
occur. When might this happen? It depends, but here&#39;s one hypothetical scenario
for you: You have an HTML caching policy for a short period of time (e.g., five
minutes), and the user visits the site &lt;em&gt;or&lt;/em&gt; a user has a left a stale tab open for
a long period of time (e.g., several hours) and comes back to read your content.
At some point in this process, a redeployment occurs. During this deployment, an
image resource&#39;s name changes due to hash-based versioning, or is removed
altogether. By the time the user lazy-loads the image, the resource is
unavailable, and thus fails.&lt;/p&gt;
&lt;p&gt;While these are relatively rare occurrences, it may behoove you to have a backup
plan if lazy loading fails. For images, such a solution may look something like
this:&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;var&lt;/span&gt; newImage &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;Image&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;newImage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;my-awesome-image.jpg&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;newImage&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 keyword&quot;&gt;function&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;// Decide what to do on error&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;newImage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onload&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&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;// Load the image&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;What you decide to do in the event of an error depends on your application. For
example, you could replace the image placeholder area with a button that allows
the user to attempt to load the image again, or simply display an error message
in the image placeholder area.&lt;/p&gt;
&lt;p&gt;Other scenarios could arise as well. Whatever you do, it&#39;s never a bad idea to
signal to the user when an error has occurred, and possibly give them an action
to take if something goes awry.&lt;/p&gt;
&lt;h2 id=&quot;wrong-no-js&quot;&gt;JavaScript availability &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lazy-loading-best-practices/#wrong-no-js&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It shouldn&#39;t be assumed that JavaScript is always available. If you&#39;re going to
lazy-load images, consider offering &lt;code&gt;&amp;lt;noscript&amp;gt;&lt;/code&gt; markup that will show images in
case JavaScript is unavailable. The simplest possible fallback example involves
using &lt;code&gt;&amp;lt;noscript&amp;gt;&lt;/code&gt; elements to serve images if JavaScript is turned off:&lt;/p&gt;
&lt;!-- An image that eventually gets lazy-loaded by JavaScript --&gt;
&lt;img class=&quot;lazy&quot; src=&quot;https://web.dev/lazy-loading-best-practices/placeholder-image.jpg&quot; data-src=&quot;image-to-lazy-load.jpg&quot; alt=&quot;I&#39;m an image!&quot; /&gt;
&lt;!-- An image that is shown if JavaScript is turned off --&gt;
&lt;noscript&gt;
  &lt;img src=&quot;https://web.dev/lazy-loading-best-practices/image-to-lazy-load.jpg&quot; alt=&quot;I&#39;m an image!&quot; /&gt;
&lt;/noscript&gt;
&lt;p&gt;If JavaScript is turned off, users will see &lt;em&gt;both&lt;/em&gt; the placeholder image and the
image contained with the &lt;code&gt;&amp;lt;noscript&amp;gt;&lt;/code&gt; elements. To get around this, place
a class of &lt;code&gt;no-js&lt;/code&gt; on the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; tag like so:&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;html&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;no-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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Then place one line of inline script in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; before any style sheets
are requested via &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags that removes the &lt;code&gt;no-js&lt;/code&gt; class from the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;
element if JavaScript is on:&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 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;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;documentElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;no-js&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&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;Finally, use some CSS to hide elements with a class of lazy when
JavaScript is unavailable:&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;.no-js .lazy&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;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&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;This doesn&#39;t prevent placeholder images from loading, but the outcome is more
desirable. People with JavaScript turned off get something more than placeholder
images, which is better than placeholders and no meaningful image content at
all.&lt;/p&gt;
</content>
    <author>
      <name>Jeremy Wagner</name>
    </author><author>
      <name>Rachel Andrew</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>Establish network connections early to improve perceived page speed</title>
    <link href="https://web.dev/preconnect-and-dns-prefetch/"/>
    <updated>2019-07-30T00:00:00Z</updated>
    <id>https://web.dev/preconnect-and-dns-prefetch/</id>
    <content type="html" mode="escaped">&lt;p&gt;Before the browser can request a resource from a server, it must establish a connection. Establishing a secure connection involves three steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Look up the domain name and resolve it to an IP address.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set up a connection to the server.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Encrypt the connection for security.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In each of these steps the browser sends a piece of data to a server, and the server sends back a response. That journey, from origin to destination and back, is called a &lt;a href=&quot;https://developer.mozilla.org/docs/Glossary/Round_Trip_Time_(RTT)&quot; rel=&quot;noopener&quot;&gt;round trip&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Depending on network conditions, a single round trip might take a significant amount of time. The connection setup process might involve up to three round trips—and more in unoptimized cases.&lt;/p&gt;
&lt;p&gt;Taking care of all that ahead of time makes applications feel much faster. This post explains how to achieve that with two resource hints: &lt;code&gt;&amp;lt;link rel=preconnect&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;link rel=dns-prefetch&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;establish-early-connections-with-rel=preconnect&quot;&gt;Establish early connections with &lt;code&gt;rel=preconnect&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preconnect-and-dns-prefetch/#establish-early-connections-with-rel=preconnect&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Modern browsers &lt;a href=&quot;https://www.igvita.com/posa/high-performance-networking-in-google-chrome/#tcp-pre-connect&quot; rel=&quot;noopener&quot;&gt;try their best to anticipate&lt;/a&gt; what connections a page will need, but they cannot reliably predict them all. The good news is that you can give them a (resource 😉) hint.&lt;/p&gt;
&lt;p&gt;Adding &lt;code&gt;rel=preconnect&lt;/code&gt; to a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; informs the browser that your page intends to establish a connection to another domain, and that you&#39;d like the process to start as soon as possible. Resources will load more quickly because the setup process has already been completed by the time the browser requests them.&lt;/p&gt;
&lt;p&gt;Resource hints get their name because they are not mandatory instructions. They provide the information about what you&#39;d like to happen, but it&#39;s ultimately up to the browser to decide whether to execute them. Setting up and keeping a connection open is a lot of work, so the browser might choose to ignore resource hints or execute them partially depending on the situation.&lt;/p&gt;
&lt;p&gt;Informing the browser of your intention is as simple as adding a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag to your page:&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;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://example.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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;img alt=&quot;A diagram showing how the download doesn&amp;#x27;t start for a while after the connection is established.&quot; decoding=&quot;async&quot; height=&quot;539&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/988BgvmiVEAp2YVKt2jq.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/988BgvmiVEAp2YVKt2jq.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/988BgvmiVEAp2YVKt2jq.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/988BgvmiVEAp2YVKt2jq.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/988BgvmiVEAp2YVKt2jq.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/988BgvmiVEAp2YVKt2jq.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/988BgvmiVEAp2YVKt2jq.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/988BgvmiVEAp2YVKt2jq.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/988BgvmiVEAp2YVKt2jq.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/988BgvmiVEAp2YVKt2jq.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/988BgvmiVEAp2YVKt2jq.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/988BgvmiVEAp2YVKt2jq.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/988BgvmiVEAp2YVKt2jq.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/988BgvmiVEAp2YVKt2jq.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/988BgvmiVEAp2YVKt2jq.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/988BgvmiVEAp2YVKt2jq.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/988BgvmiVEAp2YVKt2jq.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/988BgvmiVEAp2YVKt2jq.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;You can speed up the load time by 100–500 ms by establishing early connections to important third-party origins. These numbers might seem small, but they make a difference in how &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/&quot;&gt;users perceive web page performance&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; chrome.com &lt;a href=&quot;https://twitter.com/addyosmani/status/1090874825286000640&quot;&gt;improved Time To Interactive&lt;/a&gt; by almost 1 s by pre-connecting to important origins. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;use-cases-for-rel=preconnect&quot;&gt;Use-cases for &lt;code&gt;rel=preconnect&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preconnect-and-dns-prefetch/#use-cases-for-rel=preconnect&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;knowing-where-from,-but-not-what-youre-fetching&quot;&gt;Knowing &lt;em&gt;where from&lt;/em&gt;, but not &lt;em&gt;what&lt;/em&gt; you&#39;re fetching &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preconnect-and-dns-prefetch/#knowing-where-from,-but-not-what-youre-fetching&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Due to versioned dependencies, you sometimes end up in a situation where you know you&#39;ll be requesting a resource from a particular CDN, but not the exact path for it.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;A url of a script with the version name.&quot; decoding=&quot;async&quot; height=&quot;50&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 450px) 450px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/PsP4qymb1gIp8Ip2sD9W.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/PsP4qymb1gIp8Ip2sD9W.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/PsP4qymb1gIp8Ip2sD9W.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/PsP4qymb1gIp8Ip2sD9W.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/PsP4qymb1gIp8Ip2sD9W.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/PsP4qymb1gIp8Ip2sD9W.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/PsP4qymb1gIp8Ip2sD9W.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/PsP4qymb1gIp8Ip2sD9W.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/PsP4qymb1gIp8Ip2sD9W.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/PsP4qymb1gIp8Ip2sD9W.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/PsP4qymb1gIp8Ip2sD9W.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/PsP4qymb1gIp8Ip2sD9W.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/PsP4qymb1gIp8Ip2sD9W.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/PsP4qymb1gIp8Ip2sD9W.png?auto=format&amp;w=900 900w&quot; width=&quot;450&quot; /&gt;
&lt;figcaption&gt;An example of a versioned URL.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The other common case is loading images from an &lt;a href=&quot;https://web.dev/image-cdns&quot;&gt;image CDN&lt;/a&gt;, where the exact path for an image depends on media queries or runtime feature checks on the user&#39;s browser.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;An image CDN URL with the parameters size=300x400 and quality=auto.&quot; decoding=&quot;async&quot; height=&quot;52&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/Xx4ai7tzSq12DJsQXaL1.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/Xx4ai7tzSq12DJsQXaL1.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/Xx4ai7tzSq12DJsQXaL1.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/Xx4ai7tzSq12DJsQXaL1.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/Xx4ai7tzSq12DJsQXaL1.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/Xx4ai7tzSq12DJsQXaL1.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/Xx4ai7tzSq12DJsQXaL1.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/Xx4ai7tzSq12DJsQXaL1.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/Xx4ai7tzSq12DJsQXaL1.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/Xx4ai7tzSq12DJsQXaL1.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/Xx4ai7tzSq12DJsQXaL1.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/Xx4ai7tzSq12DJsQXaL1.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/Xx4ai7tzSq12DJsQXaL1.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/Xx4ai7tzSq12DJsQXaL1.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/Xx4ai7tzSq12DJsQXaL1.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/Xx4ai7tzSq12DJsQXaL1.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/Xx4ai7tzSq12DJsQXaL1.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/Xx4ai7tzSq12DJsQXaL1.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;figcaption&gt;An example of an image CDN URL.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In these situations, if the resource you&#39;ll be fetching is important, you want to save as much time as possible by pre-connecting to the server. The browser won&#39;t download the file until your page requests it, but at least it can handle the connection aspects ahead of time, saving the user from waiting for several round trips.&lt;/p&gt;
&lt;h3 id=&quot;streaming-media&quot;&gt;Streaming media &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preconnect-and-dns-prefetch/#streaming-media&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Another example where you may want to save some time in the connection phase, but not necessarily start retrieving content right away, is when streaming media from a different origin.&lt;/p&gt;
&lt;p&gt;Depending on how your page handles the streamed content, you may want to wait until your scripts have loaded and are ready to process the stream. Pre-connecting helps you cut the waiting time to a single round trip once you&#39;re ready to start fetching.&lt;/p&gt;
&lt;h2 id=&quot;how-to-implement-rel=preconnect&quot;&gt;How to implement &lt;code&gt;rel=preconnect&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preconnect-and-dns-prefetch/#how-to-implement-rel=preconnect&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One way of initiating a &lt;code&gt;preconnect&lt;/code&gt; is adding a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag to the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of the document.&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://example.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;Preconnecting is only effective for domains other than the origin domain, so you shouldn&#39;t use it for your site.&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; Only preconnect to critical domains you will use soon because the browser closes any connection that isn&#39;t used within 10 seconds. Unnecessary preconnecting can delay other important resources, so limit the number of preconnected domains and &lt;a href=&quot;https://andydavies.me/blog/2019/08/07/experimenting-with-link-rel-equals-preconnect-using-custom-script-injection-in-webpagetest/&quot;&gt;test the impact preconnecting makes&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;You can also initiate a preconnect via the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Link&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Link&lt;/code&gt; HTTP header&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Link: &amp;lt;https://example.com/&amp;gt;; rel=preconnect&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; A benefit of specifying a preconnect hint in the HTTP header is that it doesn&#39;t rely on markup being parsed, and it can be triggered by requests for stylesheets, scripts, and more. For example, Google Fonts sends a &lt;code&gt;Link&lt;/code&gt; header in the stylesheet response to preconnect to the domain that hosts the font files. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Some types of resources, such as fonts, are loaded in &lt;a href=&quot;https://www.w3.org/TR/css-fonts-3/#font-fetching-requirements&quot; rel=&quot;noopener&quot;&gt;anonymous mode&lt;/a&gt;. For those you must set the &lt;code&gt;crossorigin&lt;/code&gt; attribute with the &lt;code&gt;preconnect&lt;/code&gt; hint:&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;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://example.com/ComicSans&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If you omit the &lt;code&gt;crossorigin&lt;/code&gt; attribute, the browser only performs the DNS lookup.&lt;/p&gt;
&lt;h2 id=&quot;resolve-domain-name-early-with-rel=dns-prefetch&quot;&gt;Resolve domain name early with &lt;code&gt;rel=dns-prefetch&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preconnect-and-dns-prefetch/#resolve-domain-name-early-with-rel=dns-prefetch&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You remember sites by their names, but servers remember them by IP addresses. This is why the domain name system (DNS) exists. The browser uses DNS to convert the site name to an IP address. This process—&lt;a href=&quot;https://hacks.mozilla.org/2018/05/a-cartoon-intro-to-dns-over-https/&quot; rel=&quot;noopener&quot;&gt;domain name resolution&lt;/a&gt;— is the first step in establishing a connection.&lt;/p&gt;
&lt;p&gt;If a page needs to make connections to many third-party domains, preconnecting all of them is counterproductive. The &lt;code&gt;preconnect&lt;/code&gt; hint is best used for only the most critical connections. For all the rest, use &lt;code&gt;&amp;lt;link rel=dns-prefetch&amp;gt;&lt;/code&gt; to save time on the first step, the DNS lookup, which usually takes around &lt;a href=&quot;https://www.keycdn.com/support/reduce-dns-lookups&quot; rel=&quot;noopener&quot;&gt;20–120 ms&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;DNS resolution is initiated similarly to &lt;code&gt;preconnect&lt;/code&gt;: by adding a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag to the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of the document.&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;dns-prefetch&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;http://example.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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;a href=&quot;https://caniuse.com/#search=dns-prefetch&quot; rel=&quot;noopener&quot;&gt;Browser support for &lt;code&gt;dns-prefetch&lt;/code&gt;&lt;/a&gt; is slightly different from &lt;a href=&quot;https://caniuse.com/#search=preconnect&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;preconnect&lt;/code&gt;&lt;/a&gt; &lt;a href=&quot;https://caniuse.com/#search=preconnect&quot; rel=&quot;noopener&quot;&gt;support&lt;/a&gt;, so &lt;code&gt;dns-prefetch&lt;/code&gt; can serve as a fallback for browsers that don&#39;t support &lt;code&gt;preconnect&lt;/code&gt;.&lt;/p&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;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;http://example.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;dns-prefetch&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;http://example.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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;figcaption class=&quot;compare__caption&quot;&gt;
&lt;p&gt;To safely implement the fallback technique, use separate link tags.&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&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;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 dns-prefetch&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;http://example.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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;figcaption class=&quot;compare__caption&quot;&gt;
&lt;p&gt;Implementing &lt;code&gt;dns-prefetch&lt;/code&gt; fallback in the same &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag causes a bug in Safari where &lt;code&gt;preconnect&lt;/code&gt; gets cancelled.&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;effect-on-largest-contentful-paint-lcp&quot;&gt;Effect on Largest Contentful Paint (LCP) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preconnect-and-dns-prefetch/#effect-on-largest-contentful-paint-lcp&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Using &lt;code&gt;dns-prefetch&lt;/code&gt; and &lt;code&gt;preconnect&lt;/code&gt; allows sites to reduce the amount of time it takes to connect to another origin. The ultimate aim is that the time to load a resource from another origin should be minimized as much as possible.&lt;/p&gt;
&lt;p&gt;Where &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt; is concerned, it is better that resources are immediately discoverable, since &lt;a href=&quot;https://web.dev/lcp/#what-elements-are-considered&quot;&gt;LCP candidates&lt;/a&gt; are crucial parts of the user experience. A &lt;a href=&quot;https://web.dev/fetch-priority/#when-would-you-need-fetch-priority&quot;&gt;&lt;code&gt;fetchpriority&lt;/code&gt; value of &lt;code&gt;&amp;quot;high&amp;quot;&lt;/code&gt;&lt;/a&gt; on LCP resources can further improve this by signaling the importance of this asset to the browser so it can fetch it early.&lt;/p&gt;
&lt;p&gt;Where it is not possible to make LCP assets immediately discoverable, a &lt;a href=&quot;https://web.dev/preload-critical-assets/&quot;&gt;&lt;code&gt;preload&lt;/code&gt;&lt;/a&gt; link—also with the &lt;code&gt;fetchpriority&lt;/code&gt; value of &lt;code&gt;&amp;quot;high&amp;quot;&lt;/code&gt;—still allows the browser to load the resource as soon as possible.&lt;/p&gt;
&lt;p&gt;If neither of these options are available—because the exact resource will not be known until later in the page load—you can use &lt;code&gt;preconnect&lt;/code&gt; on cross-origin resources to reduce the impact of the late discovery of the resource as much as possible.&lt;/p&gt;
&lt;p&gt;Additionally, &lt;code&gt;preconnect&lt;/code&gt; is less expensive than &lt;code&gt;preload&lt;/code&gt; in terms of bandwidth usage, but still not without its risks. As is the case with excessive &lt;code&gt;preload&lt;/code&gt; hints, excessive &lt;code&gt;preconnect&lt;/code&gt; hints still consume bandwidth where TLS certificates are concerned. Be careful not to preconnect to too many origins, as this may cause bandwidth contention.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preconnect-and-dns-prefetch/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;These two resource hints are helpful for improving page speed when you know you&#39;ll download something from a third-party domain soon, but you don&#39;t know the exact URL for the resource. Examples include CDNs that distribute JavaScript libraries, images or fonts. Be mindful of constraints, use &lt;code&gt;preconnect&lt;/code&gt; only for the most important resources, rely on &lt;code&gt;dns-prefetch&lt;/code&gt; for the rest, and always measure the impact in the real-world.&lt;/p&gt;
</content>
    <author>
      <name>Milica Mihajlija</name>
    </author><author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Why does speed matter?</title>
    <link href="https://web.dev/why-speed-matters/"/>
    <updated>2019-05-01T00:00:00Z</updated>
    <id>https://web.dev/why-speed-matters/</id>
    <content type="html" mode="escaped">&lt;p&gt;Consumers increasingly rely on mobile to access digital content and services,
and if you look at your site analytics,
you&#39;ll probably see this story playing out in your own data.
Consumers are also more demanding than they&#39;ve ever been,
and when they weigh the experience on your site, they aren&#39;t just comparing you with your competitors,
they&#39;re rating you against the best-in-class services they use every day.&lt;/p&gt;
&lt;p&gt;This post rounds up some of the research that has been done on the relationship between performance and business success.&lt;/p&gt;
&lt;h2 id=&quot;performance-is-about-retaining-users&quot;&gt;Performance is about retaining users &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/why-speed-matters/#performance-is-about-retaining-users&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure&gt;
  &lt;blockquote&gt;
    &lt;p&gt;
  	Performance has directly impacted the company&#39;s bottom line.
    &lt;/p&gt;
    &lt;cite&gt;
      &lt;a href=&quot;https://www.youtube.com/watch?v=Xryhxi45Q5M&amp;feature=youtu.be&amp;t=1366&quot;&gt;Pinterest&lt;/a&gt;
    &lt;/cite&gt;
  &lt;/blockquote&gt;
&lt;/figure&gt;
&lt;p&gt;Performance plays a major role in the success of any online venture.
High-performing sites engage and retain users better than low-performing ones.&lt;/p&gt;
&lt;p&gt;Pinterest reduced perceived wait times by 40%
and this &lt;a href=&quot;https://medium.com/@Pinterest_Engineering/driving-user-growth-with-performance-improvements-cfc50dafadd7&quot; rel=&quot;noopener&quot;&gt;increased search engine traffic and sign-ups by 15%&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;COOK reduced average page load time by 850 milliseconds which
&lt;a href=&quot;https://www.nccgroup.trust/globalassets/resources/uk/case-studies/web-performance/cook-case-study.pdf&quot; rel=&quot;noopener&quot;&gt;increased conversions by 7%, decreased bounce rates by 7%, and increased pages per session by 10%&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Studies have also shown the negative impact poor performance can have on business goals.
For example, &lt;a href=&quot;https://www.creativebloq.com/features/how-the-bbc-builds-websites-that-scale&quot; rel=&quot;noopener&quot;&gt;the BBC&lt;/a&gt;
found they lost an additional 10% of users for every additional second their site took to load.&lt;/p&gt;
&lt;h2 id=&quot;performance-is-about-improving-conversions&quot;&gt;Performance is about improving conversions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/why-speed-matters/#performance-is-about-improving-conversions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Retaining users is crucial to improving conversions.
Slow sites have a negative impact on revenue, and fast sites are shown to increase conversion rates.&lt;/p&gt;
&lt;p&gt;For &lt;a href=&quot;http://resources.mobify.com/2016-Q2-mobile-insights-benchmark-report.html&quot; rel=&quot;noopener&quot;&gt;Mobify&lt;/a&gt;,
every 100ms decrease in homepage load speed worked out to a 1.11% increase in session-based conversion,
yielding an average annual revenue increase of nearly $380,000.
Additionally, a 100ms decrease in checkout page load speed amounted to a 1.55% increase in session-based conversion,
which in turn yielded an average annual revenue increase of nearly $530,000.&lt;/p&gt;
&lt;p&gt;When &lt;a href=&quot;https://www.digitalcommerce360.com/2010/08/19/web-accelerator-revs-conversion-and-sales-autoanything/&quot; rel=&quot;noopener&quot;&gt;AutoAnything reduced page load time by half&lt;/a&gt;,
they saw a boost of 12% to 13% in sales.&lt;/p&gt;
&lt;p&gt;Retailer &lt;a href=&quot;https://www.thinkwithgoogle.com/intl/en-gb/success-stories/uk-success-stories/furniture-village-and-greenlight-slash-page-load-times-boosting-user-experience/&quot; rel=&quot;noopener&quot;&gt;Furniture Village&lt;/a&gt; audited their site speed and developed a plan to address the problems they found,
leading to a 20% reduction in page load time and a 10% increase in conversion rate.&lt;/p&gt;
&lt;h2 id=&quot;performance-is-about-user-experience&quot;&gt;Performance is about user experience &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/why-speed-matters/#performance-is-about-user-experience&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When it comes to user experience, speed matters.
A &lt;a href=&quot;https://www.ericsson.com/en/press-releases/2016/2/streaming-delays-mentally-taxing-for-smartphone-users-ericsson-mobility-report&quot; rel=&quot;noopener&quot;&gt;consumer study&lt;/a&gt;
shows that the stress response to delays in mobile speed are similar to that of watching a horror movie or solving a mathematical problem,
and greater than waiting in a checkout line at a retail store.&lt;/p&gt;
&lt;p&gt;As a site begins to load, there&#39;s a period of time where users wait for content to appear.
Until this happens, there&#39;s no user experience to speak of.
This lack of an experience is fleeting on fast connections.
On slower connections, however, users are forced to wait.
Users may experience more problems as page resources slowly trickle in.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A comparison of two filmstrip reels of a page loading. The first shows a page loading on a slow connection, while the second shows the same page loading on a fast connection.&quot; decoding=&quot;async&quot; height=&quot;264&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/W0ctiX3cMOfWnNF6AQMg.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/W0ctiX3cMOfWnNF6AQMg.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/W0ctiX3cMOfWnNF6AQMg.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/W0ctiX3cMOfWnNF6AQMg.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/W0ctiX3cMOfWnNF6AQMg.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/W0ctiX3cMOfWnNF6AQMg.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/W0ctiX3cMOfWnNF6AQMg.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/W0ctiX3cMOfWnNF6AQMg.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/W0ctiX3cMOfWnNF6AQMg.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/W0ctiX3cMOfWnNF6AQMg.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/W0ctiX3cMOfWnNF6AQMg.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/W0ctiX3cMOfWnNF6AQMg.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/W0ctiX3cMOfWnNF6AQMg.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/W0ctiX3cMOfWnNF6AQMg.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/W0ctiX3cMOfWnNF6AQMg.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/W0ctiX3cMOfWnNF6AQMg.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/W0ctiX3cMOfWnNF6AQMg.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/W0ctiX3cMOfWnNF6AQMg.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;A comparison of page load on a very slow connection
(top) versus a faster connection (bottom).&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Performance is a foundational aspect of good user experiences.
When sites ship a lot of code, browsers must use megabytes of the user&#39;s data plan to download it.
Mobile devices have limited CPU power and memory.
They often get overwhelmed with what we might consider a small amount of unoptimized code.
This creates poor performance which leads to unresponsiveness.
Knowing what we know about human behavior, users will only tolerate low performing applications for so long before abandoning them.&lt;/p&gt;
&lt;h2 id=&quot;performance-is-about-people&quot;&gt;Performance is about people &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/why-speed-matters/#performance-is-about-people&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Poorly performing sites and applications can also pose real costs for the
people who use them.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://gs.statcounter.com/platform-market-share/desktop-mobile-tablet&quot; rel=&quot;noopener&quot;&gt;As mobile users continue to make up a larger portion of internet users
worldwide&lt;/a&gt;,
it&#39;s important to bear in mind that many of these users access the web through
mobile LTE, 4G, 3G, and even 2G networks.
As Ben Schwarz of Calibre points out in
&lt;a href=&quot;https://calibreapp.com/blog/beyond-the-bubble&quot; rel=&quot;noopener&quot;&gt;this study of real world performance&lt;/a&gt;,
the cost of prepaid data plans is decreasing,
which in turn is making access to the internet more affordable in places where it once wasn&#39;t.
Mobile devices and internet access are no longer luxuries.
They are common tools necessary to navigate and function in an increasingly interconnected world.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://beta.httparchive.org/reports/state-of-the-web#bytesTotal&quot; rel=&quot;noopener&quot;&gt;Total page size has been steadily increasing since at least 2011&lt;/a&gt;,
and the trend appears to be continuing.
As the typical page sends more data,
users must replenish their metered data plans more often, which costs them money.&lt;/p&gt;
&lt;p&gt;In addition to saving your users money,
fast and lightweight user experiences can also prove crucial for users in crisis.
Public resources such as hospitals, clinics,
and crisis centers have online resources that give users important and specific information that they need during a crisis.
&lt;a href=&quot;https://aneventapart.com/news/post/eric-meyer-designing-for-crisis&quot; rel=&quot;noopener&quot;&gt;While design is pivotal in presenting important information efficiently in stressful moments&lt;/a&gt;,
the importance of delivering this information fast can&#39;t be understated.
It&#39;s part of our job.&lt;/p&gt;
&lt;h2 id=&quot;get-started&quot;&gt;Get started with improving your website speed &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/why-speed-matters/#get-started&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Read up on the &lt;a href=&quot;https://web.dev/vitals/#core-web-vitals&quot;&gt;Core Web Vitals&lt;/a&gt; to learn about the metrics
that Google believes all websites should focus on.&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;
	  Today, we’re building on this work and providing an early
	  look at an upcoming Search ranking change that incorporates
	  these page experience metrics. We will introduce a new signal
	  that combines Core Web Vitals with our existing signals for page
	  experience to provide a holistic picture of the quality of a user’s
	  experience on a web page.
  &lt;/p&gt;
  &lt;cite&gt;
    &lt;a href=&quot;https://webmasters.googleblog.com/2020/05/evaluating-page-experience.html&quot;&gt;
      Evaluating page experience for a better web
    &lt;/a&gt;, Official Google Webmaster Central Blog
  &lt;/cite&gt;
&lt;/blockquote&gt;
&lt;p&gt;Then check out &lt;a href=&quot;https://web.dev/fast/&quot;&gt;Fast load times&lt;/a&gt; for lots of tips and tricks
related to getting fast and staying fast.&lt;/p&gt;
</content>
    <author>
      <name>Bojan Pavic</name>
    </author><author>
      <name>Chris Anstey</name>
    </author><author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Adapting to Users with Client Hints</title>
    <link href="https://web.dev/performance-optimizing-content-efficiency-client-hints/"/>
    <updated>2018-11-22T00:00:00Z</updated>
    <id>https://web.dev/performance-optimizing-content-efficiency-client-hints/</id>
    <content type="html" mode="escaped">&lt;p&gt;Developing sites that are fast everywhere can be a tricky prospect. The plethora
of device capabilities—and the quality of the networks they connect
to—can make it seem like an insurmountable task. While we can take
advantage of browser features to improve loading performance, how do we know
what the user’s device is capable of, or the quality of their network
connection? The solution is &lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-httpbis-client-hints-06&quot; rel=&quot;noopener&quot;&gt;client
hints&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Client hints are a set of opt-in HTTP request headers that give us insight into
these aspects of the user’s device and the network they’re connected to. By
tapping into this information server side, we can change &lt;em&gt;how&lt;/em&gt; we deliver
content based on device and/or network conditions. This can help us to create
more inclusive user experiences.&lt;/p&gt;
&lt;h2 id=&quot;its-all-about-content-negotiation&quot;&gt;It’s All About Content Negotiation &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints/#its-all-about-content-negotiation&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Client hints are another method of &lt;em&gt;content negotiation&lt;/em&gt;, which means changing
content responses based on browser request headers.&lt;/p&gt;
&lt;p&gt;One example of content negotiation involves the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Accept&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Accept&lt;/code&gt;&lt;/a&gt;
request header. It describes what &lt;em&gt;content&lt;/em&gt; types the browser understands, which
the server can use to &lt;em&gt;negotiate&lt;/em&gt; the response. For image requests, the content
of Chrome’s &lt;code&gt;Accept&lt;/code&gt; header is:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Accept&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;image/webp,image/apng,image/*,*/*;q=0.8&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;While all browsers support image formats like JPEG, PNG, and GIF, Accept tells
in this case that the browser &lt;em&gt;also&lt;/em&gt; supports &lt;a href=&quot;https://developers.google.com/speed/webp/&quot; rel=&quot;noopener&quot;&gt;WebP&lt;/a&gt; and
&lt;a href=&quot;https://en.wikipedia.org/wiki/APNG&quot; rel=&quot;noopener&quot;&gt;APNG&lt;/a&gt;. Using this information, we can
negotiate the best image types for each browser:&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;// Check Accept for an &quot;image/webp&quot; substring.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token variable&quot;&gt;$webp&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;stristr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$_SERVER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;HTTP_ACCEPT&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 string double-quoted-string&quot;&gt;&quot;image/webp&quot;&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 constant boolean&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;token operator&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 constant boolean&quot;&gt;false&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 image URL based on the browser&#39;s WebP support status.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token variable&quot;&gt;$imageFile&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$webp&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;whats-up.webp&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;whats-up.jpg&quot;&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;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;&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;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$imageFile&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 delimiter important&quot;&gt;?&gt;&lt;/span&gt;&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;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 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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Like &lt;code&gt;Accept&lt;/code&gt;, client hints are another avenue for negotiating content, but in
the context of device capabilities and network conditions. With client hints, we
can make server side performance decisions based on a user&#39;s individual
experience, such as deciding whether non-critical resources should be served to
users with poor network conditions. In this guide, we’ll describe all the
available hints and some ways you can use them to make content delivery more
accommodating to users.&lt;/p&gt;
&lt;h2 id=&quot;opting-in&quot;&gt;Opting in &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints/#opting-in&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Unlike the &lt;code&gt;Accept&lt;/code&gt; header, client hints don’t just magically appear (with the
exception of &lt;code&gt;Save-Data&lt;/code&gt;, which we’ll discuss later). In the interest of keeping
request headers at a minimum, you’ll need to opt into which client hints you’ll
want to receive by sending an &lt;code&gt;Accept-CH&lt;/code&gt; header when a user requests a
resource:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Accept-CH&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;Viewport-Width, Downlink&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The value for &lt;code&gt;Accept-CH&lt;/code&gt; is a comma-separated list of requested hints the site
will use in determining the results for subsequent resource request. When the
client reads this header, it’s being told “this site wants the &lt;code&gt;Viewport-Width&lt;/code&gt;
and &lt;code&gt;Downlink&lt;/code&gt; client hints.” Don’t worry about the specific hints themselves.
We’ll get to those in a moment.&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 order for client hints to work at all, your site must be served over HTTPS! &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;You can set these opt-in headers in any back-end language. For example, &lt;a href=&quot;http://php.net/manual/en/function.header.php&quot; rel=&quot;noopener&quot;&gt;PHP’s
&lt;code&gt;header&lt;/code&gt; function&lt;/a&gt; could be used.
You could even set these opt-in headers with &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/meta#attr-http-equiv&quot; rel=&quot;noopener&quot;&gt;the &lt;code&gt;http-equiv&lt;/code&gt;
attribute&lt;/a&gt;
on a &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; tag:&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;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;http-equiv&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;Accept-CH&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&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;Viewport-Width, Downlink&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; Earlier versions of the spec included the &lt;code&gt;Accept-CH-Lifetime&lt;/code&gt; header which has now been removed and sites should also stop sending it. If you were using this to enable hints to persist over browser sessions you should instead ensure you are sending the necessary &lt;code&gt;Accept-CH&lt;/code&gt; headers on the relevant responses. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;all-the-client-hints&quot;&gt;All the client hints! &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints/#all-the-client-hints&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Client hints describe one of two things: the device your users, well, &lt;em&gt;use&lt;/em&gt;, and
the network they’re using to access your site. Let’s briefly cover all of the
hints that are available.&lt;/p&gt;
&lt;h3 id=&quot;device-hints&quot;&gt;Device hints &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints/#device-hints&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Some client hints describe characteristics of the user’s device, usually screen
characteristics. Some of them can help you choose the optimal media resource for
a given user’s screen, but not all of them are necessarily media-centric.&lt;/p&gt;
&lt;p&gt;Before we get into this list, it might be helpful to learn a few key terms used
to describe screens and media resolution:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Intrinsic size:&lt;/strong&gt; the actual dimensions of a media resource. For example, if
you open an image in Photoshop, the dimensions shown in the image size dialogue
describe its &lt;em&gt;intrinsic size&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Density-corrected intrinsic size:&lt;/strong&gt; the dimensions of a media resource after
it has been corrected for pixel density. It’s the image’s &lt;em&gt;intrinsic size&lt;/em&gt;
divided by a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Window/devicePixelRatio&quot; rel=&quot;noopener&quot;&gt;device pixel
ratio&lt;/a&gt;.
For example, let’s take this markup:&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;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;whats-up-1x.png&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;whats-up-2x.png 2x, whats-up-1x.png 1x&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;I&lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;m that image you wanted.&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;Let’s say the intrinsic size of the &lt;code&gt;1x&lt;/code&gt; image in this case is 320x240, and the
&lt;code&gt;2x&lt;/code&gt; image’s intrinsic size is 640x480. If this markup is parsed by a client
installed on a device with a screen device pixel ratio of 2 (e.g., a Retina
screen), the &lt;code&gt;2x&lt;/code&gt; image is requested. The &lt;em&gt;density-corrected intrinsic size&lt;/em&gt; of
the &lt;code&gt;2x&lt;/code&gt; image is 320x240, since 640x480 divided by 2 is 320x240.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extrinsic size:&lt;/strong&gt; the size of a media resource after CSS and other layout
factors (such as &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes) have been applied to it. Let’s
say you have an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element that loads an image with a density-corrected
intrinsic size of 320x240, but it also has CSS &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; properties
with values of &lt;code&gt;256px&lt;/code&gt; and &lt;code&gt;192px&lt;/code&gt; applied to it, respectively. In this example,
the &lt;em&gt;extrinsic size&lt;/em&gt; of that &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element becomes 256x192.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;An illustration of intrinsic size versus extrinsic size. A box sized 320x240 pixels is shown with a label of INTRINSIC SIZE. Within it is a smaller box sized at 256x192 pixels, which represents an HTML img element with CSS applied to it. This box is labeled EXTRINSIC SIZE. To the right is a box containing CSS applied to the element which modifies the img element&amp;#x27;s layout so that its extrinsic size differs from its intrinsic size.&quot; decoding=&quot;async&quot; height=&quot;150&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/mYLOc0CZtVxXCNDoHcsK.svg&quot; width=&quot;265&quot; /&gt;
  &lt;figcaption&gt;&lt;em&gt;&lt;strong&gt;Figure 1&lt;/strong&gt;. An illustration of intrinsic versus
extrinsic size. An image gains its extrinsic size after layout factors have been
applied to it. In this case, applying CSS rules of &lt;code&gt;width: 256px;&lt;/code&gt;
and &lt;code&gt;height: 192px;&lt;/code&gt; transforms a 320x240 intrinsically sized image
to a 256x192 extrinsically sized one.&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;With some terminology under our belt, let’s get into the list of device-specific
client hints available to you.&lt;/p&gt;
&lt;h4 id=&quot;viewport-width&quot;&gt;Viewport-Width &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints/#viewport-width&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Viewport-Width&lt;/code&gt; is the width of the user’s viewport in CSS pixels:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Viewport-Width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;320&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This hint can used with other screen-specific hints to deliver different
treatments (i.e., crops) of an image which are optimal for specific screen sizes
(i.e., &lt;a href=&quot;https://www.smashingmagazine.com/2016/02/automatically-art-directed-responsive-images-go/&quot; rel=&quot;noopener&quot;&gt;art
direction&lt;/a&gt;),
or to omit resources that are unnecessary for the current screen width.&lt;/p&gt;
&lt;h4 id=&quot;dpr&quot;&gt;DPR &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints/#dpr&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;DPR&lt;/code&gt;, short for device pixel ratio, reports the ratio of physical pixels to CSS
pixels of the user’s screen:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;DPR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This hint is useful when selecting image sources which correspond to a screen&#39;s
pixel density (like &lt;code&gt;x&lt;/code&gt; descriptors do in the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/img#attr-srcset&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;srcset&lt;/code&gt;
attribute&lt;/a&gt;).&lt;/p&gt;
&lt;h4 id=&quot;width&quot;&gt;Width &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints/#width&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;Width&lt;/code&gt; hint appears on requests for image resources fired off by &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; or
&lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; tags using the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/img#attr-sizes&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;sizes&lt;/code&gt;
attribute&lt;/a&gt;.
&lt;code&gt;sizes&lt;/code&gt; tells the browser what the extrinsic size of the resource will be;
&lt;code&gt;Width&lt;/code&gt; uses that extrinsic size to request an image with an intrinsic size that
is optimal for the current layout.&lt;/p&gt;
&lt;p&gt;For example, let’s say a user requests a page with a 320 CSS pixel wide screen
with a DPR of 2. The device loads a document with an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element containing
a &lt;code&gt;sizes&lt;/code&gt; attribute value of &lt;code&gt;85vw&lt;/code&gt; (i.e., 85% of the viewport width for all
screen sizes). If the &lt;code&gt;Width&lt;/code&gt; hint has been opted-into, the client will send
this &lt;code&gt;Width&lt;/code&gt; hint to the server with the request for the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;’s &lt;code&gt;src&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;544&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In this case, the client is hinting to the server that an optimal intrinsic
width for the requested image would be 85% of the viewport width (272 pixels)
multiplied by the screen’s DPR (2), which equals 544 pixels.&lt;/p&gt;
&lt;p&gt;This hint is especially powerful because it not only takes into account the
density-corrected width of the screen, but also reconciles this critical piece
of information with the image’s extrinsic size within the layout. This gives
servers the opportunity to negotiate image responses that are optimal for both
the screen &lt;em&gt;and&lt;/em&gt; the layout.&lt;/p&gt;
&lt;h4 id=&quot;content-dpr&quot;&gt;Content-DPR &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints/#content-dpr&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;While you already know that &lt;em&gt;screens&lt;/em&gt; have a device pixel ratio, resources also
have their own pixel ratios. In the simplest resource selection use cases, pixel
ratios between devices and resources can be the same. But! In cases where both
the &lt;code&gt;DPR&lt;/code&gt; and &lt;code&gt;Width&lt;/code&gt; headers are in play, the extrinsic size of a resource can
produce scenarios where the two differ. This is where the &lt;code&gt;Content-DPR&lt;/code&gt; hint
comes into play.&lt;/p&gt;
&lt;p&gt;Unlike other client hints, &lt;code&gt;Content-DPR&lt;/code&gt; is not a &lt;em&gt;request&lt;/em&gt; header to be used by
servers, but rather a &lt;em&gt;response&lt;/em&gt; header servers &lt;em&gt;must&lt;/em&gt; send whenever &lt;code&gt;DPR&lt;/code&gt; and
&lt;code&gt;Width&lt;/code&gt; hints are used to select a resource. The value of &lt;code&gt;Content-DPR&lt;/code&gt; should
be the result of this equation:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Content-DPR&lt;/code&gt; = [Selected image resource size] / ([&lt;code&gt;Width&lt;/code&gt;] / [&lt;code&gt;DPR&lt;/code&gt;])&lt;/p&gt;
&lt;p&gt;When a &lt;code&gt;Content-DPR&lt;/code&gt; request header is sent, the browser will know how to scale
the given image for the screen’s device pixel ratio and the layout. Without it,
images may not scale properly.&lt;/p&gt;
&lt;h4 id=&quot;device-memory&quot;&gt;Device-Memory &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints/#device-memory&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Technically a part of the &lt;a href=&quot;https://www.w3.org/TR/device-memory-1/&quot; rel=&quot;noopener&quot;&gt;Device Memory
API&lt;/a&gt;, &lt;code&gt;Device-Memory&lt;/code&gt; reveals &lt;a href=&quot;https://www.w3.org/TR/device-memory-1/#sec-device-memory-client-hint-header&quot; rel=&quot;noopener&quot;&gt;the
approximate amount of
memory&lt;/a&gt;
the current device has in GiB:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Device-Memory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;2&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; Because this information &lt;a href=&quot;https://blog.mozilla.org/internetcitizen/2018/07/26/this-is-your-digital-fingerprint/&quot;&gt;could be used to fingerprint users&lt;/a&gt;, the value of &lt;code&gt;Device-Memory&lt;/code&gt; is intentionally coarse. Valid values are &lt;code&gt;0.25&lt;/code&gt;, &lt;code&gt;0.5&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt;, &lt;code&gt;4&lt;/code&gt;, and &lt;code&gt;8&lt;/code&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;A possible use case for this hint would be to reduce the amount of JavaScript
sent to browsers on devices with limited memory, &lt;a href=&quot;https://medium.com/@addyosmani/the-cost-of-javascript-in-2018-7d8950fbb5d4&quot; rel=&quot;noopener&quot;&gt;as JavaScript is the most
resource-intensive content type browsers typically
load&lt;/a&gt;.
Or you could send lower DPR images as they use less memory to decode.&lt;/p&gt;
&lt;h3 id=&quot;network-hints&quot;&gt;Network hints &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints/#network-hints&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://wicg.github.io/netinfo/&quot; rel=&quot;noopener&quot;&gt;Network Information API&lt;/a&gt; provides another
category of client hints that describe the performance of the user’s network
connection. In my opinion, these are the most useful set of hints. With them, we
have the ability to tailor experiences to users by changing how we deliver
resources to clients on slow connections.&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; Network hint values are predictions based on past latency and bandwidth readings. As such, they’re not 100% accurate, but are assumed to be good enough for client hint use cases. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;rtt&quot;&gt;RTT &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints/#rtt&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;RTT&lt;/code&gt; hint provides the approximate &lt;em&gt;Round Trip Time&lt;/em&gt;, in milliseconds, on
the application layer. The &lt;code&gt;RTT&lt;/code&gt; hint, unlike transport layer RTT, includes
server processing time.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;RTT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;125&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; The value of &lt;code&gt;RTT&lt;/code&gt; is rounded to the nearest 25 milliseconds to prevent fingerprinting. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;This hint is useful because of the role latency plays in loading performance.
Using the &lt;code&gt;RTT&lt;/code&gt; hint, we can make decisions based on network responsiveness,
which can help speed the delivery of an entire experience (e.g., through
omitting some requests).&lt;/p&gt;
&lt;h4 id=&quot;downlink&quot;&gt;Downlink &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints/#downlink&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;While latency is important in loading performance, bandwidth is influential,
too. The &lt;code&gt;Downlink&lt;/code&gt; hint, expressed in megabits per second (Mbps), reveals the
&lt;em&gt;approximate&lt;/em&gt; downstream speed of the user’s connection:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Downlink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;2.5&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; The value of &lt;code&gt;Downlink&lt;/code&gt; is rounded to the nearest multiple of 25 kilobits per second. Because again, fingerprinting. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;In conjunction with &lt;code&gt;RTT&lt;/code&gt;, &lt;code&gt;Downlink&lt;/code&gt; can be useful in changing how content is
delivered to users based on the quality of a network connection.&lt;/p&gt;
&lt;h4 id=&quot;ect&quot;&gt;ECT &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints/#ect&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;ECT&lt;/code&gt; hint stands for &lt;em&gt;Effective Connection Type&lt;/em&gt;. Its value is one of an
enumerated list of connection types, each of which describes &lt;a href=&quot;https://wicg.github.io/netinfo/#effective-connection-types&quot; rel=&quot;noopener&quot;&gt;a connection
within specified ranges of both &lt;code&gt;RTT&lt;/code&gt; and &lt;code&gt;Downlink&lt;/code&gt;
values&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This header doesn’t explain what the &lt;em&gt;actual&lt;/em&gt; connection type is—for
example, it doesn’t report whether your gateway is a cell tower or a wifi access
point. Rather, it analyzes the current connection’s latency and bandwidth and
determines what network profile it resembles most. For example, if you connect
through wifi to a slow network, &lt;code&gt;ECT&lt;/code&gt; may be populated with a value of &lt;code&gt;2g&lt;/code&gt;,
which is the closest approximation of the &lt;em&gt;effective&lt;/em&gt; connection:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;ECT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;2g&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Valid values for &lt;code&gt;ECT&lt;/code&gt; are &lt;code&gt;4g&lt;/code&gt;, &lt;code&gt;3g&lt;/code&gt;, &lt;code&gt;2g&lt;/code&gt;, and &lt;code&gt;slow-2g&lt;/code&gt;. This hint can be
used as a starting point for assessing connection quality, and subsequently
refined using the &lt;code&gt;RTT&lt;/code&gt; and &lt;code&gt;Downlink&lt;/code&gt; hints.&lt;/p&gt;
&lt;h4 id=&quot;save-data&quot;&gt;Save-Data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints/#save-data&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Save-Data&lt;/code&gt; isn’t so much a hint describing network conditions as it is a user
preference stating that pages should send less data.&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; Of all the client hints, &lt;code&gt;Save-Data&lt;/code&gt; is the only one you &lt;em&gt;can’t&lt;/em&gt; opt into with &lt;code&gt;Accept-CH&lt;/code&gt;. Only the user can control whether this hint is sent by toggling Chrome’s &lt;a href=&quot;https://support.google.com/chrome/answer/2392284&quot;&gt;Data Saver feature&lt;/a&gt; on Android devices. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;I prefer to classify &lt;code&gt;Save-Data&lt;/code&gt; as a network hint because many of the things
you would do with it are similar to other network hints. Users may also be
likely to enable it in high latency/low bandwidth environments. This hint, when
present, always looks like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Save-Data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;on&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Here at Google, &lt;a href=&quot;https://web.dev/web/fundamentals/performance/optimizing-content-efficiency/save-data/&quot;&gt;we’ve talked about what you can do with
&lt;code&gt;Save-Data&lt;/code&gt;&lt;/a&gt;.
The impact it can have on performance can be profound. It’s a signal where users
are literally asking you to send them less stuff! If you listen and act on that
signal, users will appreciate it.&lt;/p&gt;
&lt;h2 id=&quot;tying-it-all-together&quot;&gt;Tying it all together &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints/#tying-it-all-together&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What you &lt;em&gt;do&lt;/em&gt; with client hints depends on you. Because they offer so much
information, you have many options. To get some ideas flowing, let’s see what
client hints can do for &lt;a href=&quot;https://github.com/malchata/client-hints-example&quot; rel=&quot;noopener&quot;&gt;Sconnie
Timber&lt;/a&gt;, a fictional timber
company located in the rural Upper Midwest. &lt;a href=&quot;https://www.technologyreview.com/s/603083/the-unacceptable-persistence-of-the-digital-divide/&quot; rel=&quot;noopener&quot;&gt;As is often the case in remote
areas&lt;/a&gt;,
network connections can be fragile. This is where a technology like client hints
can really make a difference for users.&lt;/p&gt;
&lt;h3 id=&quot;responsive-images&quot;&gt;Responsive Images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints/#responsive-images&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;All but the simplest responsive image use cases can get complicated. What if you
have multiple treatments &lt;em&gt;and&lt;/em&gt; variants of the same images for different screen
sizes—&lt;em&gt;and&lt;/em&gt; different formats? That markup gets &lt;a href=&quot;https://dev.opera.com/articles/responsive-images/#changing-image-sizes-high-dpi-images-different-image-types--art-direction-use-case&quot; rel=&quot;noopener&quot;&gt;&lt;em&gt;very&lt;/em&gt; complicated &lt;em&gt;very&lt;/em&gt;
quickly&lt;/a&gt;.
It’s easy to get it wrong, and easy to forget or misunderstand important
concepts (such as &lt;code&gt;sizes&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;While &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; and &lt;code&gt;srcset&lt;/code&gt; are undeniably &lt;em&gt;awesome&lt;/em&gt; tools, they can be
time-consuming to develop and maintain for complex use cases. We can automate
markup generation, but doing so is also difficult because the functionality
&lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; and &lt;code&gt;srcset&lt;/code&gt; provides is complex enough that their automation needs
to be done in a way that maintains the flexibility they provide.&lt;/p&gt;
&lt;p&gt;Client hints are can simplify this. Negotiating image responses with client
hints could look something like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If applicable to your workflow, first select an image treatment (i.e.,
art-directed imagery) by checking the &lt;code&gt;Viewport-Width&lt;/code&gt; hint.&lt;/li&gt;
&lt;li&gt;Select an image resolution by checking the &lt;code&gt;Width&lt;/code&gt; hint and the &lt;code&gt;DPR&lt;/code&gt; hint, and
choosing a source that fits the image’s layout size and screen density (similar
to how &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;w&lt;/code&gt; descriptors work in &lt;code&gt;srcset&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Select the most optimal file format the browser supports (something &lt;code&gt;Accept&lt;/code&gt;
helps us do in most browsers).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Where my fictitious timber company client was concerned, I developed a naïve
responsive image selection routine in PHP that uses client hints. This meant
instead of sending this markup to all users:&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;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;&lt;br /&gt;      company-photo-256w.webp   256w,&lt;br /&gt;      company-photo-512w.webp   512w,&lt;br /&gt;      company-photo-768w.webp   768w,&lt;br /&gt;      company-photo-1024w.webp 1024w,&lt;br /&gt;      company-photo-1280w.webp 1280w&lt;br /&gt;    &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;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;br /&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;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;&lt;br /&gt;      company-photo-256w.jpg   256w,&lt;br /&gt;      company-photo-512w.jpg   512w,&lt;br /&gt;      company-photo-768w.jpg   768w,&lt;br /&gt;      company-photo-1024w.jpg 1024w,&lt;br /&gt;      company-photo-1280w.jpg 1280w&lt;br /&gt;    &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;company-photo-256w.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;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;(min-width: 560px) 251px, 88.43vw&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;The Sconnie Timber Staff!&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;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;I was able to reduce it to the following based on individual browser support:&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;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/sizes:true/company-photo.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;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;(min-width: 560px) 251px, 88.43vw&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;SAY CHEESY PICKLES.&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;In this example, the &lt;code&gt;/image&lt;/code&gt; URL is a PHP script followed by parameters
rewritten by
&lt;a href=&quot;https://httpd.apache.org/docs/current/mod/mod_rewrite.html&quot; rel=&quot;noopener&quot;&gt;mod_rewrite&lt;/a&gt;. It
takes an image filename and additional parameters to help a back-end script
choose the best image in the given conditions.&lt;/p&gt;
&lt;p&gt;I sense &lt;em&gt;“But isn’t this just reimplementing &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; and &lt;code&gt;srcset&lt;/code&gt; on the
back-end?”&lt;/em&gt; is your first question.&lt;/p&gt;
&lt;p&gt;In a way, yes—but with an important distinction: when an application uses
client hints to craft media responses, most (if not all) of the work is much
easier to automate, which can include a service (such as a CDN) that can do this
on your behalf. Whereas with HTML solutions, new markup needs to be written to
provide for every use case. Sure, you &lt;em&gt;can&lt;/em&gt; automate markup generation. If your
design or requirements change, though, there’s a good chance you’ll need to
revisit your automation strategy in the future.&lt;/p&gt;
&lt;p&gt;Client hints make it possible to start with a lossless, high-resolution
image that can then be dynamically resized to be optimal for &lt;em&gt;any&lt;/em&gt; combination
of screen and layout. Unlike &lt;code&gt;srcset&lt;/code&gt;, which requires you to enumerate a fixed
list of possible image candidates for the browser to choose from, this approach
can be more flexible. While &lt;code&gt;srcset&lt;/code&gt; forces you to offer browsers a coarse set
of variants—say, &lt;code&gt;256w&lt;/code&gt;, &lt;code&gt;512w&lt;/code&gt;, &lt;code&gt;768w&lt;/code&gt;, and &lt;code&gt;1024w&lt;/code&gt;—a client-hints
powered solution can serve all widths, without a giant pile of markup.&lt;/p&gt;
&lt;p&gt;Of course, you don’t &lt;em&gt;have&lt;/em&gt; to write image selection logic yourself. &lt;a href=&quot;https://cloudinary.com/blog/automatic_responsive_images_with_client_hints&quot; rel=&quot;noopener&quot;&gt;Cloudinary
uses client hints to craft image responses when you use the &lt;code&gt;w_auto&lt;/code&gt;
parameter&lt;/a&gt;,
and observed that median users downloaded 42% less bytes when using browsers
supporting client hints.&lt;/p&gt;
&lt;p&gt;But beware! &lt;a href=&quot;https://cloudinary.com/blog/client_hints_and_responsive_images_what_changed_in_chrome_67&quot; rel=&quot;noopener&quot;&gt;Changes in the desktop version of Chrome 67 have removed support
for cross-origin client
hints&lt;/a&gt;.
Fortunately, these restrictions don’t affect mobile versions of Chrome, and
they&#39;ll be lifted altogether for all platforms when work on &lt;a href=&quot;https://wicg.github.io/feature-policy/&quot; rel=&quot;noopener&quot;&gt;Feature
Policy&lt;/a&gt; is complete.&lt;/p&gt;
&lt;h3 id=&quot;helping-users-on-slow-networks&quot;&gt;Helping users on slow networks &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints/#helping-users-on-slow-networks&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Adaptive performance&lt;/em&gt; is the idea that we can adjust how we deliver resources
based on the information client hints makes available to us; specifically
information surrounding the current state of the user’s network connection.&lt;/p&gt;
&lt;p&gt;Where Sconnie Timber’s site is concerned, we take steps to lighten the load when
networks are slow, with &lt;code&gt;Save-Data&lt;/code&gt;, &lt;code&gt;ECT&lt;/code&gt;, &lt;code&gt;RTT&lt;/code&gt;, and &lt;code&gt;Downlink&lt;/code&gt; headers being
examined in our back-end code. When this is done, we generate a network quality
score we can use to determine if we should intervene for a better user
experience. This network score is between &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt;, where &lt;code&gt;0&lt;/code&gt; is the worst
network possible network quality, and &lt;code&gt;1&lt;/code&gt; is the best.&lt;/p&gt;
&lt;p&gt;Initially, we check if &lt;code&gt;Save-Data&lt;/code&gt; is present. If it is, the score is set to
&lt;code&gt;0&lt;/code&gt;, as we’re assuming the user wants us to do whatever is necessary to make the
experience lighter and faster.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;Save-Data&lt;/code&gt; is absent, however, we move on and weigh the values of the &lt;code&gt;ECT&lt;/code&gt;,
&lt;code&gt;RTT&lt;/code&gt;, and &lt;code&gt;Downlink&lt;/code&gt; hints to calculate a score that describes network
connection quality. The &lt;a href=&quot;https://github.com/malchata/client-hints-example/blob/master/includes/functions.php#L8&quot; rel=&quot;noopener&quot;&gt;network score generation source
code&lt;/a&gt;
is available on Github. The takeaway is, if we use the network-related hints in
&lt;em&gt;some&lt;/em&gt; fashion, we can make experiences better for those who are on slow
networks.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A comparison of a site that doesn&amp;#x27;t use client hints to adapt to a slow network connection (left) and the same site that does (right).&quot; decoding=&quot;async&quot; height=&quot;579&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/FoRAMNXoSAs3xgMCCP0O.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/FoRAMNXoSAs3xgMCCP0O.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/FoRAMNXoSAs3xgMCCP0O.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/FoRAMNXoSAs3xgMCCP0O.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/FoRAMNXoSAs3xgMCCP0O.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/FoRAMNXoSAs3xgMCCP0O.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/FoRAMNXoSAs3xgMCCP0O.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/FoRAMNXoSAs3xgMCCP0O.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/FoRAMNXoSAs3xgMCCP0O.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/FoRAMNXoSAs3xgMCCP0O.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/FoRAMNXoSAs3xgMCCP0O.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/FoRAMNXoSAs3xgMCCP0O.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/FoRAMNXoSAs3xgMCCP0O.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/FoRAMNXoSAs3xgMCCP0O.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/FoRAMNXoSAs3xgMCCP0O.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/FoRAMNXoSAs3xgMCCP0O.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/FoRAMNXoSAs3xgMCCP0O.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/FoRAMNXoSAs3xgMCCP0O.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;&lt;em&gt;&lt;strong&gt;Figure 2&lt;/strong&gt;. An “about us” page for a local
business site. The baseline experience includes web fonts, JavaScript to drive a
carousel and accordion behavior, as well as content images. These are all things
we can omit when network conditions are too slow to load them
quickly.&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;When sites adapt to the information client hints provide, we don’t have to adopt
an “all or nothing” approach. We can intelligently decide which resources to
send. We can modify our responsive image selection logic to send lower quality
images for a given display to speed up loading performance when network quality
is poor.&lt;/p&gt;
&lt;p&gt;In this example, we can see the impact client hints have when it comes to
improving the performance of sites on slower networks. Below is a WebPagetest
waterfall of a site on a slow network that doesn’t adapt to client hints:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A WebPagetest waterfall of the Sconnie Timber site loading all resources on a slow network connection.&quot; decoding=&quot;async&quot; height=&quot;420&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/TSBjfn9vApO8X95tZKbv.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/TSBjfn9vApO8X95tZKbv.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/TSBjfn9vApO8X95tZKbv.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/TSBjfn9vApO8X95tZKbv.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/TSBjfn9vApO8X95tZKbv.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/TSBjfn9vApO8X95tZKbv.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/TSBjfn9vApO8X95tZKbv.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/TSBjfn9vApO8X95tZKbv.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/TSBjfn9vApO8X95tZKbv.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/TSBjfn9vApO8X95tZKbv.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/TSBjfn9vApO8X95tZKbv.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/TSBjfn9vApO8X95tZKbv.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/TSBjfn9vApO8X95tZKbv.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/TSBjfn9vApO8X95tZKbv.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/TSBjfn9vApO8X95tZKbv.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/TSBjfn9vApO8X95tZKbv.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/TSBjfn9vApO8X95tZKbv.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/TSBjfn9vApO8X95tZKbv.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;&lt;em&gt;&lt;strong&gt;Figure 3&lt;/strong&gt;. A resource heavy site loading images,
scripts, and fonts on a slow connection.&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;And now a waterfall for the same site on the same slow connection, except this
time, the site uses client hints to eliminate non-critical page resources:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A WebPagetest waterfall of the Sconnie Timber site using client hints to decide not to load non-critical resources on a slow network connection.&quot; decoding=&quot;async&quot; height=&quot;171&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CsTBfO9sEKsYqXUu4DPc.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CsTBfO9sEKsYqXUu4DPc.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CsTBfO9sEKsYqXUu4DPc.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CsTBfO9sEKsYqXUu4DPc.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CsTBfO9sEKsYqXUu4DPc.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CsTBfO9sEKsYqXUu4DPc.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CsTBfO9sEKsYqXUu4DPc.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CsTBfO9sEKsYqXUu4DPc.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CsTBfO9sEKsYqXUu4DPc.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CsTBfO9sEKsYqXUu4DPc.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CsTBfO9sEKsYqXUu4DPc.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CsTBfO9sEKsYqXUu4DPc.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CsTBfO9sEKsYqXUu4DPc.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CsTBfO9sEKsYqXUu4DPc.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CsTBfO9sEKsYqXUu4DPc.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CsTBfO9sEKsYqXUu4DPc.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CsTBfO9sEKsYqXUu4DPc.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/CsTBfO9sEKsYqXUu4DPc.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;&lt;em&gt;&lt;strong&gt;Figure 4&lt;/strong&gt;. The same site on the same connection,
only the “nice to have” resources are excluded in favor of faster
loading.&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Client hints reduced the page load time from over 45 seconds to &lt;em&gt;less than a
tenth of that time&lt;/em&gt;. The benefits of client hints in this scenario can’t be
emphasized enough and can be a serious boon to users seeking critical
information over slow networks.&lt;/p&gt;
&lt;p&gt;Furthermore, it’s possible to use client hints without breaking the experience
for browsers that don’t support them. For example, if we want adjust resource
delivery suing the value of the &lt;code&gt;ECT&lt;/code&gt; hint while still delivering the full
experience for non-supporting browsers, we can set fall back to a default value
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 comment&quot;&gt;// Set the ECT value to &quot;4g&quot; by default.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token variable&quot;&gt;$ect&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$_SERVER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;HTTP_ECT&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 operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$_SERVER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;HTTP_ECT&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 string double-quoted-string&quot;&gt;&quot;4g&quot;&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;Here, &lt;code&gt;&amp;quot;4g&amp;quot;&lt;/code&gt; represents the highest quality network connection the &lt;code&gt;ECT&lt;/code&gt; header
describes. If we initialize &lt;code&gt;$ect&lt;/code&gt; to &lt;code&gt;&amp;quot;4g&amp;quot;&lt;/code&gt;, browsers that don’t support client
hints won’t be affected. Opt-in FTW!&lt;/p&gt;
&lt;h2 id=&quot;mind-those-caches&quot;&gt;Mind those caches! &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints/#mind-those-caches&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Whenever you change a response based on an HTTP header, you need to be aware of
how caches will handle future fetches for that resource. &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Vary&quot; rel=&quot;noopener&quot;&gt;The &lt;code&gt;Vary&lt;/code&gt;
header&lt;/a&gt; is
indispensable here, as it keys cache entries to the value of the request headers
supplied to it. Simply put, if you modify any response based on a given HTTP
request header, you should almost always include request that header in &lt;code&gt;Vary&lt;/code&gt;
like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Vary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;DPR, Width&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;There’s a &lt;em&gt;big&lt;/em&gt; caveat to this, though: You never want to &lt;code&gt;Vary&lt;/code&gt; cacheable
responses on a frequently changing header (like &lt;code&gt;Cookie&lt;/code&gt;) because those
resources become effectively uncacheable. Knowing this, you might want to avoid
&lt;code&gt;Vary&lt;/code&gt;ing on client hint headers such as &lt;code&gt;RTT&lt;/code&gt; or &lt;code&gt;Downlink&lt;/code&gt;, because those are
connection factors that could change quite often. If you want to modify
responses on those headers, consider keying only the &lt;code&gt;ECT&lt;/code&gt; header, which will
minimize cache misses.&lt;/p&gt;
&lt;p&gt;Of course, this only applies if you’re caching a response in the first place.
For example, you won’t cache HTML assets if their content is dynamic, because
that can break the user experience on repeat visits. In cases like these, feel
free to modify such responses on whatever basis you feel is necessary and not
concern yourself with &lt;code&gt;Vary&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;client-hints-in-service-workers&quot;&gt;Client hints in service workers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints/#client-hints-in-service-workers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Content negotiation isn’t just for servers anymore! Because service workers act
as proxies between clients and the servers, you have control over how resources
are delivered via JavaScript. This includes client hints. In the service worker
&lt;code&gt;fetch&lt;/code&gt; event, you can use the &lt;code&gt;event&lt;/code&gt; object’s
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Request/headers&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;request.headers.get&lt;/code&gt;&lt;/a&gt;
method to read a resource’s request headers like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;self&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;fetch&#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;let&lt;/span&gt; dpr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;DPR&#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 keyword&quot;&gt;let&lt;/span&gt; viewportWidth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Viewport-Width&#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 keyword&quot;&gt;let&lt;/span&gt; width &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Width&#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;  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;respondWith&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;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&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;// Do what you will with these hints!&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 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;/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; Because not all browsers support client hints, you’ll need to check what values &lt;code&gt;event.request.headers.get&lt;/code&gt; returns. A possible alternative approach is to record JS equivalents such as &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Window/devicePixelRatio&quot;&gt;&lt;code&gt;window.devicePixelRatio&lt;/code&gt;&lt;/a&gt; or &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Window/innerWidth&quot;&gt;&lt;code&gt;window.innerWidth&lt;/code&gt;&lt;/a&gt; to &lt;a href=&quot;https://web.dev/web/ilt/pwa/working-with-indexeddb&quot;&gt;&lt;code&gt;IndexedDB&lt;/code&gt;&lt;/a&gt;, which is accessible in the service worker scope. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Any client hint header you opt into can be read in this fashion. Though that’s
not the only way you can get some of this information. Network-specific hints
can be read in these equivalent JavaScript properties in the &lt;code&gt;navigator&lt;/code&gt; object:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
    &lt;tr&gt;
    &lt;th&gt;Client hint&lt;/th&gt;
    &lt;th&gt;JS equivalent&lt;/th&gt;
    &lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
    &lt;tr&gt;
    &lt;td&gt;`ECT`&lt;/td&gt;
    &lt;td&gt;`navigator.connection.effectiveType`&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
    &lt;td&gt;`RTT`&lt;/td&gt;
    &lt;td&gt;`navigator.connection.rtt` &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
    &lt;td&gt;`Save-Data`&lt;/td&gt;
    &lt;td&gt;`navigator.connection.saveData`&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
    &lt;td&gt;`Downlink`&lt;/td&gt;
    &lt;td&gt;`navigator.connection.downlink` &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
    &lt;td&gt;`Device-Memory`&lt;/td&gt;
    &lt;td&gt;`navigator.deviceMemory`&lt;/td&gt;
    &lt;/tr&gt;
&lt;/tbody&gt;
&lt;caption&gt;
    Imagemin plugins for filetypes.
&lt;/caption&gt;
&lt;/table&gt;
&lt;p&gt;Because these APIs aren’t available everywhere you need feature check with the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/in&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;in&lt;/code&gt;
operator&lt;/a&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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;connection&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; navigator&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;// Work with netinfo API properties in JavaScript!&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;From here, you can use logic similar to what you would use on the server, except
you don’t &lt;em&gt;need&lt;/em&gt; a server to negotiate content with client hints. Service
workers alone have the power to make experiences faster and more resilient due
to the added ability they have to serve content when the user is offline.&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints/#wrapping-up&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With client hints, we have the power to make experiences faster for users in a
fully progressive way. We can serve media based on the user’s device
capabilities in a way that makes serving responsive images easier than relying
on &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; and &lt;code&gt;srcset&lt;/code&gt;, especially for complex use cases. This enables us
to not only reduce time and effort on the development side, but also to optimize
resources—particularly images—in a way that targets user’s screens
more finely than &lt;picture&gt; and srcset can.&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Perhaps more importantly, we can sniff out poor network connections and bridge
the gap for users by modifying what we send—and how we send it. This can
go a &lt;em&gt;long&lt;/em&gt; way in making sites easier to access for users on fragile networks.
Combined with service workers, we can create incredibly fast sites that are
available offline.&lt;/p&gt;
&lt;p&gt;While client hints are only available in Chrome—and Chromium-based
browsers—it’s possible to use them in such a way that doesn’t encumber
browsers that don’t support them. Consider using client hints to create truly
inclusive and adaptable experiences that are aware of every user’s device
capabilities, and the networks they connect to. Hopefully, other browser vendors
will see the value of them and show intent to implement.&lt;/p&gt;
&lt;h3 id=&quot;resources&quot;&gt;Resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints/#resources&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://cloudinary.com/blog/automatic_responsive_images_with_client_hints&quot; rel=&quot;noopener&quot;&gt;Automatic Responsive Images with Client
Hints&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cloudinary.com/blog/client_hints_and_responsive_images_what_changed_in_chrome_67&quot; rel=&quot;noopener&quot;&gt;Client Hints and Responsive Images—What Changed in Chrome
67&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=md7Ua82fPe4&quot; rel=&quot;noopener&quot;&gt;Take a (Client) Hint!&lt;/a&gt;
(&lt;a href=&quot;https://jlwagner.net/talks/take-a-client-hint&quot; rel=&quot;noopener&quot;&gt;Slides&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/web/fundamentals/performance/optimizing-content-efficiency/save-data/&quot;&gt;Delivering Fast and Light Applications with
&lt;code&gt;Save-Data&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Thank you to &lt;a href=&quot;https://twitter.com/igrigorik&quot; rel=&quot;noopener&quot;&gt;Ilya Grigorik&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/etportis&quot; rel=&quot;noopener&quot;&gt;Eric
Portis&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/jeffposnick&quot; rel=&quot;noopener&quot;&gt;Jeff Posnick&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/yoavweiss&quot; rel=&quot;noopener&quot;&gt;Yoav
Weiss&lt;/a&gt;, and &lt;a href=&quot;https://twitter.com/estellevw&quot; rel=&quot;noopener&quot;&gt;Estelle Weyl&lt;/a&gt; for their
valuable feedback and edits on this article.&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Reduce JavaScript payloads with code splitting</title>
    <link href="https://web.dev/reduce-javascript-payloads-with-code-splitting/"/>
    <updated>2018-11-05T00:00:00Z</updated>
    <id>https://web.dev/reduce-javascript-payloads-with-code-splitting/</id>
    <content type="html" mode="escaped">&lt;p&gt;Nobody likes waiting.
&lt;strong&gt;&lt;a href=&quot;https://www.thinkwithgoogle.com/intl/en-154/insights-inspiration/research-data/need-mobile-speed-how-mobile-latency-impacts-publisher-revenue/&quot; rel=&quot;noopener&quot;&gt;Over 50% of users abandon a website if it takes longer than 3 seconds to load&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Sending large JavaScript payloads impacts the speed of your site
significantly. Instead of shipping all the JavaScript to your user as soon as
the first page of your application is loaded, split your bundle into
multiple pieces and only send what&#39;s necessary at the very beginning.&lt;/p&gt;
&lt;h2 id=&quot;why-is-code-splitting-beneficial&quot;&gt;Why is code splitting beneficial? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-javascript-payloads-with-code-splitting/#why-is-code-splitting-beneficial&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Code splitting is a technique that seeks to minimize startup time. When we ship less JavaScript at startup, we can get applications to be &lt;a href=&quot;https://web.dev/tti/&quot;&gt;interactive faster&lt;/a&gt; by minimizing main thread work during this critical period.&lt;/p&gt;
&lt;p&gt;When it comes to &lt;a href=&quot;https://web.dev/vitals/&quot;&gt;Core Web Vitals&lt;/a&gt;, reducing JavaScript payloads downloaded at startup will contribute to better &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; times. The reasoning behind this is that, by freeing up the main thread, the application is able to respond to user inputs more quickly by reducing JavaScript parse, compile, and execution-related startup costs.&lt;/p&gt;
&lt;p&gt;Depending on your website&#39;s architecture—particularly if your website relies heavily on client-side rendering—reducing the size of JavaScript payloads responsible for rendering markup may lead to improved &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt; times. This can occur when the LCP resource is &lt;a href=&quot;https://web.dev/optimize-lcp/#optimize-when-the-resource-is-discovered&quot;&gt;delayed in being discovered by the browser&lt;/a&gt; until after client-side markup is completed, or when the main thread is too busy to &lt;a href=&quot;https://web.dev/optimize-lcp/#2-eliminate-element-render-delay&quot;&gt;render that LCP element&lt;/a&gt;. Both scenarios can delay the LCP time for the page.&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; If your website relies on client-side rendering exclusively, you should seek to use &lt;a href=&quot;https://web.dev/rendering-on-the-web/#server-rendering&quot;&gt;Server-Side Rendering (SSR)&lt;/a&gt; to ensure that the server is sending meaningful markup to the client in response to the navigation request. This can help &lt;a href=&quot;https://web.dev/preload-scanner/&quot;&gt;the browser preload scanner&lt;/a&gt; to opportunistically fetch resources &lt;a href=&quot;https://web.dev/preload-scanner/#rendering-markup-with-client-side-javascript&quot;&gt;more effectively&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;measure&quot;&gt;Measure &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-javascript-payloads-with-code-splitting/#measure&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Lighthouse displays a failed audit when a significant amount of time is taken to
execute all the JavaScript on a page.&lt;/p&gt;
&lt;img alt=&quot;A failing Lighthouse audit showing scripts taking too long to execute.&quot; decoding=&quot;async&quot; height=&quot;100&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 797px) 797px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/p0Ahh3pzXog3jPdDp6La.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/p0Ahh3pzXog3jPdDp6La.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/p0Ahh3pzXog3jPdDp6La.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/p0Ahh3pzXog3jPdDp6La.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/p0Ahh3pzXog3jPdDp6La.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/p0Ahh3pzXog3jPdDp6La.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/p0Ahh3pzXog3jPdDp6La.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/p0Ahh3pzXog3jPdDp6La.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/p0Ahh3pzXog3jPdDp6La.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/p0Ahh3pzXog3jPdDp6La.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/p0Ahh3pzXog3jPdDp6La.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/p0Ahh3pzXog3jPdDp6La.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/p0Ahh3pzXog3jPdDp6La.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/p0Ahh3pzXog3jPdDp6La.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/p0Ahh3pzXog3jPdDp6La.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/p0Ahh3pzXog3jPdDp6La.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/p0Ahh3pzXog3jPdDp6La.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/p0Ahh3pzXog3jPdDp6La.png?auto=format&amp;w=1594 1594w&quot; width=&quot;797&quot; /&gt;
&lt;p&gt;Split the JavaScript bundle to only send the code needed for the initial route when the
user loads an application. This minimizes the amount of script that needs to be
parsed and compiled, which results in faster page load times.&lt;/p&gt;
&lt;p&gt;Popular module bundlers like &lt;a href=&quot;https://webpack.js.org/guides/code-splitting/&quot; rel=&quot;noopener&quot;&gt;webpack&lt;/a&gt;,
&lt;a href=&quot;https://parceljs.org/code_splitting.html&quot; rel=&quot;noopener&quot;&gt;Parcel&lt;/a&gt;, and
&lt;a href=&quot;https://rollupjs.org/guide/en#dynamic-import&quot; rel=&quot;noopener&quot;&gt;Rollup&lt;/a&gt; allow you to split your
bundles using &lt;a href=&quot;https://v8.dev/features/dynamic-import&quot; rel=&quot;noopener&quot;&gt;dynamic imports&lt;/a&gt;.
For example, consider the following code snippet that shows an example of a
&lt;code&gt;someFunction&lt;/code&gt; method that gets fired when a form is submitted.&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; moduleA &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;library&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;form&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;&quot;submit&quot;&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 operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  e&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 function&quot;&gt;someFunction&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;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;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;someFunction&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 punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// uses moduleA&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 here, &lt;code&gt;someFunction&lt;/code&gt; uses a module imported from a particular library. If
this module is not being used elsewhere, the code block can be modified to use a
dynamic import to fetch it only when the form is submitted by the user.&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;highlight-line&quot;&gt;form&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;&quot;submit&quot;&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 operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  e&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;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&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;&lt;span class=&quot;token string&quot;&gt;&#39;library.moduleA&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&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;module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;default&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// using the default export&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&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 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;someFunction&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;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;handleError&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;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&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&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;someFunction&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 punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// uses moduleA&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The code that makes up the module does not get included into the initial bundle
and is now &lt;strong&gt;lazy loaded&lt;/strong&gt;, or provided to the user only when it is needed after
the form submission. To further improve page performance, &lt;a href=&quot;https://web.dev/preload-critical-assets&quot;&gt;preload critical chunks to prioritize and fetch them sooner&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Although the previous code snippet is a simple example, lazy loading third party
dependencies is not a common pattern in larger applications. Usually, third
party dependencies are split into a separate vendor bundle that can be cached
since they don&#39;t update as often. You can read more about how the
&lt;a href=&quot;https://webpack.js.org/plugins/split-chunks-plugin/&quot; rel=&quot;noopener&quot;&gt;&lt;strong&gt;SplitChunksPlugin&lt;/strong&gt;&lt;/a&gt; can
help you do this.&lt;/p&gt;
&lt;p&gt;Splitting on the route or component level when using a client-side framework is
a simpler approach to lazy loading different parts of your application. Many
popular frameworks that use webpack provide abstractions to make lazy loading
easier than diving into the configurations yourself.&lt;/p&gt;
</content>
    <author>
      <name>Houssein Djirdeh</name>
    </author><author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Replace animated GIFs with video for faster page loads</title>
    <link href="https://web.dev/replace-gifs-with-videos/"/>
    <updated>2018-11-05T00:00:00Z</updated>
    <id>https://web.dev/replace-gifs-with-videos/</id>
    <content type="html" mode="escaped">&lt;p&gt;Have you ever seen an animated GIF on a service like Imgur or Gfycat, inspected
it in your dev tools, only to find out that GIF was really a video? There&#39;s a
good reason for that. Animated GIFs can be downright &lt;em&gt;huge&lt;/em&gt;.&lt;/p&gt;
&lt;img alt=&quot;DevTools network panel showing a 13.7 MB gif.&quot; decoding=&quot;async&quot; height=&quot;155&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/3UZ0b9dDotVIXWQT5Auk.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/3UZ0b9dDotVIXWQT5Auk.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/3UZ0b9dDotVIXWQT5Auk.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/3UZ0b9dDotVIXWQT5Auk.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/3UZ0b9dDotVIXWQT5Auk.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/3UZ0b9dDotVIXWQT5Auk.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/3UZ0b9dDotVIXWQT5Auk.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/3UZ0b9dDotVIXWQT5Auk.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/3UZ0b9dDotVIXWQT5Auk.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/3UZ0b9dDotVIXWQT5Auk.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/3UZ0b9dDotVIXWQT5Auk.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/3UZ0b9dDotVIXWQT5Auk.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/3UZ0b9dDotVIXWQT5Auk.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/3UZ0b9dDotVIXWQT5Auk.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/3UZ0b9dDotVIXWQT5Auk.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/3UZ0b9dDotVIXWQT5Auk.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/3UZ0b9dDotVIXWQT5Auk.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/3UZ0b9dDotVIXWQT5Auk.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Thankfully, this is one of those areas of loading performance where you can do
relatively little work to realize huge gains! &lt;strong&gt;By converting large GIFs to
videos, you can save big on users&#39; bandwidth&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;measure-first&quot;&gt;Measure first &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/replace-gifs-with-videos/#measure-first&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use Lighthouse to check your site for GIFs that can be converted to videos. In
DevTools, click on the Audits tab and check the Performance checkbox. Then run
Lighthouse and check the report.
If you have any GIFs that can be converted, you should see a suggestion to &amp;quot;Use
video formats for animated content&amp;quot;:&lt;/p&gt;
&lt;img alt=&quot;A failing Lighthouse audit, use video formats for animated content.&quot; decoding=&quot;async&quot; height=&quot;173&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/KOSr9IivnkyaFk6RJ5u1.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/KOSr9IivnkyaFk6RJ5u1.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/KOSr9IivnkyaFk6RJ5u1.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/KOSr9IivnkyaFk6RJ5u1.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/KOSr9IivnkyaFk6RJ5u1.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/KOSr9IivnkyaFk6RJ5u1.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/KOSr9IivnkyaFk6RJ5u1.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/KOSr9IivnkyaFk6RJ5u1.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/KOSr9IivnkyaFk6RJ5u1.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/KOSr9IivnkyaFk6RJ5u1.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/KOSr9IivnkyaFk6RJ5u1.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/KOSr9IivnkyaFk6RJ5u1.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/KOSr9IivnkyaFk6RJ5u1.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/KOSr9IivnkyaFk6RJ5u1.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/KOSr9IivnkyaFk6RJ5u1.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/KOSr9IivnkyaFk6RJ5u1.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/KOSr9IivnkyaFk6RJ5u1.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/KOSr9IivnkyaFk6RJ5u1.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h2 id=&quot;create-mpeg-videos&quot;&gt;Create MPEG videos &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/replace-gifs-with-videos/#create-mpeg-videos&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are a number of ways to convert GIFs to video,
&lt;strong&gt;&lt;a href=&quot;https://www.ffmpeg.org/&quot; rel=&quot;noopener&quot;&gt;FFmpeg&lt;/a&gt;&lt;/strong&gt; is the tool used in this guide.
To use FFmpeg to convert the GIF, &lt;code&gt;my-animation.gif&lt;/code&gt; to an MP4 video, run the
following command in your console:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i my-animation.gif -b:v &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; -crf &lt;span class=&quot;token number&quot;&gt;25&lt;/span&gt; -f mp4 -vcodec libx264 -pix_fmt yuv420p my-animation.mp4&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This tells FFmpeg to take &lt;code&gt;my-animation.gif&lt;/code&gt; as the &lt;strong&gt;input&lt;/strong&gt;, signified by the
&lt;code&gt;-i&lt;/code&gt; flag, and to convert it to a video called &lt;code&gt;my-animation.mp4&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The libx264 encoder only works with files that have even dimensions, like 320px
by 240px. If the input GIF has odd dimensions you can include a crop filter to
avoid FFmpeg throwing a &#39;height/width not divisible by 2&#39; error:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i my-animation.gif -vf &lt;span class=&quot;token string&quot;&gt;&quot;crop=trunc(iw/2)*2:trunc(ih/2)*2&quot;&lt;/span&gt; -b:v &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; -crf &lt;span class=&quot;token number&quot;&gt;25&lt;/span&gt; -f mp4 -vcodec libx264 -pix_fmt yuv420p my-animation.mp4&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;create-webm-videos&quot;&gt;Create WebM videos &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/replace-gifs-with-videos/#create-webm-videos&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While MP4 has been around since 1999, WebM is a relatively new file format
initially released in 2010. WebM videos are much smaller than MP4 videos, but
not all browsers support WebM so it makes sense to generate both.&lt;/p&gt;
&lt;p&gt;To use FFmpeg to convert &lt;code&gt;my-animation.gif&lt;/code&gt; to a WebM video, run the following
command in your console:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg -i my-animation.gif -c vp9 -b:v &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; -crf &lt;span class=&quot;token number&quot;&gt;41&lt;/span&gt; my-animation.webm&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;compare-the-difference&quot;&gt;Compare the difference &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/replace-gifs-with-videos/#compare-the-difference&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The cost savings between a GIF and a video can be pretty significant.&lt;/p&gt;
&lt;img alt=&quot;File size comparison showing 3.7 MB for the gif, 551 KB for the mp4 and 341 KB for the webm.&quot; decoding=&quot;async&quot; height=&quot;188&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/LWzvOWaOdMnNLTPWjayt.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/LWzvOWaOdMnNLTPWjayt.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/LWzvOWaOdMnNLTPWjayt.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/LWzvOWaOdMnNLTPWjayt.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/LWzvOWaOdMnNLTPWjayt.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/LWzvOWaOdMnNLTPWjayt.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/LWzvOWaOdMnNLTPWjayt.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/LWzvOWaOdMnNLTPWjayt.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/LWzvOWaOdMnNLTPWjayt.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/LWzvOWaOdMnNLTPWjayt.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/LWzvOWaOdMnNLTPWjayt.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/LWzvOWaOdMnNLTPWjayt.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/LWzvOWaOdMnNLTPWjayt.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/LWzvOWaOdMnNLTPWjayt.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/LWzvOWaOdMnNLTPWjayt.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/LWzvOWaOdMnNLTPWjayt.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/LWzvOWaOdMnNLTPWjayt.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/LWzvOWaOdMnNLTPWjayt.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;In this example, the initial GIF is 3.7 MB, compared to the MP4 version, which
is 551 KB, and the WebM version, which is only 341 KB!&lt;/p&gt;
&lt;h2 id=&quot;replace-the-gif-img-with-a-video&quot;&gt;Replace the GIF img with a video &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/replace-gifs-with-videos/#replace-the-gif-img-with-a-video&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Animated GIFs have three key traits that a video needs to replicate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They play automatically.&lt;/li&gt;
&lt;li&gt;They loop continuously (usually, but it is possible to prevent looping).&lt;/li&gt;
&lt;li&gt;They&#39;re silent.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Luckily, you can recreate these behaviors using the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;video autoplay loop muted playsinline&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;/video&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;A &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element with these attributes plays automatically, loops endlessly,
plays no audio, and plays inline (that is, not full screen), all the hallmark
behaviors expected of animated GIFs! 🎉&lt;/p&gt;
&lt;p&gt;Finally, the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element requires one or more &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; child elements
pointing to different video files that the browser can choose from, depending on
the browser&#39;s format support. Provide both WebM and MP4, so that if a browser
doesn&#39;t support WebM, it can fall back to MP4.&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;video&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autoplay&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;muted&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;playsinline&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;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;my-animation.webm&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;video/webm&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;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;my-animation.mp4&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;video/mp4&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;video&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-quaternary-box-bg color-quaternary-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; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Code brackets&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;M9.41 16.59L8 18l-6-6 6-6 1.41 1.41L4.83 12l4.58 4.59zm5.18-9.18L16 6l6 6-6 6-1.41-1.41L19.17 12l-4.58-4.59z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Try it&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;a href=&quot;https://web.dev/codelab-replace-gifs-with-video&quot;&gt;Replace an animated GIF with a video&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Browsers don&#39;t speculate about which &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; is optimal, so the order of &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt;&#39;s matters. For example, if you specify an MP4 video first and the browser supports WebM, browsers will skip the WebM &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; and use the MPEG-4 instead. If you prefer a WebM &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; be used first, specify it first! &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;effect-on-largest-contentful-paint-lcp&quot;&gt;Effect on Largest Contentful Paint (LCP) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/replace-gifs-with-videos/#effect-on-largest-contentful-paint-lcp&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It should be noted that while &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements are candidates for LCP, &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; elements without a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/video#attr-poster&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;poster&lt;/code&gt; image&lt;/a&gt; are not &lt;a href=&quot;https://web.dev/lcp/#what-elements-are-considered&quot;&gt;LCP candidates&lt;/a&gt;. The solution in the case of emulating animated GIFs is &lt;em&gt;not&lt;/em&gt; to add &lt;code&gt;poster&lt;/code&gt; attribute to your &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; elements, because that image will go unused.&lt;/p&gt;
&lt;p&gt;What does this mean for your website? The recommendation is to stick with using a &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; instead of an animated GIF, but with the understanding that such media will not be a candidate for LCP, and the next largest candidate will be used instead. As GIFs and &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt;s are typically larger and so slower to download, moving to a different LCP candidate will likely even improve the site&#39;s LCP.&lt;/p&gt;
</content>
    <author>
      <name>Houssein Djirdeh</name>
    </author><author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Preload critical assets to improve loading speed</title>
    <link href="https://web.dev/preload-critical-assets/"/>
    <updated>2018-11-05T00:00:00Z</updated>
    <id>https://web.dev/preload-critical-assets/</id>
    <content type="html" mode="escaped">&lt;p&gt;When you open a web page, the browser requests the HTML document from a server, parses its contents, and submits separate requests for any referenced resources. As a developer, you already know about all the resources your page needs and which of them are the most important. You can use that knowledge to request the critical resources ahead of time and speed up the loading process. This post explains how to achieve that with &lt;code&gt;&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;how-preloading-works&quot;&gt;How preloading works &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-critical-assets/#how-preloading-works&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Preloading is best suited for resources typically discovered late by the browser.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Screenshot of Chrome DevTools Network panel.&quot; decoding=&quot;async&quot; height=&quot;509&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 701px) 701px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/Ad9PLq3DcQt9Ycp63z6O.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/Ad9PLq3DcQt9Ycp63z6O.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/Ad9PLq3DcQt9Ycp63z6O.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/Ad9PLq3DcQt9Ycp63z6O.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/Ad9PLq3DcQt9Ycp63z6O.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/Ad9PLq3DcQt9Ycp63z6O.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/Ad9PLq3DcQt9Ycp63z6O.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/Ad9PLq3DcQt9Ycp63z6O.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/Ad9PLq3DcQt9Ycp63z6O.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/Ad9PLq3DcQt9Ycp63z6O.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/Ad9PLq3DcQt9Ycp63z6O.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/Ad9PLq3DcQt9Ycp63z6O.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/Ad9PLq3DcQt9Ycp63z6O.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/Ad9PLq3DcQt9Ycp63z6O.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/Ad9PLq3DcQt9Ycp63z6O.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/Ad9PLq3DcQt9Ycp63z6O.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/Ad9PLq3DcQt9Ycp63z6O.png?auto=format&amp;w=1402 1402w&quot; width=&quot;701&quot; /&gt;
&lt;figcaption&gt;In this example, Pacifico font is defined in the stylesheet with a &lt;a href=&quot;https://web.dev/reduce-webfont-size/#defining-a-font-family-with-@font-face)&quot;&gt;&lt;code&gt;@font-face&lt;/code&gt;&lt;/a&gt; rule. The browser loads the font file only after it has finished downloading and parsing the stylesheet.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;By preloading a certain resource, you are telling the browser that you would like to fetch it sooner than the browser would otherwise discover it because you are certain that it is important for the current page.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Screenshot of Chrome DevTools Network panel after applying preloading.&quot; decoding=&quot;async&quot; height=&quot;509&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 701px) 701px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/PgRbERrxLGfF439yBMeY.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/PgRbERrxLGfF439yBMeY.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/PgRbERrxLGfF439yBMeY.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/PgRbERrxLGfF439yBMeY.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/PgRbERrxLGfF439yBMeY.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/PgRbERrxLGfF439yBMeY.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/PgRbERrxLGfF439yBMeY.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/PgRbERrxLGfF439yBMeY.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/PgRbERrxLGfF439yBMeY.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/PgRbERrxLGfF439yBMeY.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/PgRbERrxLGfF439yBMeY.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/PgRbERrxLGfF439yBMeY.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/PgRbERrxLGfF439yBMeY.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/PgRbERrxLGfF439yBMeY.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/PgRbERrxLGfF439yBMeY.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/PgRbERrxLGfF439yBMeY.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/PgRbERrxLGfF439yBMeY.png?auto=format&amp;w=1402 1402w&quot; width=&quot;701&quot; /&gt;
&lt;figcaption&gt;In this example, Pacifico font is preloaded, so the download happens in parallel with the stylesheet.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The critical request chain represents the order of resources that are prioritized and fetched by the browser. Lighthouse identifies assets that are on the third level of this chain as late-discovered. You can use the &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/uses-rel-preload/&quot; rel=&quot;noopener&quot;&gt;&lt;strong&gt;Preload key requests&lt;/strong&gt;&lt;/a&gt; audit to identify which resources to preload.&lt;/p&gt;
&lt;img alt=&quot;Lighthouse&amp;#x27;s preload key requests audit.&quot; decoding=&quot;async&quot; height=&quot;97&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 745px) 745px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/BPUTHBNZFbeXqb0dVx2f.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/BPUTHBNZFbeXqb0dVx2f.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/BPUTHBNZFbeXqb0dVx2f.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/BPUTHBNZFbeXqb0dVx2f.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/BPUTHBNZFbeXqb0dVx2f.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/BPUTHBNZFbeXqb0dVx2f.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/BPUTHBNZFbeXqb0dVx2f.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/BPUTHBNZFbeXqb0dVx2f.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/BPUTHBNZFbeXqb0dVx2f.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/BPUTHBNZFbeXqb0dVx2f.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/BPUTHBNZFbeXqb0dVx2f.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/BPUTHBNZFbeXqb0dVx2f.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/BPUTHBNZFbeXqb0dVx2f.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/BPUTHBNZFbeXqb0dVx2f.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/BPUTHBNZFbeXqb0dVx2f.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/BPUTHBNZFbeXqb0dVx2f.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/BPUTHBNZFbeXqb0dVx2f.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/BPUTHBNZFbeXqb0dVx2f.png?auto=format&amp;w=1490 1490w&quot; width=&quot;745&quot; /&gt;
&lt;p&gt;You can preload resources by adding a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag with &lt;code&gt;rel=&amp;quot;preload&amp;quot;&lt;/code&gt; to the head of your HTML document:&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;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.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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The browser caches preloaded resources so they are available immediately when needed. (It doesn&#39;t execute the scripts or apply the stylesheets.)&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; After implementing preloading, many sites, including &lt;a href=&quot;https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf&quot;&gt;Shopify, Financial Times and Treebo, saw 1-second improvements&lt;/a&gt; in user-centric metrics such as &lt;a href=&quot;https://web.dev/tti/&quot;&gt;Time to Interactive&lt;/a&gt; and &lt;a href=&quot;https://web.dev/fcp/&quot;&gt;First Contentful Paint&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Resource hints, for example, &lt;a href=&quot;https://web.dev/preconnect-and-dns-prefetch&quot;&gt;&lt;code&gt;preconnect&lt;/code&gt;&lt;/a&gt;and &lt;a href=&quot;https://web.dev/link-prefetch&quot;&gt;&lt;code&gt;prefetch&lt;/code&gt;&lt;/a&gt;, are executed as the browser sees fit. The &lt;code&gt;preload&lt;/code&gt;, on the other hand, is mandatory for the browser. Modern browsers are already pretty good at prioritizing resources, that&#39;s why it&#39;s important to use &lt;code&gt;preload&lt;/code&gt; sparingly and only preload the most critical resources.&lt;/p&gt;
&lt;p&gt;Unused preloads trigger a Console warning in Chrome, approximately 3 seconds after the &lt;code&gt;load&lt;/code&gt; event.&lt;/p&gt;
&lt;img alt=&quot;Chrome DevTools Console warning about unused preloaded resources.&quot; decoding=&quot;async&quot; height=&quot;228&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/z4FbCezjXHxaIhq188TU.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/z4FbCezjXHxaIhq188TU.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/z4FbCezjXHxaIhq188TU.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/z4FbCezjXHxaIhq188TU.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/z4FbCezjXHxaIhq188TU.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/z4FbCezjXHxaIhq188TU.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/z4FbCezjXHxaIhq188TU.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/z4FbCezjXHxaIhq188TU.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/z4FbCezjXHxaIhq188TU.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/z4FbCezjXHxaIhq188TU.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/z4FbCezjXHxaIhq188TU.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/z4FbCezjXHxaIhq188TU.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/z4FbCezjXHxaIhq188TU.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/z4FbCezjXHxaIhq188TU.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/z4FbCezjXHxaIhq188TU.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/z4FbCezjXHxaIhq188TU.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/z4FbCezjXHxaIhq188TU.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/z4FbCezjXHxaIhq188TU.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; &lt;code&gt;preload&lt;/code&gt; is supported  in all modern browsers. &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 50, 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; 50 &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 85, 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; 85 &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;/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/HTML/Link_types/preload#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt; &lt;/div&gt; &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;use-cases&quot;&gt;Use cases &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-critical-assets/#use-cases&quot;&gt;#&lt;/a&gt;&lt;/h2&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; At the time of writing, Chrome has an open &lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/detail?id=788757&quot;&gt;bug&lt;/a&gt; for preloaded requests that are fetched sooner than other higher priority resources. Until this is resolved, be wary of how preloaded resources can &amp;quot;jump the queue&amp;quot; and be requested sooner than they should. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;preloading-resources-defined-in-css&quot;&gt;Preloading resources defined in CSS &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-critical-assets/#preloading-resources-defined-in-css&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Fonts defined with &lt;a href=&quot;https://web.dev/reduce-webfont-size/#defining-a-font-family-with-@font-face&quot;&gt;&lt;code&gt;@font-face&lt;/code&gt;&lt;/a&gt; rules or background images defined in CSS files aren&#39;t discovered until the browser downloads and parses those CSS files. Preloading these resources ensures they are fetched before the CSS files have downloaded.&lt;/p&gt;
&lt;h3 id=&quot;preloading-css-files&quot;&gt;Preloading CSS files &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-critical-assets/#preloading-css-files&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you are using the &lt;a href=&quot;https://web.dev/extract-critical-css&quot;&gt;critical CSS approach&lt;/a&gt;, you split your CSS into two parts. The critical CSS required for rendering the above-the-fold content is inlined in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of the document and non-critical CSS is usually lazy-loaded with JavaScript. Waiting for JavaScript to execute before loading non-critical CSS can cause delays in rendering when users scroll, so it&#39;s a good idea to use &lt;code&gt;&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&lt;/code&gt; to initiate the download sooner.&lt;/p&gt;
&lt;h3 id=&quot;preloading-javascript-files&quot;&gt;Preloading JavaScript files &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-critical-assets/#preloading-javascript-files&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Because browsers don&#39;t execute preloaded files, preloading is useful to separate fetching from &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/bootup-time/&quot; rel=&quot;noopener&quot;&gt;execution&lt;/a&gt; which can improve metrics such as Time to Interactive. Preloading works best if you &lt;a href=&quot;https://web.dev/reduce-javascript-payloads-with-code-splitting&quot;&gt;split&lt;/a&gt; your JavaScript bundles and only preload critical chunks.&lt;/p&gt;
&lt;h2 id=&quot;how-to-implement-rel=preload&quot;&gt;How to implement rel=preload &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-critical-assets/#how-to-implement-rel=preload&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The simplest way to implement &lt;code&gt;preload&lt;/code&gt; is to add a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag to the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of the document:&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;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.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;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;Supplying the &lt;code&gt;as&lt;/code&gt; attribute helps the browser set the priority of the prefetched resource according to its type, set the right headers, and determine whether the resource already exists in the cache. Accepted values for this attribute include: &lt;code&gt;script&lt;/code&gt;, &lt;code&gt;style&lt;/code&gt;, &lt;code&gt;font&lt;/code&gt;, &lt;code&gt;image&lt;/code&gt;, and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/link#Attributes&quot; rel=&quot;noopener&quot;&gt;others&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; Take a look at the &lt;a href=&quot;https://docs.google.com/document/d/1bCDuq9H1ih9iNjgzyAL0gpwNFiEP4TZS-YLRp_RuMlc/edit&quot;&gt;Chrome Resource Priorities and Scheduling&lt;/a&gt; document to learn more about how the browser prioritizes different types of resources. &lt;/div&gt;&lt;/aside&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; Omitting the &lt;code&gt;as&lt;/code&gt; attribute, or having an invalid value is equivalent to an &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/XMLHttpRequest&quot;&gt;XHR request,&lt;/a&gt; where the browser doesn&#39;t know what it is fetching so it can&#39;t determine the correct priority. It can also cause some resources, such as scripts, to be fetched twice. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Some types of resources, such as fonts, are loaded in &lt;a href=&quot;https://www.w3.org/TR/css-fonts-3/#font-fetching-requirements&quot; rel=&quot;noopener&quot;&gt;anonymous mode&lt;/a&gt;. For those you must set the &lt;code&gt;crossorigin&lt;/code&gt; attribute with &lt;code&gt;preload&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;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;ComicSans.woff2&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;font&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;font/woff2&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;/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; Fonts preloaded without the &lt;code&gt;crossorigin&lt;/code&gt; attribute will be fetched twice! &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; elements also accept a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/link#attr-type&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;type&lt;/code&gt; attribute&lt;/a&gt;, which contains the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/MIME_types&quot; rel=&quot;noopener&quot;&gt;MIME type&lt;/a&gt; of the linked resource. The browsers use the value of the &lt;code&gt;type&lt;/code&gt; attribute to make sure that resources get preloaded only if their file type is supported. If a browser doesn&#39;t support the specified resource type, it will ignore the &lt;code&gt;&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-quaternary-box-bg color-quaternary-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; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Code brackets&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;M9.41 16.59L8 18l-6-6 6-6 1.41 1.41L4.83 12l4.58 4.59zm5.18-9.18L16 6l6 6-6 6-1.41-1.41L19.17 12l-4.58-4.59z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Try it&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;a href=&quot;https://web.dev/codelab-preload-web-fonts&quot;&gt;Improve the performance of a page by preloading web fonts&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;You can also preload any type of resource via the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Link&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Link&lt;/code&gt; HTTP header&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Link: &amp;lt;/css/style.css&amp;gt;; rel=&amp;quot;preload&amp;quot;; as=&amp;quot;style&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;A benefit of specifying &lt;code&gt;preload&lt;/code&gt; in the HTTP Header is that the browser doesn&#39;t need to parse the document to discover it, which can offer small improvements in some cases.&lt;/p&gt;
&lt;h3 id=&quot;preloading-javascript-modules-with-webpack&quot;&gt;Preloading JavaScript modules with webpack &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-critical-assets/#preloading-javascript-modules-with-webpack&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you are using a module bundler that creates build files of your application, you need to check if it supports the injection of preload tags. With &lt;a href=&quot;https://webpack.js.org/&quot; rel=&quot;noopener&quot;&gt;webpack&lt;/a&gt; version 4.6.0 or later, preloading is supported through the use of &lt;a href=&quot;https://webpack.js.org/api/module-methods/#magic-comments&quot; rel=&quot;noopener&quot;&gt;magic comments&lt;/a&gt; inside &lt;code&gt;import()&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;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;token comment&quot;&gt;/* webpackPreload: true */&lt;/span&gt;_ &lt;span class=&quot;token string&quot;&gt;&quot;CriticalChunk&quot;&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 you are using an older version of webpack, use a third-party plugin such as &lt;a href=&quot;https://github.com/GoogleChromeLabs/preload-webpack-plugin&quot; rel=&quot;noopener&quot;&gt;preload-webpack-plugin&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;effects-of-preloading-on-core-web-vitals&quot;&gt;Effects of preloading on Core Web Vitals &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-critical-assets/#effects-of-preloading-on-core-web-vitals&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Preloading is a powerful performance optimization that has an effect on loading speed. Such optimizations can lead to changes in your site&#39;s &lt;a href=&quot;https://web.dev/vitals/&quot;&gt;Core Web Vitals&lt;/a&gt;, and it&#39;s important to be aware them.&lt;/p&gt;
&lt;h3 id=&quot;largest-contentful-paint-lcp&quot;&gt;Largest Contentful Paint (LCP) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-critical-assets/#largest-contentful-paint-lcp&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Preloading has a powerful effect on &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt; when it comes to fonts and images, as both images and text nodes can be &lt;a href=&quot;https://web.dev/lcp/#what-elements-are-considered&quot;&gt;LCP candidates&lt;/a&gt;. Hero images and large runs of text that are rendered using web fonts can benefit significantly from a well-placed preload hint, and should be used when there are opportunities to deliver these important bits of content to the user faster.&lt;/p&gt;
&lt;p&gt;However, you want to be careful when it comes to preloading—and other optimizations! In particular, avoid preloading too many resources. If too many resources are prioritized, effectively none of them are. The effects of excessive preload hints will be especially detrimental to those on slower networks where bandwidth contention will be more evident.&lt;/p&gt;
&lt;p&gt;Instead, focus on a few high-value resources that you know will benefit from a well-placed preload. When preloading fonts, ensure that you&#39;re serving fonts in WOFF 2.0 format to reduce resource load time as much as possible. Since WOFF 2.0 has &lt;a href=&quot;https://caniuse.com/woff2&quot; rel=&quot;noopener&quot;&gt;excellent browser support&lt;/a&gt;, using older formats such as WOFF 1.0 or TrueType (TTF) will delay your LCP if the LCP candidate is a text node.&lt;/p&gt;
&lt;p&gt;When it comes to LCP and JavaScript, you&#39;ll want to ensure that you&#39;re sending complete markup from the server in order for the &lt;a href=&quot;https://web.dev/preload-scanner/&quot;&gt;browser&#39;s preload scanner&lt;/a&gt; to work properly. If you&#39;re serving up an experience that relies entirely on JavaScript to render markup and can&#39;t send server-rendered HTML, it would be advantageous to step in where the browser preload scanner can&#39;t and preload resources that would only otherwise be discoverable when the JavaScript finishes loading and executing.&lt;/p&gt;
&lt;h3 id=&quot;cumulative-layout-shift-cls&quot;&gt;Cumulative Layout Shift (CLS) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-critical-assets/#cumulative-layout-shift-cls&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/cls/&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt; is an especially important metric where web fonts are concerned, and CLS has significant interplay with web fonts that use the &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; CSS property&lt;/a&gt; to manage how fonts are loaded. To minimize web font-related layout shifts, consider the following strategies:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Preload fonts while using the default &lt;code&gt;block&lt;/code&gt; value for &lt;code&gt;font-display&lt;/code&gt;.&lt;/strong&gt; This is a delicate balance. Blocking the display of fonts without a fallback can be considered a user experience problem. On one hand, loading fonts with &lt;code&gt;font-display: block;&lt;/code&gt; eliminates web font-related layout shifts. On the other hand, you still want to get those web fonts loaded as soon as possible if they&#39;re crucial to the user experience. Combining a preload with &lt;code&gt;font-display: block;&lt;/code&gt; may be an acceptable compromise.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Preload fonts while using the &lt;code&gt;fallback&lt;/code&gt; value for &lt;code&gt;font-display&lt;/code&gt;.&lt;/strong&gt; &lt;code&gt;fallback&lt;/code&gt; is a compromise between &lt;code&gt;swap&lt;/code&gt; and &lt;code&gt;block&lt;/code&gt;, in that it has an extremely short blocking period.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use the &lt;code&gt;optional&lt;/code&gt; value for &lt;code&gt;font-display&lt;/code&gt; without a preload.&lt;/strong&gt; If a web font isn&#39;t crucial to the user experience, but it is still used to render a significant amount of page text, consider using the &lt;code&gt;optional&lt;/code&gt; value. In adverse conditions, &lt;code&gt;optional&lt;/code&gt; will display page text in a fallback font while it loads the font in the background for the next navigation. The net result in these conditions is improved CLS, as system fonts will render immediately, while subsequent page loads will load the font immediately without layout shifts.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;CLS is a difficult metric to optimize for when it comes to web fonts. As always, experiment in the &lt;a href=&quot;https://web.dev/lab-and-field-data-differences/#lab-data&quot;&gt;lab&lt;/a&gt;, but trust your &lt;a href=&quot;https://web.dev/lab-and-field-data-differences/#field-data&quot;&gt;field data&lt;/a&gt; to determine if your font loading strategies are improving CLS or making it worse.&lt;/p&gt;
&lt;h3 id=&quot;responsiveness-metrics&quot;&gt;Responsiveness metrics &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-critical-assets/#responsiveness-metrics&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&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&lt;/a&gt; are two metrics that gauge responsiveness to user input. Since the lion&#39;s share of interactivity on the web is driven by JavaScript, preloading JavaScript that powers important interactions may help to keep your FID and INP metrics as low as they can possibly be. However, be aware that FID is a load responsiveness metric and INP observes interactions throughout the entire page lifecycle—including during startup. Preloading too much JavaScript during startup can carry unintended negative consequences if too many resources are contending for bandwidth.&lt;/p&gt;
&lt;p&gt;You&#39;ll also want to be careful about how you go about &lt;a href=&quot;https://web.dev/reduce-javascript-payloads-with-code-splitting/&quot;&gt;code splitting&lt;/a&gt;. Code splitting is an excellent optimization for reducing the amount of JavaScript loaded during startup, but interactions can be delayed if they rely on JavaScript loaded right at the start of the interaction. To compensate for this, you&#39;ll need to examine the user&#39;s intent, and inject a preload for the necessary chunk(s) of JavaScript before the interaction takes place. One example could be preloading JavaScript required for validating a form&#39;s contents when any of the fields in the form are focused.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/preload-critical-assets/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To improve page speed, preload important resources that are discovered late by the browser. Preloading everything would be counterproductive so use &lt;code&gt;preload&lt;/code&gt; sparingly and &lt;a href=&quot;https://web.dev/fast#measure-performance-in-the-field&quot;&gt;measure the impact in the real-world&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Houssein Djirdeh</name>
    </author><author>
      <name>Milica Mihajlija</name>
    </author><author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Choose the right image format</title>
    <link href="https://web.dev/choose-the-right-image-format/"/>
    <updated>2018-08-30T00:00:00Z</updated>
    <id>https://web.dev/choose-the-right-image-format/</id>
    <content type="html" mode="escaped">&lt;p&gt;The very first question you should ask yourself is whether an image is,
in fact, required to achieve the effect you are after.
Good design is simple and will also always yield the best performance.
If you can eliminate an image resource,
which often requires a large number of bytes relative to HTML, CSS, JavaScript and other assets on the page,
then that is always the best optimization strategy.
That said, a well-placed image can also communicate more information than a thousand words,
so it is up to you to find that balance.&lt;/p&gt;
&lt;p&gt;Next, you should consider if there is an alternative technology that could deliver the desired results,
but in a more efficient manner:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CSS effects&lt;/strong&gt; (such as shadows or gradients) and CSS animations
can be used to produce resolution-independent assets that always look sharp at every resolution and zoom level,
often at a fraction of the bytes required by an image file.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Web fonts&lt;/strong&gt; enable use of beautiful typefaces
while preserving the ability to select, search,
and resize text—a significant improvement in usability.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you ever find yourself encoding text in an image asset, stop and reconsider.
Great typography is critical to good design, branding, and readability,
but text-in-images delivers a poor user experience:
the text is not selectable, not searchable, not zoomable,
not accessible, and not friendly for high-DPI devices.
The use of web fonts requires its &lt;a href=&quot;https://www.igvita.com/2014/01/31/optimizing-web-font-rendering-performance/&quot; rel=&quot;noopener&quot;&gt;own set of optimizations&lt;/a&gt;,
but it addresses all of these concerns and is always a better choice for displaying text.&lt;/p&gt;
&lt;h2 id=&quot;choose-the-right-image-format&quot;&gt;Choose the right image format &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/choose-the-right-image-format/#choose-the-right-image-format&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you are sure an image is the correct option, you should carefully select the right kind of image for the job.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Zoomed-in vector and raster images&quot; decoding=&quot;async&quot; height=&quot;313&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 585px) 585px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/dJuB2DQcbhtwD5VdPVlR.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/dJuB2DQcbhtwD5VdPVlR.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/dJuB2DQcbhtwD5VdPVlR.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/dJuB2DQcbhtwD5VdPVlR.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/dJuB2DQcbhtwD5VdPVlR.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/dJuB2DQcbhtwD5VdPVlR.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/dJuB2DQcbhtwD5VdPVlR.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/dJuB2DQcbhtwD5VdPVlR.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/dJuB2DQcbhtwD5VdPVlR.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/dJuB2DQcbhtwD5VdPVlR.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/dJuB2DQcbhtwD5VdPVlR.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/dJuB2DQcbhtwD5VdPVlR.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/dJuB2DQcbhtwD5VdPVlR.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/dJuB2DQcbhtwD5VdPVlR.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/dJuB2DQcbhtwD5VdPVlR.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/dJuB2DQcbhtwD5VdPVlR.png?auto=format&amp;w=1170 1170w&quot; width=&quot;585&quot; /&gt;
  &lt;figcaption&gt;Zoomed-in vector image (L) raster image (R)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Vector_graphics&quot; rel=&quot;noopener&quot;&gt;Vector graphics&lt;/a&gt;
use lines, points, and polygons to represent an image.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Raster_graphics&quot; rel=&quot;noopener&quot;&gt;Raster graphics&lt;/a&gt;
represent an image by encoding the individual values of each pixel within a rectangular grid.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each format has its own set of pros and cons.
Vector formats are ideally suited for images that consist of simple geometric shapes such as logos, text, or icons.
They deliver sharp results at every resolution and zoom setting,
which makes them an ideal format for high-resolution screens and assets that need to be displayed at varying sizes.&lt;/p&gt;
&lt;p&gt;However, vector formats fall short when the scene is complicated (for example, a photo):
the amount of SVG markup to describe all the shapes can be prohibitively high
and the output may still not look &amp;quot;photorealistic&amp;quot;.
When that&#39;s the case, that&#39;s when you should be using a raster image format
such as PNG, JPEG, WebP, or AVIF.&lt;/p&gt;
&lt;p&gt;Raster images do not have the same nice properties of being resolution or zoom independent
—when you scale up a raster image you&#39;ll see jagged and blurry graphics.
As a result, you may need to save multiple versions of a raster image at various resolutions
to deliver the optimal experience to your users.&lt;/p&gt;
&lt;h2 id=&quot;implications-of-high-resolution-screens&quot;&gt;Implications of high-resolution screens &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/choose-the-right-image-format/#implications-of-high-resolution-screens&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are two different kinds of pixels: CSS pixels and device pixels.
A single CSS pixel may correspond directly to a single device pixel, or may be backed by multiple device pixels.
What&#39;s the point? Well, the more device pixels there are, the finer the detail of the displayed content on the screen.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Three images showing the difference between CSS pixels and device pixels.&quot; decoding=&quot;async&quot; height=&quot;205&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 470px) 470px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/oQV7qJ9fUMkYsKlUMrL4.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/oQV7qJ9fUMkYsKlUMrL4.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/oQV7qJ9fUMkYsKlUMrL4.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/oQV7qJ9fUMkYsKlUMrL4.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/oQV7qJ9fUMkYsKlUMrL4.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/oQV7qJ9fUMkYsKlUMrL4.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/oQV7qJ9fUMkYsKlUMrL4.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/oQV7qJ9fUMkYsKlUMrL4.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/oQV7qJ9fUMkYsKlUMrL4.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/oQV7qJ9fUMkYsKlUMrL4.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/oQV7qJ9fUMkYsKlUMrL4.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/oQV7qJ9fUMkYsKlUMrL4.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/oQV7qJ9fUMkYsKlUMrL4.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/oQV7qJ9fUMkYsKlUMrL4.png?auto=format&amp;w=940 940w&quot; width=&quot;470&quot; /&gt;
  &lt;figcaption&gt;The difference between CSS pixels and device pixels.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;High DPI (HiDPI) screens produce beautiful results, but there is one obvious tradeoff:
image assets require more detail to take advantage of the higher device pixel counts.
The good news is, vector images are ideally suited for this task,
as they can be rendered at any resolution with sharp results—
you might incur a higher processing cost to render the finer detail,
but the underlying asset is the same and is resolution independent.&lt;/p&gt;
&lt;p&gt;On the other hand, raster images pose a much larger challenge because they encode image data on a per-pixel basis.
Hence, the larger the number of pixels, the larger the filesize of a raster image.
As an example, let&#39;s consider the difference between a photo asset displayed at 100x100 (CSS) pixels:&lt;/p&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
&lt;table&gt;
&lt;thead&gt;
  &lt;tr&gt;
    &lt;th&gt;Screen resolution&lt;/th&gt;
    &lt;th&gt;Total pixels&lt;/th&gt;
    &lt;th&gt;Uncompressed filesize (4 bytes per pixel)&lt;/th&gt;
  &lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;td data-th=&quot;resolution&quot;&gt;1x&lt;/td&gt;
  &lt;td data-th=&quot;total pixels&quot;&gt;100 x 100 = 10,000&lt;/td&gt;
  &lt;td data-th=&quot;filesize&quot;&gt;40,000 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td data-th=&quot;resolution&quot;&gt;2x&lt;/td&gt;
  &lt;td data-th=&quot;total pixels&quot;&gt;100 x 100 x 4 = 40,000&lt;/td&gt;
  &lt;td data-th=&quot;filesize&quot;&gt;160,000 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td data-th=&quot;resolution&quot;&gt;3x&lt;/td&gt;
  &lt;td data-th=&quot;total pixels&quot;&gt;100 x 100 x 9 = 90,000&lt;/td&gt;
  &lt;td data-th=&quot;filesize&quot;&gt;360,000 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;When we double the resolution of the physical screen,
the total number of pixels increases by a factor of four:
double the number of horizontal pixels, times double the number of vertical pixels.
Hence, a &amp;quot;2x&amp;quot; screen not just doubles, but quadruples the number of required pixels!&lt;/p&gt;
&lt;p&gt;So, what does this mean in practice?
High-resolution screens enable you to deliver beautiful images, which can be a great product feature.
However, high-resolution screens also require high-resolution images, therefore:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prefer vector images whenever possible as they are resolution-independent and always deliver sharp results.&lt;/li&gt;
&lt;li&gt;If a raster image is required, serve &lt;a href=&quot;https://web.dev/serve-responsive-images/&quot;&gt;responsive images&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;features-of-different-raster-image-formats&quot;&gt;Features of different raster image formats &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/choose-the-right-image-format/#features-of-different-raster-image-formats&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In addition to different lossy and lossless compression algorithms,
different image formats support different features such as animation and transparency (alpha) channels.
As a result, the choice of the &amp;quot;right format&amp;quot; for a particular image is a combination of desired visual results and functional requirements.&lt;/p&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
&lt;table&gt;
&lt;thead&gt;
  &lt;tr&gt;
    &lt;th&gt;Format&lt;/th&gt;
    &lt;th&gt;Transparency&lt;/th&gt;
    &lt;th&gt;Animation&lt;/th&gt;
    &lt;th&gt;Browser&lt;/th&gt;
  &lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;td data-th=&quot;format&quot;&gt;&lt;a rel=&quot;noopener&quot; href=&quot;http://en.wikipedia.org/wiki/Portable_Network_Graphics&quot;&gt;PNG&lt;/a&gt;&lt;/td&gt;
  &lt;td data-th=&quot;transparency&quot;&gt;Yes&lt;/td&gt;
  &lt;td data-th=&quot;animation&quot;&gt;No&lt;/td&gt;
  &lt;td data-th=&quot;browser&quot;&gt;All&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td data-th=&quot;format&quot;&gt;&lt;a rel=&quot;noopener&quot; href=&quot;http://en.wikipedia.org/wiki/JPEG&quot;&gt;JPEG&lt;/a&gt;&lt;/td&gt;
  &lt;td data-th=&quot;transparency&quot;&gt;No&lt;/td&gt;
  &lt;td data-th=&quot;animation&quot;&gt;No&lt;/td&gt;
  &lt;td data-th=&quot;browser&quot;&gt;All&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td data-th=&quot;format&quot;&gt;&lt;a rel=&quot;noopener&quot; href=&quot;http://en.wikipedia.org/wiki/WebP&quot;&gt;WebP&lt;/a&gt;&lt;/td&gt;
  &lt;td data-th=&quot;transparency&quot;&gt;Yes&lt;/td&gt;
  &lt;td data-th=&quot;animation&quot;&gt;Yes&lt;/td&gt;
  &lt;td data-th=&quot;browser&quot;&gt;All modern browsers. See &lt;a href=&quot;https://caniuse.com/#feat=webp&quot;&gt;Can I use?&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td data-th=&quot;format&quot;&gt;&lt;a rel=&quot;noopener&quot; href=&quot;http://en.wikipedia.org/wiki/AVIF&quot;&gt;AVIF&lt;/a&gt;&lt;/td&gt;
  &lt;td data-th=&quot;transparency&quot;&gt;Yes&lt;/td&gt;
  &lt;td data-th=&quot;animation&quot;&gt;Yes&lt;/td&gt;
  &lt;td data-th=&quot;browser&quot;&gt;No. See &lt;a href=&quot;https://caniuse.com/avif&quot;&gt;Can I use?&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;There are two universally supported raster image formats: PNG and JPEG.
In addition to these formats, modern browsers support the newer format WebP, while only some support the newer AVIF format. Both of the newer formats offer better overall compression and more features. So, which format should you use?&lt;/p&gt;
&lt;p&gt;WebP and AVIF will generally provide better compression than older formats,
and should be used where possible.
You can use WebP or AVIF images along with a JPEG or PNG image as a fallback.
See &lt;a href=&quot;https://web.dev/serve-images-webp/&quot;&gt;Use WebP images&lt;/a&gt; for more details.&lt;/p&gt;
&lt;p&gt;In terms of older image formats, consider the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Do you need animation? Use &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; elements.&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;What about GIF? GIF limits the color palette to at most 256 colors,
and creates significantly larger file sizes than &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; elements. See
&lt;a href=&quot;https://web.dev/replace-gifs-with-videos/&quot;&gt;Replace animated GIFs with video&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Do you need to preserve fine detail with highest resolution? Use PNG or lossless WebP.&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;PNG does not apply any lossy compression algorithms beyond the choice of the size of the color palette.
As a result, it will produce the highest quality image,
but at a cost of significantly higher filesize than other formats. Use judiciously.&lt;/li&gt;
&lt;li&gt;WebP has a lossless encoding mode that may be more efficient than PNG.&lt;/li&gt;
&lt;li&gt;If the image asset contains imagery composed of geometric shapes, consider converting it to a vector (SVG) format!&lt;/li&gt;
&lt;li&gt;If the image asset contains text, stop and reconsider. Text in images is not selectable, searchable, or &amp;quot;zoomable&amp;quot;.
If you need to convey a custom look (for branding or other reasons), use a web font instead.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Are you optimizing a photo, screenshot, or a similar image asset? Use JPEG, lossy WebP, or lossy AVIF.&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;JPEG uses a combination of lossy and lossless optimization to reduce filesize of the image asset. Try several JPEG quality levels to find the best quality versus filesize tradeoff for your asset.&lt;/li&gt;
&lt;li&gt;Lossy WebP or lossy AVIF may be acceptable JPEG alternatives, but be aware that WebP&#39;s lossy mode in particular discards some chroma information to achieve smaller images. This means that select colors may not be the same as an equivalent JPEG.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Finally, note that if you are using a WebView to render content in your platform-specific application,
then you have full control of the client and can use WebP exclusively!
Facebook and many others use WebP to deliver all of their images within their applications—
the savings are definitely worth it.&lt;/p&gt;
&lt;h2 id=&quot;impact-on-largest-contentful-paint-lcp&quot;&gt;Impact on Largest Contentful Paint (LCP) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/choose-the-right-image-format/#impact-on-largest-contentful-paint-lcp&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Images may be &lt;a href=&quot;https://web.dev/lcp/#what-elements-are-considered&quot;&gt;LCP candidates&lt;/a&gt;. That means the size of an image affects its &lt;a href=&quot;https://web.dev/optimize-lcp/#3-reduce-resource-load-time&quot;&gt;load time&lt;/a&gt;. When an image is an LCP candidate, efficiently encoding that image is crucial to improving LCP.&lt;/p&gt;
&lt;p&gt;You should strive to apply the advice given in this article so that the perceptual performance of a page is as fast as it can possibly be for all users. LCP is part of perceptual performance, as it measures how fast the largest (and therefore most perceivable) element on the page displays.&lt;/p&gt;
</content>
    <author>
      <name>Ilya Grigorik</name>
    </author><author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Choose the correct level of compression</title>
    <link href="https://web.dev/compress-images/"/>
    <updated>2018-08-30T00:00:00Z</updated>
    <id>https://web.dev/compress-images/</id>
    <content type="html" mode="escaped">&lt;p&gt;Images often account for most of the downloaded bytes on a web page
and also often occupy a significant amount of visual space.
As a result,
optimizing images can often yield some of the largest byte savings and performance improvements for your website:
the fewer bytes the browser has to download,
the less competition there is for the client&#39;s bandwidth
and the faster the browser can download and render useful content on the screen.&lt;/p&gt;
&lt;p&gt;Image optimization is both an art and science:
an art because there is no one definitive answer for how best to compress an individual image,
and a science because there are many well developed techniques
and algorithms that can significantly reduce the size of an image.
Finding the optimal settings for your image requires careful analysis along many dimensions:
format capabilities, content of encoded data, quality, pixel dimensions, and more.&lt;/p&gt;
&lt;h2 id=&quot;optimizing-vector-images&quot;&gt;Optimizing vector images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/compress-images/#optimizing-vector-images&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;All modern browsers support Scalable Vector Graphics (SVG),
which is an XML-based image format for two-dimensional graphics.
You can embed the SVG markup directly on the page
or as an external resource.
Most vector-based drawing software can create SVG files or you can
write them by hand directly in your favorite text editor.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token prolog&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --&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;svg&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;version&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;1.2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;baseProfile&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;tiny&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&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;Layer_1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;xmlns&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;http://www.w3.org/2000/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;&lt;span class=&quot;token namespace&quot;&gt;xmlns:&lt;/span&gt;xlink&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;http://www.w3.org/1999/xlink&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;x&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;0px&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;y&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;0px&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;viewBox&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;0 0 612 792&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;xml:&lt;/span&gt;space&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;preserve&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;g&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&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;XMLID_1_&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;g&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;circle&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fill&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;red&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;stroke&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;black&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;stroke-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;2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;stroke-miterlimit&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;10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cx&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;50&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cy&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;50&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;r&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;40&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;g&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;g&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;svg&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 above example renders the below simple circle shape with a black outline and red background
and was exported from Adobe Illustrator.&lt;/p&gt;

&lt;!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --&gt;
&lt;p&gt;&lt;svg version=&quot;1.2&quot; baseProfile=&quot;tiny&quot; id=&quot;Layer_1&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot; x=&quot;0px&quot; y=&quot;0px&quot; viewBox=&quot;0 0 612 120&quot; xml:space=&quot;preserve&quot;&gt;
&lt;g id=&quot;XMLID_1_&quot;&gt;
&lt;g&gt;
&lt;circle fill=&quot;red&quot; stroke=&quot;black&quot; stroke-width=&quot;2&quot; stroke-miterlimit=&quot;10&quot; cx=&quot;50&quot; cy=&quot;50&quot; r=&quot;40&quot;&gt;&lt;/circle&gt;
&lt;/g&gt;
&lt;/g&gt;
&lt;/svg&gt;&lt;/p&gt;
&lt;p&gt;As you can tell, it contains a lot of metadata,
such as layer information, comments, and XML namespaces that are often unnecessary to render the asset in the browser.
As a result, it is always a good idea to minify your SVG files by running through a tool like &lt;a href=&quot;https://github.com/svg/svgo&quot; rel=&quot;noopener&quot;&gt;SVGO&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Case in point, SVGO reduces the size of the above SVG file generated by Illustrator by 58%,
taking it from 470 to 199 bytes.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&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;svg&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;version&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;1.2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;baseProfile&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;tiny&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;xmlns&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;http://www.w3.org/2000/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;viewBox&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;0 0 612 792&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;circle&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fill&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;red&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;stroke&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;#000&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;stroke-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;2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;stroke-miterlimit&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;10&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cx&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;50&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cy&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;50&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;r&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;40&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;svg&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;Because SVG is an XML-based format,
you can also apply GZIP compression to reduce its transfer size—make sure your server is configured to compress SVG assets!&lt;/p&gt;
&lt;p&gt;A raster image is simply a two-dimensional grid of individual &amp;quot;pixels&amp;quot;—for example,
a 100x100 pixel image is a sequence of 10,000 pixels.
In turn, each pixel stores the &amp;quot;&lt;a href=&quot;https://en.wikipedia.org/wiki/RGBA_color_space&quot; rel=&quot;noopener&quot;&gt;RGBA&lt;/a&gt;&amp;quot; values:
(R) red channel, (G) green channel, (B) blue channel, and (A) alpha (transparency) channel.&lt;/p&gt;
&lt;p&gt;Internally, the browser allocates 256 values (shades) for each channel,
which translates to 8 bits per channel (2 ^ 8 = 256),
and 4 bytes per pixel (4 channels x 8 bits = 32 bits = 4 bytes).
As a result, if we know the dimensions of the grid we can easily calculate the filesize:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;100x100 pixel image is composed of 10,000 pixels&lt;/li&gt;
&lt;li&gt;10,000 pixels x 4 bytes = 40,000 bytes&lt;/li&gt;
&lt;li&gt;40,000 bytes / 1024 = 39 KB&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; As an aside, regardless of the image format used to transfer the data from the server to the client, when the image is decoded by the browser, each pixel always occupies 4 bytes of memory. This can be an important constraint for large images and devices which do not have a lot of available memory —for example, low-end mobile devices. &lt;/div&gt;&lt;/aside&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
&lt;table&gt;
&lt;thead&gt;
  &lt;tr&gt;
    &lt;th&gt;Dimensions&lt;/th&gt;
    &lt;th&gt;Pixels&lt;/th&gt;
    &lt;th&gt;File size&lt;/th&gt;
  &lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
  &lt;td data-th=&quot;dimensions&quot;&gt;100 x 100&lt;/td&gt;
  &lt;td data-th=&quot;pixels&quot;&gt;10,000&lt;/td&gt;
  &lt;td data-th=&quot;file size&quot;&gt;39 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td data-th=&quot;dimensions&quot;&gt;200 x 200&lt;/td&gt;
  &lt;td data-th=&quot;pixels&quot;&gt;40,000&lt;/td&gt;
  &lt;td data-th=&quot;file size&quot;&gt;156 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td data-th=&quot;dimensions&quot;&gt;300 x 300&lt;/td&gt;
  &lt;td data-th=&quot;pixels&quot;&gt;90,000&lt;/td&gt;
  &lt;td data-th=&quot;file size&quot;&gt;351 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td data-th=&quot;dimensions&quot;&gt;500 x 500&lt;/td&gt;
  &lt;td data-th=&quot;pixels&quot;&gt;250,000&lt;/td&gt;
  &lt;td data-th=&quot;file size&quot;&gt;977 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td data-th=&quot;dimensions&quot;&gt;800 x 800&lt;/td&gt;
  &lt;td data-th=&quot;pixels&quot;&gt;640,000&lt;/td&gt;
  &lt;td data-th=&quot;file size&quot;&gt;2500 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;39 KB for a 100x100 pixel image may not seem like a big deal,
but the filesize quickly explodes for larger images and makes image assets both slow and expensive to download.
This post has so far only focused on the &amp;quot;uncompressed&amp;quot; image format.
Thankfully, a lot can be done to reduce the image file size.&lt;/p&gt;
&lt;p&gt;One simple strategy is to reduce the &amp;quot;bit-depth&amp;quot; of the image from 8 bits per channel to a smaller color palette:
8 bits per channel gives us 256 values per channel and 16,777,216 (256 ^ 3) colors in total.
What if you reduce the palette to 256 colors?
Then you would only need 8 bits in total for the RGB channels and immediately save two bytes per pixel—that&#39;s 50% compression savings over the original 4 bytes per pixel format!&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Compression artifacts&quot; decoding=&quot;async&quot; height=&quot;266&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 612px) 612px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/ssek7uXzhs67joEbp0P8.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/ssek7uXzhs67joEbp0P8.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/ssek7uXzhs67joEbp0P8.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/ssek7uXzhs67joEbp0P8.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/ssek7uXzhs67joEbp0P8.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/ssek7uXzhs67joEbp0P8.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/ssek7uXzhs67joEbp0P8.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/ssek7uXzhs67joEbp0P8.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/ssek7uXzhs67joEbp0P8.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/ssek7uXzhs67joEbp0P8.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/ssek7uXzhs67joEbp0P8.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/ssek7uXzhs67joEbp0P8.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/ssek7uXzhs67joEbp0P8.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/ssek7uXzhs67joEbp0P8.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/ssek7uXzhs67joEbp0P8.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/ssek7uXzhs67joEbp0P8.png?auto=format&amp;w=1224 1224w&quot; width=&quot;612&quot; /&gt;
  &lt;figcaption&gt;Left to right (PNG): 32-bit (16M colors), 7-bit (128 colors), 5-bit (32 colors).&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Complex scenes with gradual color transitions (for example, gradients or sky)
require larger color palettes to avoid visual artifacts such as the pixelated sky in the 5-bit asset.
On the other hand, if the image only uses a few colors,
then a large palette is simply wasting precious bits!&lt;/p&gt;
&lt;p&gt;Next, once you&#39;ve optimized the data stored in individual pixels you could get more clever and look at nearby pixels as well:
turns out, many images, and especially photos, have many nearby pixels with similar colors—
for example, the sky, repeating textures, and so on.
Using this information to your advantage the compressor can apply &lt;a href=&quot;https://en.wikipedia.org/wiki/Delta_encoding&quot; rel=&quot;noopener&quot;&gt;delta encoding&lt;/a&gt;
where instead of storing the individual values for each pixel,
you can store the difference between nearby pixels:
if the adjacent pixels are the same, then the delta is &amp;quot;zero&amp;quot; and you only need to store a single bit!
But why stop there…&lt;/p&gt;
&lt;p&gt;The human eye has different level of sensitivity to different colors:
you can optimize your color encoding to account for this by reducing or increasing the palette for those colors.
&amp;quot;Nearby&amp;quot; pixels form a two-dimensional grid. This means that each pixel has multiple neighbors:
you can use this fact to further improve delta encoding.
Instead of looking at just the immediate neighbors for each pixel,
you can look at larger blocks of nearby pixels and encode different blocks with different settings.&lt;/p&gt;
&lt;p&gt;As you can tell, image optimization gets complicated quickly (or fun, depending on your perspective),
and is an active area of academic and commercial research.
Images occupy a lot of bytes and there is a lot of value in developing better image compression techniques!
If you&#39;re curious to learn more, head to the &lt;a href=&quot;https://en.wikipedia.org/wiki/Image_compression&quot; rel=&quot;noopener&quot;&gt;Wikipedia page&lt;/a&gt;,
or check out the &lt;a href=&quot;https://developers.google.com/speed/webp/docs/compression&quot; rel=&quot;noopener&quot;&gt;WebP compression techniques whitepaper&lt;/a&gt; for a hands-on example.&lt;/p&gt;
&lt;p&gt;So, once again, this is all great, but also very academic:
how does it help you to optimize images on your site?
Well, it&#39;s important to understand the shape of the problem: RGBA pixels, bit-depth, and various optimization techniques.
All of these concepts are critical to understand and keep in mind before you dive into the discussions of various raster image formats.&lt;/p&gt;
&lt;h2 id=&quot;lossless-versus-lossy-image-compression&quot;&gt;Lossless versus lossy image compression &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/compress-images/#lossless-versus-lossy-image-compression&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For certain types of data, such as source code for a page, or an executable file,
it is critical that a compressor does not alter or lose any of the original information:
a single missing or wrong bit of data could completely change the meaning of the contents of the file,
or worse, break it entirely.
For some other types of data, such as images, audio, and video,
it may be perfectly acceptable to deliver an &amp;quot;approximate&amp;quot; representation of the original data.&lt;/p&gt;
&lt;p&gt;In fact, due to how the eye works,
we can often get away with discarding some information about each pixel in order to reduce the filesize of an image—
for example, our eyes have different sensitivity to different colors,
which means that we can use fewer bits to encode some colors.
As a result, a typical image optimization pipeline consists of two high level steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Image is processed with a &lt;a href=&quot;https://en.wikipedia.org/wiki/Lossy_compression&quot; rel=&quot;noopener&quot;&gt;lossy&lt;/a&gt; filter that eliminates some pixel data.&lt;/li&gt;
&lt;li&gt;Image is processed with a &lt;a href=&quot;https://en.wikipedia.org/wiki/Lossless_compression&quot; rel=&quot;noopener&quot;&gt;lossless&lt;/a&gt; filter that compresses the pixel data.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The first step is optional,
and the exact algorithm will depend on the particular image format,
but it is important to understand that any image can undergo a lossy compression step to reduce its size.
In fact, the difference between various image formats, such as GIF, PNG, JPEG, and others,
is in the combination of the specific algorithms they use (or omit) when applying the lossy and lossless steps.&lt;/p&gt;
&lt;p&gt;So, what is the &amp;quot;optimal&amp;quot; configuration of lossy and lossless optimization?
The answer depends on the image contents and your own criteria such as the tradeoff between filesize and artifacts introduced by lossy compression:
In some cases, you may want to skip lossy optimization to communicate intricate detail in its full fidelity.
In other cases, you may be able to apply aggressive lossy optimization to reduce the filesize of the image asset.
This is where your own judgment and context need to come into play—there is no one universal setting.&lt;/p&gt;
&lt;p&gt;As a hands-on example, when using a lossy format such as JPEG,
the compressor will typically expose a customizable &amp;quot;quality&amp;quot; setting
(for example, the quality slider provided by the &amp;quot;Save for Web&amp;quot; functionality in Adobe Photoshop),
which is typically a number between 1 and 100 that controls the inner workings of the specific collection of lossy and lossless algorithms.
For best results, experiment with various quality settings for your images,
and don&#39;t be afraid to dial down the quality—the visual results are often very good and the filesize savings can be quite large.&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; Note that quality levels for different image formats are not directly comparable due to differences in algorithms used to encode the image: quality 90 JPEG will produce a very different result than a quality 90 WebP. In fact, even quality levels for the same image format may produce visibly different output based on implementation of the compressor! &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;effects-of-image-compression-on-core-web-vitals&quot;&gt;Effects of image compression on Core Web Vitals &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/compress-images/#effects-of-image-compression-on-core-web-vitals&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Because images are often candidates for &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint&lt;/a&gt;, reducing the resource load time of an image can translate into better LCP 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;When playing with compression settings on raster image formats, be sure to experiment with WebP and AVIF formats to see if you can deliver the same image in a small footprint as compared to older formats.&lt;/p&gt;
&lt;p&gt;You want to be careful not to &lt;em&gt;overcompress&lt;/em&gt; raster images, though. A good solution is to use an image optimization CDN to find the best compression settings for you, but an alternative may be to use tools like &lt;a href=&quot;https://github.com/google/butteraugli&quot; rel=&quot;noopener&quot;&gt;Butteraugli&lt;/a&gt; to estimate visual differences so that you don&#39;t encode images &lt;em&gt;too&lt;/em&gt; aggressively and lose too much quality.&lt;/p&gt;
&lt;h2 id=&quot;image-optimization-checklist&quot;&gt;Image optimization checklist &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/compress-images/#image-optimization-checklist&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Some tips and techniques to keep in mind as you work on optimizing your images:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Prefer vector formats:&lt;/strong&gt; vector images are resolution and scale independent,
which makes them a perfect fit for the multi-device and high-resolution world.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Minify and compress SVG assets:&lt;/strong&gt; XML markup produced by most drawing applications
often contains unnecessary metadata which can be removed;
Ensure that your servers are configured to apply GZIP compression for SVG assets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prefer WebP or AVIF over older raster formats&lt;/strong&gt;: &lt;a href=&quot;https://web.dev/serve-images-webp/&quot;&gt;WebP&lt;/a&gt; and &lt;a href=&quot;https://web.dev/compress-images-avif/&quot;&gt;AVIF images&lt;/a&gt; will usually be far smaller than older image formats.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pick best raster image format:&lt;/strong&gt; determine your functional requirements and &lt;a href=&quot;https://web.dev/choose-the-right-image-format/&quot;&gt;select the one that suits each particular asset&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Experiment with optimal quality settings for raster formats:&lt;/strong&gt; don&#39;t be afraid to dial down the &amp;quot;quality&amp;quot; settings,
the results are often very good and byte savings are significant.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remove unnecessary image metadata:&lt;/strong&gt; many raster images contain unnecessary metadata about the asset:
geo information, camera information, and so on.
Use appropriate tools to strip this data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Serve scaled images:&lt;/strong&gt; &lt;a href=&quot;https://web.dev/serve-images-with-correct-dimensions/&quot;&gt;resize images&lt;/a&gt; and ensure that the &amp;quot;display&amp;quot; size is as close as possible to the &amp;quot;natural&amp;quot; size of the image.
Pay close attention to large images in particular, as they account for largest overhead when resized!&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automate, automate, automate:&lt;/strong&gt; invest into automated tools and infrastructure that will ensure that all of your image assets are always optimized.&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Ilya Grigorik</name>
    </author><author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Reduce JavaScript payloads with tree shaking</title>
    <link href="https://web.dev/reduce-javascript-payloads-with-tree-shaking/"/>
    <updated>2018-06-14T00:00:00Z</updated>
    <id>https://web.dev/reduce-javascript-payloads-with-tree-shaking/</id>
    <content type="html" mode="escaped">&lt;p&gt;Today&#39;s web applications can get pretty big, especially the JavaScript part of them. As of mid-2018, HTTP Archive puts the &lt;a href=&quot;https://httparchive.org/reports/state-of-javascript#bytesJs&quot; rel=&quot;noopener&quot;&gt;median transfer size of JavaScript on mobile devices&lt;/a&gt; at approximately 350 KB. And this is just transfer size! JavaScript is often compressed when sent over the network, meaning that the &lt;em&gt;actual&lt;/em&gt; amount of JavaScript is quite a bit more after the browser decompresses it. That&#39;s important to point out, because as far as resource &lt;em&gt;processing&lt;/em&gt; is concerned, compression is irrelevant. 900 KB of decompressed JavaScript is still 900 KB to the parser and compiler, even though it may be roughly 300 KB when compressed.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A diagram illustrating the process of downloading, decompressing, parsing, compiling, and executing JavaScript.&quot; decoding=&quot;async&quot; height=&quot;106&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VytWruOnBN3nHJnSm18w.svg&quot; width=&quot;600&quot; /&gt;
  &lt;figcaption&gt;
    The process of downloading and running JavaScript. Note that even though the transfer size of the script is 300 KB compressed, it is still 900 KB worth of JavaScript that must be parsed, compiled, and executed.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;JavaScript is an expensive resource to process. Unlike images which only incur relatively trivial decode time once downloaded, JavaScript must be parsed, compiled, and then finally executed. Byte for byte, this makes JavaScript more expensive than other types of resources.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A diagram comparing the processing time of 170 KB of JavaScript versus an equivalently sized JPEG image. The JavaScript resource is far more resource-intensive byte for byte than the JPEG.&quot; decoding=&quot;async&quot; height=&quot;504&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/rEdnN5fuzBC6DOjUqgEL.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/rEdnN5fuzBC6DOjUqgEL.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/rEdnN5fuzBC6DOjUqgEL.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/rEdnN5fuzBC6DOjUqgEL.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/rEdnN5fuzBC6DOjUqgEL.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/rEdnN5fuzBC6DOjUqgEL.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/rEdnN5fuzBC6DOjUqgEL.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/rEdnN5fuzBC6DOjUqgEL.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/rEdnN5fuzBC6DOjUqgEL.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/rEdnN5fuzBC6DOjUqgEL.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/rEdnN5fuzBC6DOjUqgEL.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/rEdnN5fuzBC6DOjUqgEL.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/rEdnN5fuzBC6DOjUqgEL.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/rEdnN5fuzBC6DOjUqgEL.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/rEdnN5fuzBC6DOjUqgEL.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/rEdnN5fuzBC6DOjUqgEL.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/rEdnN5fuzBC6DOjUqgEL.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/rEdnN5fuzBC6DOjUqgEL.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    The processing cost of parsing/compiling 170 KB of JavaScript vs decode time of an equivalently sized JPEG. (&lt;a href=&quot;https://medium.com/dev-channel/the-cost-of-javascript-84009f51e99e&quot; rel=&quot;noopener&quot;&gt;source&lt;/a&gt;).
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;https://v8.dev/blog/background-compilation&quot; rel=&quot;noopener&quot;&gt;While improvements are continually being made&lt;/a&gt; to &lt;a href=&quot;https://blog.mozilla.org/javascript/2017/12/12/javascript-startup-bytecode-cache/&quot; rel=&quot;noopener&quot;&gt;improve the efficiency of JavaScript engines&lt;/a&gt;, improving JavaScript performance is—as always—a task for developers.&lt;/p&gt;
&lt;p&gt;To that end, there are techniques to improve JavaScript performance. &lt;a href=&quot;https://web.dev/reduce-javascript-payloads-with-code-splitting/&quot;&gt;Code splitting&lt;/a&gt;, is one such technique that improves performance by partitioning application JavaScript into chunks, and serving those chunks to only the routes of an application that need them.&lt;/p&gt;
&lt;p&gt;While this technique works, it doesn&#39;t address a common problem of JavaScript-heavy applications, which is the inclusion of code that&#39;s never used. Tree shaking attempts to solve this problem.&lt;/p&gt;
&lt;h2 id=&quot;what-is-tree-shaking&quot;&gt;What is tree shaking? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-javascript-payloads-with-tree-shaking/#what-is-tree-shaking&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Tree_shaking&quot; rel=&quot;noopener&quot;&gt;Tree shaking&lt;/a&gt; is a form of dead code elimination. &lt;a href=&quot;https://github.com/rollup/rollup#tree-shaking&quot; rel=&quot;noopener&quot;&gt;The term was popularized by Rollup&lt;/a&gt;, but the concept of dead code elimination has existed for some time. The concept has also found purchase in &lt;a href=&quot;https://webpack.js.org/guides/tree-shaking/&quot; rel=&quot;noopener&quot;&gt;webpack&lt;/a&gt;, which is demonstrated in this article by way of a sample app.&lt;/p&gt;
&lt;p&gt;The term &amp;quot;tree shaking&amp;quot; comes from the mental model of your application and its dependencies as a tree-like structure. Each node in the tree represents a dependency that provides distinct functionality for your app. In modern apps, these dependencies are brought in via &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/import&quot; rel=&quot;noopener&quot;&gt;static &lt;code&gt;import&lt;/code&gt; statements&lt;/a&gt; like so:&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;// Import all the array utilities!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; arrayUtils &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;array-utils&quot;&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; Note: If you&#39;re not sure what ES6 modules are, &lt;a href=&quot;https://ponyfoo.com/articles/es6-modules-in-depth&quot;&gt;this explainer at Pony Foo&lt;/a&gt; is worth a look. This guide assumes you have working knowledge of how ES6 modules work, so if you don&#39;t know anything about them, give that article a read! &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;When an app is young—a sapling, if you will—it may have few dependencies. It&#39;s also using most—if not all—the dependencies you add. As your app matures, however, more dependencies can get added. To compound matters, older dependencies fall out of use, but may not get pruned from your codebase. The end result is that an app ends up shipping with a lot of &lt;a href=&quot;https://web.dev/unused-javascript/&quot;&gt;unused JavaScript&lt;/a&gt;. Tree shaking addresses this by taking advantage of how static &lt;code&gt;import&lt;/code&gt; statements pull in specific parts of ES6 modules:&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;// Import only some of the utilities!&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; unique&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; implode&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; explode &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;&quot;array-utils&quot;&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 difference between this &lt;code&gt;import&lt;/code&gt; example and the previous one is that rather than importing &lt;em&gt;everything&lt;/em&gt; from the &lt;code&gt;&amp;quot;array-utils&amp;quot;&lt;/code&gt; module—which could be a lot of code)—this example imports only specific parts of it. In dev builds, this doesn&#39;t change anything, as the entire module gets imported regardless. In production builds, webpack can be configured to &amp;quot;shake&amp;quot; off &lt;a href=&quot;https://developer.mozilla.org/docs/web/javascript/reference/statements/export&quot; rel=&quot;noopener&quot;&gt;exports&lt;/a&gt; from ES6 modules that weren&#39;t explicitly imported, making those production builds smaller. In this guide, you&#39;ll learn how to do just that!&lt;/p&gt;
&lt;h2 id=&quot;finding-opportunities-to-shake-a-tree&quot;&gt;Finding opportunities to shake a tree &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-javascript-payloads-with-tree-shaking/#finding-opportunities-to-shake-a-tree&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For illustrative purposes, &lt;a href=&quot;https://github.com/malchata/webpack-tree-shaking-example&quot; rel=&quot;noopener&quot;&gt;a sample one-page app&lt;/a&gt; is available that demonstrates how tree shaking works. You can clone it and follow along if you like, but we&#39;ll cover every step of the way together in this guide, so cloning isn&#39;t necessary (unless hands-on learning is your thing).&lt;/p&gt;
&lt;p&gt;The sample app is a searchable database of guitar effect pedals. You enter a query and a list of effect pedals will appear.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of a sample one page application for searching a database of guitar effect pedals.&quot; decoding=&quot;async&quot; height=&quot;673&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/bBmp460zNYmTVeAgQhw5.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/bBmp460zNYmTVeAgQhw5.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/bBmp460zNYmTVeAgQhw5.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/bBmp460zNYmTVeAgQhw5.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/bBmp460zNYmTVeAgQhw5.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/bBmp460zNYmTVeAgQhw5.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/bBmp460zNYmTVeAgQhw5.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/bBmp460zNYmTVeAgQhw5.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/bBmp460zNYmTVeAgQhw5.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/bBmp460zNYmTVeAgQhw5.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/bBmp460zNYmTVeAgQhw5.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/bBmp460zNYmTVeAgQhw5.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/bBmp460zNYmTVeAgQhw5.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/bBmp460zNYmTVeAgQhw5.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/bBmp460zNYmTVeAgQhw5.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/bBmp460zNYmTVeAgQhw5.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/bBmp460zNYmTVeAgQhw5.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/bBmp460zNYmTVeAgQhw5.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    A screenshot of the sample app.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The behavior that drives this app is separated into vendor (i.e., &lt;a href=&quot;https://preactjs.com/&quot; rel=&quot;noopener&quot;&gt;Preact&lt;/a&gt; and &lt;a href=&quot;https://emotion.sh/&quot; rel=&quot;noopener&quot;&gt;Emotion&lt;/a&gt;) and app-specific code bundles (or &amp;quot;chunks&amp;quot;, as webpack calls them):&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of two application code bundles (or chunks) shown in the network panel of Chrome&amp;#x27;s DevTools.&quot; decoding=&quot;async&quot; height=&quot;282&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oreVh5rQcXxIDenvQxE7.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oreVh5rQcXxIDenvQxE7.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oreVh5rQcXxIDenvQxE7.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oreVh5rQcXxIDenvQxE7.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oreVh5rQcXxIDenvQxE7.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oreVh5rQcXxIDenvQxE7.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oreVh5rQcXxIDenvQxE7.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oreVh5rQcXxIDenvQxE7.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oreVh5rQcXxIDenvQxE7.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oreVh5rQcXxIDenvQxE7.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oreVh5rQcXxIDenvQxE7.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oreVh5rQcXxIDenvQxE7.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oreVh5rQcXxIDenvQxE7.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oreVh5rQcXxIDenvQxE7.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oreVh5rQcXxIDenvQxE7.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oreVh5rQcXxIDenvQxE7.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oreVh5rQcXxIDenvQxE7.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/oreVh5rQcXxIDenvQxE7.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    The app&#39;s two JavaScript bundles. These are uncompressed sizes.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The JavaScript bundles shown in the figure above are production builds, meaning they&#39;re optimized through uglification. 21.1 KB for an app-specific bundle isn&#39;t bad, but it should be noted that no tree shaking is occurring whatsoever. Let&#39;s look at the app code and see what can be done to fix that.&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 don&#39;t care for a long-winded explanation, and want to dive into code, go ahead and check out &lt;a href=&quot;https://github.com/malchata/webpack-tree-shaking-example/tree/tree-shake&quot;&gt;the &lt;code&gt;tree-shake&lt;/code&gt; branch&lt;/a&gt; in the app&#39;s GitHub repo. You can also &lt;a href=&quot;https://github.com/malchata/webpack-tree-shaking-example/compare/tree-shake&quot;&gt;diff this branch&lt;/a&gt; to see exactly what was changed to make tree shaking work! &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;In any application, finding tree shaking opportunities are going to involve looking for static &lt;code&gt;import&lt;/code&gt; statements. &lt;a href=&quot;https://github.com/malchata/webpack-tree-shaking-example/blob/master/src/components/FilterablePedalList/FilterablePedalList.js#L4&quot; rel=&quot;noopener&quot;&gt;Near the top of the main component file&lt;/a&gt;, you&#39;ll see a line like this:&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 operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; utils &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../../utils/utils&quot;&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 &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/import#description&quot; rel=&quot;noopener&quot;&gt;import ES6 modules in a variety of ways&lt;/a&gt;, but ones like this should get your attention. This specific line says &amp;quot;&lt;code&gt;import&lt;/code&gt; &lt;em&gt;everything&lt;/em&gt; from the &lt;code&gt;utils&lt;/code&gt; module, and put it in a namespace called &lt;code&gt;utils&lt;/code&gt;.&amp;quot; The big question to ask here is, &amp;quot;just how much &lt;em&gt;stuff&lt;/em&gt; is in that module?&amp;quot;&lt;/p&gt;
&lt;p&gt;If you look at &lt;a href=&quot;https://github.com/malchata/webpack-tree-shaking-example/blob/master/src/utils/utils.js&quot; rel=&quot;noopener&quot;&gt;the &lt;code&gt;utils&lt;/code&gt; module source code&lt;/a&gt;, you&#39;ll find there&#39;s about 1,300 lines of code.&lt;/p&gt;
&lt;p&gt;Do you &lt;em&gt;need&lt;/em&gt; all that stuff? Let&#39;s double check by searching &lt;a href=&quot;https://github.com/malchata/webpack-tree-shaking-example/blob/master/src/components/FilterablePedalList/FilterablePedalList.js&quot; rel=&quot;noopener&quot;&gt;the main component file&lt;/a&gt; that imports the &lt;code&gt;utils&lt;/code&gt; module to see how many instances of that namespace come up.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of a search in a text editor for &amp;#x27;utils.&amp;#x27;, returning only 3 results.&quot; decoding=&quot;async&quot; height=&quot;117&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Grk6jcj6IiMHIiJPqiEM.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Grk6jcj6IiMHIiJPqiEM.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Grk6jcj6IiMHIiJPqiEM.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Grk6jcj6IiMHIiJPqiEM.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Grk6jcj6IiMHIiJPqiEM.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Grk6jcj6IiMHIiJPqiEM.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Grk6jcj6IiMHIiJPqiEM.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Grk6jcj6IiMHIiJPqiEM.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Grk6jcj6IiMHIiJPqiEM.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Grk6jcj6IiMHIiJPqiEM.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Grk6jcj6IiMHIiJPqiEM.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Grk6jcj6IiMHIiJPqiEM.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Grk6jcj6IiMHIiJPqiEM.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Grk6jcj6IiMHIiJPqiEM.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Grk6jcj6IiMHIiJPqiEM.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Grk6jcj6IiMHIiJPqiEM.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Grk6jcj6IiMHIiJPqiEM.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Grk6jcj6IiMHIiJPqiEM.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    The &lt;code&gt;utils&lt;/code&gt; namespace we&#39;ve imported tons of modules from is only invoked three times within the main component file.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;As it turns out, the &lt;code&gt;utils&lt;/code&gt; namespace appears in only three spots in our application—but for what functions? If you take a look at the main component file again, it appears to be only one function, which is &lt;code&gt;utils.simpleSort&lt;/code&gt;, which is used to sort the search results list by a number of criteria when the sorting dropdowns are changed:&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 keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sortBy &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;model&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 comment&quot;&gt;// `simpleSort` gets used here...&lt;/span&gt;&lt;br /&gt;  json &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; utils&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;simpleSort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;json&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;model&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sortOrder&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 keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sortBy &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;type&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 comment&quot;&gt;// ..and here...&lt;/span&gt;&lt;br /&gt;  json &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; utils&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;simpleSort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;json&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sortOrder&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;// ..and here.&lt;/span&gt;&lt;br /&gt;  json &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; utils&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;simpleSort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;json&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;manufacturer&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sortOrder&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;Out of a 1,300 line file with a bunch of exports, only one of them is used. This results in shipping a lot of unused 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; Note: This project is purposefully kept simple, so it&#39;s a bit easier in this case to find out where the bloat is coming from. In large projects with many modules, however, things get more complicated. Tools such as &lt;a href=&quot;https://www.npmjs.com/package/webpack-bundle-analyzer&quot;&gt;Webpack Bundle Analyzer&lt;/a&gt; and &lt;a href=&quot;https://www.npmjs.com/package/source-map-explorer&quot;&gt;source-map-explorer&lt;/a&gt; may provide further insight. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;While this example app is admittedly a bit contrived, it doesn&#39;t change the fact that this synthetic sort of scenario resembles actual optimization opportunities you may encounter in a production web app. Now that you&#39;ve identified an opportunity for tree shaking to be useful, how is it actually done?&lt;/p&gt;
&lt;h2 id=&quot;keeping-babel-from-transpiling-es6-modules-to-commonjs-modules&quot;&gt;Keeping Babel from transpiling ES6 modules to CommonJS modules &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-javascript-payloads-with-tree-shaking/#keeping-babel-from-transpiling-es6-modules-to-commonjs-modules&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://babeljs.io/&quot; rel=&quot;noopener&quot;&gt;Babel&lt;/a&gt; is an indispensable tool, but it may make the effects of tree shaking a bit more difficult to observe. If you&#39;re using &lt;a href=&quot;https://babeljs.io/docs/en/babel-preset-env&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;@babel/preset-env&lt;/code&gt;&lt;/a&gt;, Babel &lt;em&gt;may&lt;/em&gt; transform ES6 modules into more widely compatible CommonJS modules—that is, modules you &lt;code&gt;require&lt;/code&gt; instead of &lt;code&gt;import&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Because tree shaking is more difficult to do for CommonJS modules, webpack won&#39;t know what to prune from bundles if you decide to use them. The solution is to configure &lt;code&gt;@babel/preset-env&lt;/code&gt; to explicitly leave ES6 modules alone. Wherever you configure Babel—be it in &lt;code&gt;babel.config.js&lt;/code&gt; or &lt;code&gt;package.json&lt;/code&gt;—this involves adding a little something extra:&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;// babel.config.js&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&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;presets&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 punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token string&quot;&gt;&quot;@babel/preset-env&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 literal-property property&quot;&gt;modules&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&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;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;Specifying &lt;code&gt;modules: false&lt;/code&gt; in your &lt;code&gt;@babel/preset-env&lt;/code&gt; config gets Babel to behave as desired, which allows webpack to analyze your dependency tree and shake off unused dependencies.&lt;/p&gt;
&lt;h2 id=&quot;keeping-side-effects-in-mind&quot;&gt;Keeping side effects in mind &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-javascript-payloads-with-tree-shaking/#keeping-side-effects-in-mind&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Another aspect to consider when shaking dependencies from your app is whether
your project&#39;s modules have side effects. An example of a side effect is when a
function modifies something outside of its own scope, which is a &lt;em&gt;side effect&lt;/em&gt;
of its execution:&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; fruits &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;&quot;apple&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;orange&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pear&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;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;fruits&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 comment&quot;&gt;// (3) [&quot;apple&quot;, &quot;orange&quot;, &quot;pear&quot;]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;addFruit&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;fruit&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;  fruits&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;fruit&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 function&quot;&gt;addFruit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;kiwi&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;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;fruits&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 comment&quot;&gt;// (4) [&quot;apple&quot;, &quot;orange&quot;, &quot;pear&quot;, &quot;kiwi&quot;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In this example, &lt;code&gt;addFruit&lt;/code&gt; produces a side effect when it modifies the &lt;code&gt;fruits&lt;/code&gt; array, which is outside its scope.&lt;/p&gt;
&lt;p&gt;Side effects also apply to ES6 modules, and that matters in the context of tree shaking. Modules that take predictable inputs and produce equally predictable outputs without modifying anything outside of their own scope are dependencies that can be safely dropped if we&#39;re not using them. They&#39;re self-contained, &lt;em&gt;modular&lt;/em&gt; pieces of code. Hence, &amp;quot;modules&amp;quot;.&lt;/p&gt;
&lt;p&gt;Where webpack is concerned, a hint can be used to specify that a package and its dependencies are free of side effects by specifying &lt;code&gt;&amp;quot;sideEffects&amp;quot;: false&lt;/code&gt; in a project&#39;s &lt;code&gt;package.json&lt;/code&gt; file:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;webpack-tree-shaking-example&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;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1.0.0&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;&quot;sideEffects&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&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;Alternatively, you can tell webpack which specific files are not side effect-free:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;webpack-tree-shaking-example&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;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1.0.0&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;&quot;sideEffects&quot;&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;&quot;./src/utils/utils.js&quot;&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In the latter example, any file that isn&#39;t specified will be assumed to be free of side effects. If you don&#39;t want to add this to your &lt;code&gt;package.json&lt;/code&gt; file, &lt;a href=&quot;https://github.com/webpack/webpack/issues/6065#issuecomment-351060570&quot; rel=&quot;noopener&quot;&gt;you can also specify this flag in your webpack config via &lt;code&gt;module.rules&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;importing-only-whats-needed&quot;&gt;Importing only what&#39;s needed &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-javascript-payloads-with-tree-shaking/#importing-only-whats-needed&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After instructing Babel to leave ES6 modules alone, a slight adjustment to our &lt;code&gt;import&lt;/code&gt; syntax is required to bring in only the functions needed from the &lt;code&gt;utils&lt;/code&gt; module. In this guide&#39;s example, all that&#39;s needed is the &lt;code&gt;simpleSort&lt;/code&gt; function:&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; simpleSort &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;&quot;../../utils/utils&quot;&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;Because only &lt;code&gt;simpleSort&lt;/code&gt; is being imported instead of the entire &lt;code&gt;utils&lt;/code&gt; module, every instance of &lt;code&gt;utils.simpleSort&lt;/code&gt; will need to changed to &lt;code&gt;simpleSort&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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sortBy &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;model&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;  json &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;simpleSort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;json&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;model&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sortOrder&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 keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sortBy &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;type&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;  json &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;simpleSort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;json&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sortOrder&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;  json &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;simpleSort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;json&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;manufacturer&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sortOrder&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;This should be all that&#39;s needed for tree shaking to work in this example. This is the webpack output before shaking the dependency tree:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;                 Asset      Size  Chunks             Chunk Names&lt;br /&gt;js/vendors.16262743.js  &lt;span class=&quot;token number&quot;&gt;37.1&lt;/span&gt; KiB       &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;emitted&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;  vendors&lt;br /&gt;   js/main.797ebb8b.js  &lt;span class=&quot;token number&quot;&gt;20.8&lt;/span&gt; KiB       &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;emitted&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;  main&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This is the output &lt;em&gt;after&lt;/em&gt; tree shaking is successful:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;                 Asset      Size  Chunks             Chunk Names&lt;br /&gt;js/vendors.45ce9b64.js  &lt;span class=&quot;token number&quot;&gt;36.9&lt;/span&gt; KiB       &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;emitted&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;  vendors&lt;br /&gt;   js/main.559652be.js  &lt;span class=&quot;token number&quot;&gt;8.46&lt;/span&gt; KiB       &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;emitted&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;  main&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;While both bundles shrank, it&#39;s really the &lt;code&gt;main&lt;/code&gt; bundle that benefits most. By shaking off the unused parts of the &lt;code&gt;utils&lt;/code&gt; module, the &lt;code&gt;main&lt;/code&gt; bundle shrinks by about 60%. This not only lowers the amount of time the script takes to the download, but processing time as well.&lt;/p&gt;
&lt;h2 id=&quot;go-shake-some-trees&quot;&gt;Go shake some trees! &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-javascript-payloads-with-tree-shaking/#go-shake-some-trees&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Whatever mileage you get out of tree shaking depends on your app and its dependencies and architecture. Try it! If you know for a fact you haven&#39;t set up your module bundler to perform this optimization, there&#39;s no harm trying and seeing how it benefits your application.&lt;/p&gt;
&lt;p&gt;You may realize a significant performance gain from tree shaking, or not much at all. But by configuring your build system to take advantage of this optimization in production builds and selectively importing only what your application needs, you&#39;ll be proactively keeping your application bundles as small as possible.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Special thanks to Kristofer Baxter, &lt;a href=&quot;https://web.dev/authors/developit/&quot;&gt;Jason Miller&lt;/a&gt;, &lt;a href=&quot;https://web.dev/authors/addyosmani/&quot;&gt;Addy Osmani&lt;/a&gt;, &lt;a href=&quot;https://web.dev/authors/jeffposnick/&quot;&gt;Jeff Posnick&lt;/a&gt;, Sam Saccone, and &lt;a href=&quot;https://web.dev/authors/philipwalton/&quot;&gt;Philip Walton&lt;/a&gt; for their valuable feedback, which significantly improved the quality of this article.&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Delivering Fast and Light Applications with Save-Data</title>
    <link href="https://web.dev/optimizing-content-efficiency-save-data/"/>
    <updated>2016-02-18T00:00:00Z</updated>
    <id>https://web.dev/optimizing-content-efficiency-save-data/</id>
    <content type="html" mode="escaped">&lt;p&gt;&lt;strong&gt;The &lt;a href=&quot;https://httpwg.github.io/http-extensions/client-hints.html#the-save-data-hint&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Save-Data&lt;/code&gt; client hint request
header&lt;/a&gt;
available in Chrome, Opera, and Yandex browsers lets developers deliver lighter,
faster applications to users who opt-in to data saving mode in their browser.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-need-for-lightweight-pages&quot;&gt;The need for lightweight pages &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimizing-content-efficiency-save-data/#the-need-for-lightweight-pages&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;img alt=&quot;Weblight stats&quot; decoding=&quot;async&quot; height=&quot;423&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 600px) 600px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2dptWWpJQ5nE6Wf276UO.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2dptWWpJQ5nE6Wf276UO.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2dptWWpJQ5nE6Wf276UO.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2dptWWpJQ5nE6Wf276UO.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2dptWWpJQ5nE6Wf276UO.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2dptWWpJQ5nE6Wf276UO.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2dptWWpJQ5nE6Wf276UO.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2dptWWpJQ5nE6Wf276UO.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2dptWWpJQ5nE6Wf276UO.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2dptWWpJQ5nE6Wf276UO.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2dptWWpJQ5nE6Wf276UO.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2dptWWpJQ5nE6Wf276UO.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2dptWWpJQ5nE6Wf276UO.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2dptWWpJQ5nE6Wf276UO.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2dptWWpJQ5nE6Wf276UO.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/2dptWWpJQ5nE6Wf276UO.png?auto=format&amp;w=1200 1200w&quot; width=&quot;600&quot; /&gt;
&lt;p&gt;Everyone agrees that faster and lighter web pages provide a more satisfying user
experience, allow better content comprehension and retention, and deliver
increased conversions and revenue. &lt;a href=&quot;https://support.google.com/webmasters/answer/6211428&quot; rel=&quot;noopener&quot;&gt;Google
research&lt;/a&gt; has shown that
&amp;quot;…optimized pages load four times faster than the original page and use 80%
fewer bytes. Because these pages load so much faster, we also saw a 50% increase
in traffic to these pages.&amp;quot;&lt;/p&gt;
&lt;p&gt;And, although the number of 2G connections is &lt;a href=&quot;http://www.gsmamobileeconomy.com/GSMA_Global_Mobile_Economy_Report_2015.pdf&quot; rel=&quot;noopener&quot;&gt;finally on the
decline&lt;/a&gt;,
2G was &lt;a href=&quot;http://www.gsmamobileeconomy.com/GSMA_Global_Mobile_Economy_Report_2015.pdf&quot; rel=&quot;noopener&quot;&gt;still the dominant network
technology&lt;/a&gt;
in 2015. The penetration and availability of 3G and 4G networks is growing
rapidly, but the associated ownership costs and network constraints are still a
significant factor for hundreds of millions of users.&lt;/p&gt;
&lt;p&gt;These are strong arguments for page optimization.&lt;/p&gt;
&lt;p&gt;There are alternative methods for improving site speed without direct developer
involvement, such as proxy browsers and transcoding services. Although such
services are quite popular, they come with substantial drawbacks — simple
(and sometimes unacceptable) image and text compression, inability to process
secure (HTTPS) pages, only optimizing pages visited via a search result, and
more. The very popularity of these services is itself an indicator that web
developers are not properly addressing the high user demand for fast and light
applications and pages. But reaching that goal is a complex and sometimes
difficult path.&lt;/p&gt;
&lt;h2 id=&quot;the-save-data-request-header&quot;&gt;The &lt;code&gt;Save-Data&lt;/code&gt; request header &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimizing-content-efficiency-save-data/#the-save-data-request-header&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One fairly straightforward technique is to let the browser help, using the
&lt;code&gt;Save-Data&lt;/code&gt; request header. By identifying this header, a web page can customize
and deliver an optimized user experience to cost- and performance-constrained
users.&lt;/p&gt;
&lt;p&gt;Supported browsers (below) allow the user to enable a *data saving- mode that
gives the browser permission to apply a set of optimizations to reduce the
amount of data required to render the page. When this feature is exposed, or
advertised, the browser may request lower resolution images, defer loading of
some resources, or route requests through a service that applies other
content-specific optimizations such as image and text resource compression.&lt;/p&gt;
&lt;h2 id=&quot;browser-support&quot;&gt;Browser support &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimizing-content-efficiency-save-data/#browser-support&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Chrome 49+&lt;/strong&gt; advertises &lt;code&gt;Save-Data&lt;/code&gt; &lt;a href=&quot;https://support.google.com/chrome/answer/2392284&quot; rel=&quot;noopener&quot;&gt;when the user
enables&lt;/a&gt; the &amp;quot;Data Saver&amp;quot;
option on mobile, or the &amp;quot;Data Saver&amp;quot; extension on desktop browsers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Opera 35+&lt;/strong&gt; advertises &lt;code&gt;Save-Data&lt;/code&gt; when the user enables &amp;quot;&lt;a href=&quot;http://www.opera.com/computer/features/fast-browser&quot; rel=&quot;noopener&quot;&gt;Opera
Turbo&lt;/a&gt;&amp;quot; mode on desktop,
or the &amp;quot;&lt;a href=&quot;http://www.opera.com/help/mobile/android#turbo&quot; rel=&quot;noopener&quot;&gt;Data savings&lt;/a&gt;&amp;quot; option
on Android browsers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Yandex 16.2+&lt;/strong&gt; advertises &lt;code&gt;Save-Data&lt;/code&gt; when &lt;a href=&quot;https://yandex.com/support/newbrowser/search-and-browse/turbo.xml&quot; rel=&quot;noopener&quot;&gt;Turbo
mode&lt;/a&gt; is
enabled on desktop or &lt;a href=&quot;https://yandex.com/support/browser-mobile-android-phone/navigation/turbo-mode.xml&quot; rel=&quot;noopener&quot;&gt;mobile
browsers&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;detecting-the-save-data-setting&quot;&gt;Detecting the &lt;code&gt;Save-Data&lt;/code&gt; setting &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimizing-content-efficiency-save-data/#detecting-the-save-data-setting&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To determine when to deliver the &amp;quot;light&amp;quot; experience to your users, your
application can check for the &lt;code&gt;Save-Data&lt;/code&gt; client hint request header. This
request header indicates the client&#39;s preference for reduced data usage due to
high transfer costs, slow connection speeds, or other reasons.&lt;/p&gt;
&lt;p&gt;When the user enables the data saving mode in their browser, the browser appends
the &lt;code&gt;Save-Data&lt;/code&gt; request header to all outgoing requests (both HTTP and HTTPS).
As of this writing, the browser only advertises one *&lt;em&gt;on&lt;/em&gt;- token in the header
(&lt;code&gt;Save-Data: on&lt;/code&gt;), but this may be extended in the future to indicate other user
preferences.&lt;/p&gt;
&lt;p&gt;Additionally, it&#39;s possible to detect if &lt;code&gt;Save-Data&lt;/code&gt; is turned on in JavaScript:&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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;connection&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; navigator&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;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;saveData &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;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Implement data saving operations here.&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Checking for the presence of the &lt;code&gt;connection&lt;/code&gt; object within the &lt;code&gt;navigator&lt;/code&gt;
object is vital, as it represents the Network Information API, which is only
implemented in Chrome, Chrome for Android, and Samsung Internet browsers. From
there, you only need to check if &lt;code&gt;navigator.connection.saveData&lt;/code&gt; is equal to
&lt;code&gt;true&lt;/code&gt;, and you can implement any data saving operations in that condition.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;The Save-Data header revealed in Chrome&amp;#x27;s Developer Tools pictured along with the Data Saver extension.&quot; decoding=&quot;async&quot; height=&quot;623&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fvgG3HcmdSKyWutIkkJe.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fvgG3HcmdSKyWutIkkJe.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fvgG3HcmdSKyWutIkkJe.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fvgG3HcmdSKyWutIkkJe.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fvgG3HcmdSKyWutIkkJe.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fvgG3HcmdSKyWutIkkJe.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fvgG3HcmdSKyWutIkkJe.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fvgG3HcmdSKyWutIkkJe.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fvgG3HcmdSKyWutIkkJe.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fvgG3HcmdSKyWutIkkJe.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fvgG3HcmdSKyWutIkkJe.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fvgG3HcmdSKyWutIkkJe.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fvgG3HcmdSKyWutIkkJe.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fvgG3HcmdSKyWutIkkJe.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fvgG3HcmdSKyWutIkkJe.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fvgG3HcmdSKyWutIkkJe.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fvgG3HcmdSKyWutIkkJe.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fvgG3HcmdSKyWutIkkJe.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Enabling the Data Saver extension in Chrome desktop.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If your application &lt;a href=&quot;https://web.dev/web/fundamentals/getting-started/push-notifications/step-03&quot;&gt;uses a service
worker&lt;/a&gt;, it can
inspect the request headers and apply relevant logic to optimize the experience.
Alternatively, the server can look for the advertised preferences in the
&lt;code&gt;Save-Data&lt;/code&gt; request header and return an alternate response — different
markup, smaller images and video, and so on.&lt;/p&gt;
&lt;p&gt;Tip: If you use &lt;a href=&quot;https://developers.google.com/speed/pagespeed/module/&quot; rel=&quot;noopener&quot;&gt;PageSpeed for Apache or Nginx&lt;/a&gt; to
optimize your pages, see &lt;a href=&quot;https://github.com/pagespeed/mod_pagespeed/issues/1258&quot; rel=&quot;noopener&quot;&gt;this
discussion&lt;/a&gt; to learn how
to enable &lt;code&gt;Save-Data&lt;/code&gt; savings for your users._&lt;/p&gt;
&lt;h2 id=&quot;implementation-tips-and-best-practices&quot;&gt;Implementation tips and best practices &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimizing-content-efficiency-save-data/#implementation-tips-and-best-practices&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;When using &lt;code&gt;Save-Data&lt;/code&gt;, provide some UI devices that support it and allow users
to easily toggle between experiences. For example:
&lt;ul&gt;
&lt;li&gt;Notify users that &lt;code&gt;Save-Data&lt;/code&gt; is supported and encourage them to use it.&lt;/li&gt;
&lt;li&gt;Allow users to identify and choose the mode with appropriate prompts and
intuitive on/off buttons or checkboxes.&lt;/li&gt;
&lt;li&gt;When data saving mode is selected, announce and provide an easy and obvious
way to disable it and revert back to the full experience if desired.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Remember that lightweight applications are not lesser applications. They don&#39;t
omit important functionality or data, they&#39;re just more cognizant of the
involved costs and the user experience. For example:
&lt;ul&gt;
&lt;li&gt;A photo gallery application may deliver lower resolution previews, or use a less
code-heavy carousel mechanism.&lt;/li&gt;
&lt;li&gt;A search application may return fewer results at a time, limit the number of
media-heavy results, or reduce the number of dependencies required to render
the page.&lt;/li&gt;
&lt;li&gt;A news-oriented site may show fewer stories, omit less popular categories,
or provide smaller media previews.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Provide server logic to check for the &lt;code&gt;Save-Data&lt;/code&gt; request header and consider
providing an alternate, lighter page response when it is enabled — e.g.,
reduce the number of required resources and dependencies, apply more aggressive
resource compression, etc.
&lt;ul&gt;
&lt;li&gt;If you&#39;re serving an alternate response based on the &lt;code&gt;Save-Data&lt;/code&gt; header,
remember to add it to the Vary list — &lt;code&gt;Vary: Save-Data&lt;/code&gt; — to tell
upstream caches that they should cache and serve this version only if the
&lt;code&gt;Save-Data&lt;/code&gt; request header is present. For more details, see the best practices
for
&lt;a href=&quot;https://httpwg.github.io/http-extensions/client-hints.html#interaction-with-caches&quot; rel=&quot;noopener&quot;&gt;interaction with caches&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If you use a service worker, your application can detect when the data saving
option is enabled by checking for the presence of the &lt;code&gt;Save-Data&lt;/code&gt; request
header, or by checking the value of the &lt;code&gt;navigator.connection.saveData&lt;/code&gt;
property. If enabled, consider whether you can rewrite the request to fetch
fewer bytes, or use an already fetched response.&lt;/li&gt;
&lt;li&gt;Consider augmenting &lt;code&gt;Save-Data&lt;/code&gt; with other signals, such as information about
the user&#39;s connection type and technology (see &lt;a href=&quot;http://w3c.github.io/netinfo/#examples-of-usage&quot; rel=&quot;noopener&quot;&gt;NetInfo
API&lt;/a&gt;). For example, you might
want to serve the lightweight experience to any user on a 2G connection even if
&lt;code&gt;Save-Data&lt;/code&gt; is not enabled. Conversely, just because the user is on a &amp;quot;fast&amp;quot; 4G
connection doesn&#39;t mean they aren&#39;t interested in saving data — for
example, when roaming. Additionally, you could augment the presence of
&lt;code&gt;Save-Data&lt;/code&gt; with the &lt;code&gt;Device-Memory&lt;/code&gt; client hint to further adapt to users on
devices with limited memory. User device memory is also advertised in the
&lt;code&gt;navigator.deviceMemory&lt;/code&gt; client hint.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;recipes&quot;&gt;Recipes &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimizing-content-efficiency-save-data/#recipes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What you can achieve via &lt;code&gt;Save-Data&lt;/code&gt; is limited only to what you can come up
with. To give you an idea of what&#39;s possible, let&#39;s run through a couple of use
cases. You may come up with other use cases of your own as you read this, so
feel free to experiment and see what&#39;s possible!&lt;/p&gt;
&lt;h3 id=&quot;checking-for-save-data-in-server-side-code&quot;&gt;Checking for &lt;code&gt;Save-Data&lt;/code&gt; in server side code &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimizing-content-efficiency-save-data/#checking-for-save-data-in-server-side-code&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;While the &lt;code&gt;Save-Data&lt;/code&gt; state is something you &lt;em&gt;can&lt;/em&gt; detect in JavaScript via the
&lt;code&gt;navigator.connection.saveData&lt;/code&gt; property, detecting it on the server side is
sometimes preferable. JavaScript &lt;em&gt;can&lt;/em&gt; fail to execute in some cases. Plus,
server side detection is the only way to modify markup &lt;em&gt;before&lt;/em&gt; it&#39;s sent to the
client, which is involved in some of &lt;code&gt;Save-Data&lt;/code&gt;s most beneficial use cases.&lt;/p&gt;
&lt;p&gt;The specific syntax for detecting the &lt;code&gt;Save-Data&lt;/code&gt; header in server side code
depends on the language used, but the basic idea should be the same for any
application back end. In PHP, for example, request headers are stored in the
&lt;a href=&quot;http://php.net/manual/en/reserved.variables.server.php&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;$_SERVER&lt;/code&gt; superglobal
array&lt;/a&gt; at indexes
starting with &lt;code&gt;HTTP_&lt;/code&gt;. This means you can detect the &lt;code&gt;Save-Data&lt;/code&gt; header by
checking the existence and value of the &lt;code&gt;$_SERVER[&amp;quot;HTTP_SAVE_DATA&amp;quot;]&lt;/code&gt; variable
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 comment&quot;&gt;// false by default.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token variable&quot;&gt;$saveData&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;false&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;// Check if the `Save-Data` header exists and is set to a value of &quot;on&quot;.&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 keyword&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$_SERVER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;HTTP_SAVE_DATA&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 operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;strtolower&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$_SERVER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;HTTP_SAVE_DATA&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 operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;on&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 comment&quot;&gt;// `Save-Data` detected!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token variable&quot;&gt;$saveData&lt;/span&gt; &lt;span class=&quot;token operator&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;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;If you place this check before any markup is sent to the client, the &lt;code&gt;$saveData&lt;/code&gt;
variable will contain the &lt;code&gt;Save-Data&lt;/code&gt; state, and will be available anywhere for
use on the page. With this mechanism illustrated, let&#39;s look a few examples of
how we can use it to limit how much data we send to the user.&lt;/p&gt;
&lt;h3 id=&quot;serve-low-resolution-images-for-high-resolution-screens&quot;&gt;Serve low resolution images for high resolution screens &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimizing-content-efficiency-save-data/#serve-low-resolution-images-for-high-resolution-screens&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A common use case for images on the web involves serving images in sets of two:
One image for &amp;quot;standard&amp;quot; screens (1x), and another image that&#39;s twice as large
(2x) for high resolution screens (e.g., &lt;a href=&quot;https://en.wikipedia.org/wiki/Retina_Display&quot; rel=&quot;noopener&quot;&gt;Retina
Display&lt;/a&gt;). This class of high
resolution screens is not necessarily limited to high end devices, and is
becoming increasingly common. In cases where a lighter application experience is
preferred, it might be prudent to send lower resolution (1x) images to these
screens, rather than larger (2x) variants. To achieve this when the &lt;code&gt;Save-Data&lt;/code&gt;
header is present, we simply modify the markup we send to the client:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;if ($saveData === true) {&lt;br /&gt;  // Send a low-resolution version of the image for clients specifying `Save-Data`.&lt;br /&gt;  ?&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;butterfly-1x.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;A butterfly perched on a flower.&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 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 punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&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;// Send the usual assets for everyone else.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token delimiter important&quot;&gt;?&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;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;butterfly-1x.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;butterfly-2x.jpg 2x, butterfly-1x.jpg 1x&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 butterfly perched on a flower.&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 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 punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This use case is a perfect example of how little effort it takes to accommodate
someone who is specifically asking you to send them less data. If you don&#39;t like
modifying markup on the back end, you could also achieve the same result by
using a URL rewrite module such as &lt;a href=&quot;http://httpd.apache.org/docs/current/mod/mod_rewrite.html&quot; rel=&quot;noopener&quot;&gt;Apache&#39;s
&lt;code&gt;mod_rewrite&lt;/code&gt;&lt;/a&gt;. There
are &lt;a href=&quot;https://css-tricks.com/help-users-save-data/#article-header-id-0&quot; rel=&quot;noopener&quot;&gt;examples of how to achieve
this&lt;/a&gt; with
relatively little configuration.&lt;/p&gt;
&lt;p&gt;You could also extend this concept to CSS &lt;code&gt;background-image&lt;/code&gt; properties by
simply adding a class to the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; element:&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&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;&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;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 variable&quot;&gt;$saveData&lt;/span&gt; &lt;span class=&quot;token operator&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;span class=&quot;token delimiter important&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;save-data&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;span class=&quot;token keyword&quot;&gt;endif&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token delimiter important&quot;&gt;?&gt;&lt;/span&gt;&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;p&gt;From here, you can target the &lt;code&gt;save-data&lt;/code&gt; class on the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; element in your
CSS to change how images are delivered. You could send low resolution background
images to high resolution screens as shown in the above HTML example, or omit
certain resources altogether.&lt;/p&gt;
&lt;h3 id=&quot;omit-non-essential-imagery&quot;&gt;Omit non-essential imagery &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimizing-content-efficiency-save-data/#omit-non-essential-imagery&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Some image content on the web is simply non-essential. While such imagery can
make for nice asides to content, they may not be desirable by those trying to
squeeze all they can out of metered data plans. In what is perhaps the simplest
use case of &lt;code&gt;Save-Data&lt;/code&gt;, we can use the PHP detection code from earlier and omit
non-essential image markup altogether:&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;This paragraph is essential content. The image below may be humorous, but it&#39;s not critical to the content.&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;p&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 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 keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$saveData&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;false&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;// Only send this image if `Save-Data` hasn&#39;t been detected.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token delimiter important&quot;&gt;?&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;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;meme.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;One does not simply consume data.&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 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 punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This technique can certainly have a pronounced effect, as you can see in the
figure below:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A comparison of non-critical imagery being loaded when Save-Data is absent, versus that same imagery being omitted when Save-Data is present.&quot; decoding=&quot;async&quot; height=&quot;330&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fQ5ULmzstgJNbfV3tRTC.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fQ5ULmzstgJNbfV3tRTC.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fQ5ULmzstgJNbfV3tRTC.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fQ5ULmzstgJNbfV3tRTC.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fQ5ULmzstgJNbfV3tRTC.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fQ5ULmzstgJNbfV3tRTC.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fQ5ULmzstgJNbfV3tRTC.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fQ5ULmzstgJNbfV3tRTC.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fQ5ULmzstgJNbfV3tRTC.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fQ5ULmzstgJNbfV3tRTC.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fQ5ULmzstgJNbfV3tRTC.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fQ5ULmzstgJNbfV3tRTC.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fQ5ULmzstgJNbfV3tRTC.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fQ5ULmzstgJNbfV3tRTC.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fQ5ULmzstgJNbfV3tRTC.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fQ5ULmzstgJNbfV3tRTC.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fQ5ULmzstgJNbfV3tRTC.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/fQ5ULmzstgJNbfV3tRTC.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;A comparison of non-critical imagery being loaded when Save-Data is
absent, versus that same imagery being omitted when Save-Data is
present.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Of course, omitting images isn&#39;t the only possibility. You can also act on
&lt;code&gt;Save-Data&lt;/code&gt; to forego sending other non-critical resources, such as certain
typefaces.&lt;/p&gt;
&lt;h3 id=&quot;omit-non-essential-web-fonts&quot;&gt;Omit non-essential web fonts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimizing-content-efficiency-save-data/#omit-non-essential-web-fonts&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;While web fonts don&#39;t usually make up nearly as much of a given page&#39;s total
payload as images often do, they&#39;re still quite popular. &lt;a href=&quot;https://httparchive.org/reports/page-weight#bytesFont&quot; rel=&quot;noopener&quot;&gt;They don&#39;t consume an
insignificant amount of
data&lt;/a&gt;, either.
Furthermore, the way browsers fetch and render fonts is more complicated than
you might think, with concepts such as
&lt;a href=&quot;https://www.zachleat.com/web/webfont-glossary/#foit&quot; rel=&quot;noopener&quot;&gt;FOIT&lt;/a&gt;,
&lt;a href=&quot;https://www.zachleat.com/web/webfont-glossary/#fout&quot; rel=&quot;noopener&quot;&gt;FOUT&lt;/a&gt;, and browser
heuristics making rendering a nuanced operation.&lt;/p&gt;
&lt;p&gt;It might stand to reason then that you might want to leave out non-essential web
fonts for users who want leaner user experiences. &lt;code&gt;Save-Data&lt;/code&gt; makes this a
reasonably painless thing to do.&lt;/p&gt;
&lt;p&gt;For example, let&#39;s say you&#39;ve included &lt;a href=&quot;https://fonts.google.com/specimen/Fira+Sans&quot; rel=&quot;noopener&quot;&gt;Fira
Sans&lt;/a&gt; from &lt;a href=&quot;https://fonts.google.com/&quot; rel=&quot;noopener&quot;&gt;Google
Fonts&lt;/a&gt; on your site. Fira Sans is an excellent body
copy font, but maybe it isn&#39;t so crucial to users trying to save data. By adding
a class of &lt;code&gt;save-data&lt;/code&gt; to the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; element when the &lt;code&gt;Save-Data&lt;/code&gt; header is
present, we can write styles that invoke the non-essential typeface at first,
but then opts out of it when the &lt;code&gt;Save-Data&lt;/code&gt; header is present:&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;/* Opt into web fonts by default. */&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;p,&lt;br /&gt;li&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;Fira Sans&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Arial&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&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;/* Opt out of web fonts if the `save-Data` class is present. */&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.save-data p,&lt;br /&gt;.save-data li&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;Arial&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&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;Using this approach, you can leave the &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; snippet from Google Fonts in
place, because the browser speculatively loads CSS resources (including web
fonts) by first applying styles to the DOM, and then checking if any HTML
elements invoke any of the resources in the style sheet. If someone happens by
with &lt;code&gt;Save-Data&lt;/code&gt; on, Fira Sans will never load because the styled DOM never
invokes it. Arial will kick in, instead. It&#39;s not as nice as as Fira Sans, but
it may be preferable to those users trying to stretch their data plans.&lt;/p&gt;
&lt;h3 id=&quot;opting-out-of-server-pushes&quot;&gt;Opting out of server pushes &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimizing-content-efficiency-save-data/#opting-out-of-server-pushes&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://tools.ietf.org/html/rfc7540#section-8.2&quot; rel=&quot;noopener&quot;&gt;HTTP/2 server push&lt;/a&gt; is often
the most touted feature of HTTP/2. &lt;a href=&quot;https://www.smashingmagazine.com/2017/04/guide-http2-server-push/#measuring-server-push-performance&quot; rel=&quot;noopener&quot;&gt;While it can boost
performance&lt;/a&gt;,
it can potentially be problematic due to &lt;a href=&quot;https://jakearchibald.com/2017/h2-push-tougher-than-i-thought/&quot; rel=&quot;noopener&quot;&gt;caching
&amp;quot;gotchas&amp;quot;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you&#39;re comfortable using server push and understand its current, quirky way
of interacting with the browser cache, then great. But you may want to consider
disabling it altogether if the &lt;code&gt;Save-Data&lt;/code&gt; header is present.&lt;/p&gt;
&lt;p&gt;Many HTTP/2 implementations kick off a server push for a resource when a &lt;code&gt;Link&lt;/code&gt;
response header invoking &lt;a href=&quot;https://www.w3.org/TR/preload/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;rel=preload&lt;/code&gt;&lt;/a&gt; is set.
This leads to some confusion as to whether &lt;code&gt;rel=preload&lt;/code&gt; and server push are one
and the same, but they&#39;re two distinct things. &lt;code&gt;rel=preload&lt;/code&gt; is a resource hint,
and server push is part of HTTP/2. It just so happens the &lt;code&gt;Link&lt;/code&gt; header kicks
off a server push in a number of HTTP/2 implementations.&lt;/p&gt;
&lt;p&gt;The specification for &lt;code&gt;rel=preload&lt;/code&gt; &lt;a href=&quot;https://www.w3.org/TR/preload/#server-push-http-2&quot; rel=&quot;noopener&quot;&gt;addresses this potential pain
point&lt;/a&gt; by offering a &lt;code&gt;nopush&lt;/code&gt;
keyword to be used in &lt;code&gt;Link&lt;/code&gt; HTTP response headers. Using the back end
detection logic outlined earlier, you could append &lt;code&gt;nopush&lt;/code&gt; if &lt;code&gt;Save-Data&lt;/code&gt; is
present:&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 comment&quot;&gt;// `preload` like usual…&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token variable&quot;&gt;$preload&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;&amp;lt;/css/styles.css&gt;; rel=preload; as=style&quot;&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;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$saveData&lt;/span&gt; &lt;span class=&quot;token operator&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;span class=&quot;token comment&quot;&gt;// …but don&#39;t push anything if `Save-Data` is detected!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token variable&quot;&gt;$preload&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.=&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;; nopush&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;&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 double-quoted-string&quot;&gt;&quot;Link: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$preload&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;&lt;a href=&quot;https://www.ctrl.blog/entry/http2-save-data-push&quot; rel=&quot;noopener&quot;&gt;There are other ways to achieve
this&lt;/a&gt;, some more more nuanced
than others, but the idea is the same: HTTP/2 server push is turned off when
&lt;code&gt;Save-Data&lt;/code&gt; is present.&lt;/p&gt;
&lt;p&gt;As you can see, there&#39;s a lot that can be accomplished with &lt;code&gt;Save-Data&lt;/code&gt;. These
are just a couple simple use cases to get you going, so feel free to experiment
and see what novel use cases you can come up with!&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimizing-content-efficiency-save-data/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;Save-Data&lt;/code&gt; header does not have much nuance; it is either on or off, and
the application bears the burden of providing appropriate experiences based on
its setting, regardless of the reason.&lt;/p&gt;
&lt;p&gt;For example, some users might not allow data saving mode if they suspect there
will be a loss of app content or function, even in a poor connectivity
situation. Conversely, some users might enable it as a matter of course to keep
pages as small and simple as possible, even in a good connectivity situation.
It&#39;s best for your app to assume that the user wants the full and unlimited
experience until you have a clear indication otherwise via an explicit user
action.&lt;/p&gt;
&lt;p&gt;As site owners and web developers, let&#39;s take on the responsibility of managing
our content to improve the user experience for data- and cost-constrained users.&lt;/p&gt;
&lt;p&gt;For more detail on &lt;code&gt;Save-Data&lt;/code&gt; and excellent practical examples, see &lt;a href=&quot;https://css-tricks.com/help-users-save-data/&quot; rel=&quot;noopener&quot;&gt;Help Your
Users &lt;code&gt;Save Data&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Ilya Grigorik</name>
    </author><author>
      <name>Dave Gash</name>
    </author><author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Avoid large, complex layouts and layout thrashing</title>
    <link href="https://web.dev/avoid-large-complex-layouts-and-layout-thrashing/"/>
    <updated>2015-03-20T00:00:00Z</updated>
    <id>https://web.dev/avoid-large-complex-layouts-and-layout-thrashing/</id>
    <content type="html" mode="escaped">&lt;p&gt;Layout is where the browser figures out the geometric information for elements: their size and location in the page. Each element will have explicit or implicit sizing information based on the CSS that was used, the contents of the element, or a parent element. The process is called Layout in Chrome (and derived browsers such as Edge), and Safari. In Firefox it&#39;s called Reflow, but the process is effectively the same.&lt;/p&gt;
&lt;p&gt;Similarly to style calculations, the immediate concerns for layout cost are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The number of elements that require layout, which is a byproduct of the page&#39;s &lt;a href=&quot;https://web.dev/dom-size-and-interactivity/&quot;&gt;DOM size&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The complexity of those layouts.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/avoid-large-complex-layouts-and-layout-thrashing/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Layout has a direct effect on interaction latency&lt;/li&gt;
&lt;li&gt;Layout is normally scoped to the whole document.&lt;/li&gt;
&lt;li&gt;The number of DOM elements will affect performance; you should avoid triggering layout wherever possible.&lt;/li&gt;
&lt;li&gt;Avoid forced synchronous layouts and layout thrashing; read style values then make style changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-effects-of-layout-on-interaction-latency&quot;&gt;The effects of layout on interaction latency &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/avoid-large-complex-layouts-and-layout-thrashing/#the-effects-of-layout-on-interaction-latency&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When a user interacts with the page, those interactions should be as fast as possible. The amount of time it takes for an interaction to complete—ending when the browser presents the next frame to show the results of the interaction—is known as &lt;em&gt;interaction latency&lt;/em&gt;. This is an aspect of page performance that the &lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint&lt;/a&gt; metric measures.&lt;/p&gt;
&lt;p&gt;The amount of time it takes for the browser to present the next frame in response to a user interaction is known as the interaction&#39;s &lt;em&gt;presentation delay&lt;/em&gt;. The goal of an interaction is to provide visual feedback in order to signal to the user that something has occurred, and visual updates can involve some amount of layout work in order to achieve that goal.&lt;/p&gt;
&lt;p&gt;In order to keep your website&#39;s INP as low as possible, it&#39;s important to avoid layout when possible. If it&#39;s not possible to avoid layout entirely, it&#39;s important to limit that layout work so that the browser can present the next frame quickly.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-good-bg color-state-good-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; height=&quot;24&quot; width=&quot;24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Check&quot;&gt;   &lt;path d=&quot;M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Objective&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Read to learn more:&lt;/strong&gt; &lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;avoid-layout-wherever-possible&quot;&gt;Avoid layout wherever possible &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/avoid-large-complex-layouts-and-layout-thrashing/#avoid-layout-wherever-possible&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you change styles the browser checks to see if any of the changes require layout to be calculated, and for that render tree to be updated. Changes to &amp;quot;geometric properties&amp;quot;, such as widths, heights, left, or top all require layout.&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;.box&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; 20px&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; 20px&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;/**&lt;br /&gt;  * Changing width and height&lt;br /&gt;  * triggers layout.&lt;br /&gt;  */&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.box--expanded&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; 200px&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; 350px&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;Layout is almost always scoped to the entire document. If you have a lot of elements, it&#39;s going to take a long time to figure out the locations and dimensions of them all.&lt;/p&gt;
&lt;p&gt;If it&#39;s not possible to avoid layout then the key is to once again use Chrome DevTools to see how long it&#39;s taking, and determine if layout is the cause of a bottleneck. Firstly, open DevTools, go to the Timeline tab, hit record and interact with your site. When you stop recording you&#39;ll see a breakdown of how your site performed:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;DevTools showing a long time in Layout.&quot; decoding=&quot;async&quot; height=&quot;602&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ZK8uTy6hUd50CVY6hoMS.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;When digging into the trace in the above example, we see that over 28 milliseconds is spent inside layout for each frame, which, when we have 16 milliseconds to get a frame on screen in an animation, is far too high. You can also see that DevTools will tell you the tree size (1,618 elements in this case), and how many nodes were in need of layout (5 in this case).&lt;/p&gt;
&lt;p&gt;Keep in mind that the general advice here is to avoid layout &lt;em&gt;whenever possible&lt;/em&gt;—but it isn&#39;t always possible to avoid layout. In cases where you can&#39;t avoid layout, know that the cost of layout has a relationship with the size of the DOM. Although the relationship between the two isn&#39;t tightly coupled, larger DOMs will generally incur higher layout costs.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-good-bg color-state-good-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot; height=&quot;24&quot; width=&quot;24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Check&quot;&gt;   &lt;path d=&quot;M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Objective&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Read to learn more:&lt;/strong&gt; &lt;a href=&quot;https://web.dev/dom-size-and-interactivity/&quot;&gt;DOM size and interactivity&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;avoid-forced-synchronous-layouts&quot;&gt;Avoid forced synchronous layouts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/avoid-large-complex-layouts-and-layout-thrashing/#avoid-forced-synchronous-layouts&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Shipping a frame to screen has this order:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Using flexbox as layout.&quot; decoding=&quot;async&quot; height=&quot;122&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/mSZbp9o13Mub4fq8PWzT.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;First the JavaScript runs, &lt;em&gt;then&lt;/em&gt; style calculations, &lt;em&gt;then&lt;/em&gt; layout. It is, however, possible to force a browser to perform layout earlier with JavaScript. This is called &lt;strong&gt;forced synchronous layout&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The first thing to keep in mind is that as the JavaScript runs all the old layout values from the previous frame are known and available for you to query. So if, for example, you want to write out the height of an element (let&#39;s call it &amp;quot;box&amp;quot;) at the start of the frame you may write some code like this:&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;// Schedule our function to run at the start of the frame:&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;requestAnimationFrame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;logBoxHeight&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;logBoxHeight&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;// Gets the height of the box in pixels and logs it out:&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;box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;offsetHeight&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;Things get problematic if you&#39;ve changed the styles of the box &lt;em&gt;before&lt;/em&gt; you ask for its height:&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;logBoxHeight&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;  box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;super-big&#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;// Gets the height of the box in pixels and logs it out:&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;box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;offsetHeight&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;Now, in order to answer the height question, the browser must &lt;em&gt;first&lt;/em&gt; apply the style change (because of adding the &lt;code&gt;super-big&lt;/code&gt; class), and &lt;em&gt;then&lt;/em&gt; run layout. Only then will it be able to return the correct height. This is unnecessary and potentially expensive work.&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 the example above uses the &lt;code&gt;offsetHeight&lt;/code&gt; property, there are &lt;a href=&quot;https://gist.github.com/paulirish/5d52fb081b3570c81e3a&quot;&gt;many properties to be aware of&lt;/a&gt; that can trigger forced synchronous layout. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Because of this, you should always batch your style reads and do them first (where the browser can use the previous frame&#39;s layout values) and then do any writes:&lt;/p&gt;
&lt;p&gt;Done correctly the above function would be:&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;logBoxHeight&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;// Gets the height of the box in pixels and logs it out:&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;box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;offsetHeight&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;  box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;super-big&#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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;For the most part you shouldn&#39;t need to apply styles and then query values; using the last frame&#39;s values should be sufficient. Running the style calculations and layout synchronously and earlier than the browser would like are potential bottlenecks, and not something you will typically want to do.&lt;/p&gt;
&lt;h2 id=&quot;avoid-layout-thrashing&quot;&gt;Avoid layout thrashing &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/avoid-large-complex-layouts-and-layout-thrashing/#avoid-layout-thrashing&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There&#39;s a way to make forced synchronous layouts even worse: &lt;em&gt;do lots of them in quick succession&lt;/em&gt;. Take a look at this 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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;resizeAllParagraphsToMatchBlockWidth&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;// Puts the browser into a read-write-read-write cycle.&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;let&lt;/span&gt; i &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; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; paragraphs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&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;    paragraphs&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width &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;box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;offsetWidth&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;px&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;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;This code loops over a group of paragraphs and sets each paragraph&#39;s width to match the width of an element called &amp;quot;box&amp;quot;. It looks harmless enough, but the problem is that each iteration of the loop reads a style value (&lt;code&gt;box.offsetWidth&lt;/code&gt;) and then immediately uses it to update the width of a paragraph (&lt;code&gt;paragraphs[i].style.width&lt;/code&gt;). On the next iteration of the loop, the browser has to account for the fact that styles have changed since &lt;code&gt;offsetWidth&lt;/code&gt; was last requested (in the previous iteration), and so it must apply the style changes, and run layout. This will happen on &lt;em&gt;every single iteration!&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The fix for this sample is to once again &lt;em&gt;read&lt;/em&gt; and then &lt;em&gt;write&lt;/em&gt; values:&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;// Read.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; width &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; box&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;offsetWidth&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;resizeAllParagraphsToMatchBlockWidth&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;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &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; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; paragraphs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&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 comment&quot;&gt;// Now write.&lt;/span&gt;&lt;br /&gt;    paragraphs&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width &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;width&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;px&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;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;If you want to guarantee safety, consider using &lt;a href=&quot;https://github.com/wilsonpage/fastdom&quot; rel=&quot;noopener&quot;&gt;FastDOM&lt;/a&gt;, which automatically batches your reads and writes for you, and should prevent you from triggering forced synchronous layouts or layout thrashing accidentally.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hero image from &lt;a href=&quot;https://unsplash.com/&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;, by &lt;a href=&quot;https://unsplash.com/@halacious&quot; rel=&quot;noopener&quot;&gt;Hal Gatewood&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author><author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Reduce the scope and complexity of style calculations</title>
    <link href="https://web.dev/reduce-the-scope-and-complexity-of-style-calculations/"/>
    <updated>2015-03-20T00:00:00Z</updated>
    <id>https://web.dev/reduce-the-scope-and-complexity-of-style-calculations/</id>
    <content type="html" mode="escaped">&lt;p&gt;Changing the DOM, through adding and removing elements, changing attributes, classes, or through animation, will all cause the browser to recalculate element styles and, in many cases, layout (or reflow) the page, or parts of it. This process is called &lt;em&gt;computed style calculation&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The first part of computing styles is to create a set of matching selectors, which is essentially the browser figuring out which classes, pseudo-selectors, and IDs apply to any given element.&lt;/p&gt;
&lt;p&gt;The second part of the process involves taking all the style rules from the matching selectors and figuring out what final styles the element has.&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; Roughly half of the time used in Blink (the rendering engine used in Chromium and derived browsers) to calculate the computed style for an element is used to match selectors, and the other half of the time is used to construct the RenderStyle (computed style representation) from the matched rules.  Rune Lillesveen, Opera / &lt;a href=&quot;https://docs.google.com/document/d/1vEW86DaeVs4uQzNFI5R-_xS9TcS1Cs_EUsHRSgCHGu8/view&quot;&gt;Style Invalidation in Blink&lt;/a&gt; &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-the-scope-and-complexity-of-style-calculations/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;How reducing style calculation costs can lower interaction latency.&lt;/li&gt;
&lt;li&gt;Reduce the complexity of your selectors; use a class-centric methodology (BEM, for example).&lt;/li&gt;
&lt;li&gt;Reduce the number of elements on which style calculation must be calculated.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;style-recalculation-time-and-interaction-latency&quot;&gt;Style recalculation time and interaction latency &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-the-scope-and-complexity-of-style-calculations/#style-recalculation-time-and-interaction-latency&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt; is a user-centric runtime performance metric that assesses a page&#39;s overall responsiveness to user input. When interaction latency is assessed by this metric, it measures the time starting from when the user interacts with the page, up until the browser paints the next frame showing the corresponding visual updates made to the user interface.&lt;/p&gt;
&lt;p&gt;A significant component of an interaction is the time it takes to paint the next frame. Rendering work done to present the next frame is made up of many parts, including calculation of page styles that occur just prior to layout, paint, and compositing work. While this article focuses solely on style calculation costs, it&#39;s important to emphasize that reducing any part of the rendering phase inherent to interaction will reduce its total latency—style calculation included.&lt;/p&gt;
&lt;h2 id=&quot;reduce-the-complexity-of-your-selectors&quot;&gt;Reduce the complexity of your selectors &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-the-scope-and-complexity-of-style-calculations/#reduce-the-complexity-of-your-selectors&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the simplest case, you can reference an element in your CSS with just a class:&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;.title&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;/* styles */&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;But, as any project grows, it will likely result in more complex CSS, such that you may end up with selectors that look 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 selector&quot;&gt;.box:nth-last-child(-n+1) .title&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;/* styles */&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 order to know how these styles apply to the page, the browser has to effectively ask &amp;quot;is this an element with a class of &lt;code&gt;title&lt;/code&gt; which has a parent who happens to be the minus nth child plus 1 element with a class of &lt;code&gt;box&lt;/code&gt;?&amp;quot; Figuring this out &lt;em&gt;can&lt;/em&gt; take a lot of time, depending on the selector used as well as the browser in question. The intended behavior of the selector could instead be changed to a class:&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;.final-box-title&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;/* styles */&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;You can take issue with the name of the class, but the job just got a lot simpler for the browser. In the previous version, in order to know, for example, that the element is the last of its type, the browser must first know everything about all the other elements and whether the are any elements that come after it that would be the &lt;code&gt;nth-last-child&lt;/code&gt;, which is potentially more expensive than simply matching up the selector to the element because its class matches.&lt;/p&gt;
&lt;h2 id=&quot;reduce-the-number-of-elements-being-styled&quot;&gt;Reduce the number of elements being styled &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-the-scope-and-complexity-of-style-calculations/#reduce-the-number-of-elements-being-styled&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Another performance consideration—which is typically &lt;em&gt;the more important factor for many style updates&lt;/em&gt;—is the sheer volume of work that needs to be carried out when an element changes.&lt;/p&gt;
&lt;p&gt;In general terms, the worst case cost of calculating the computed style of elements is the number of elements multiplied by the selector count, because each element needs to be at least checked once against every style to see if it matches.&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; It used to be the case that if you changed a class on—say—the body element, that all the children in the page would need to have their computed styles recalculated. Thankfully that is no longer the case; some browsers instead maintain a small collection of rules unique to each element that, if changed, cause the element&#39;s styles to be recalculated. That means that an element may or may not need to be recalculated depending on where it is in the tree, and what specifically got changed. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Style calculations can often be targeted to a few elements directly rather than invalidating the page as a whole. In modern browsers, this tends to be less of an issue because the browser doesn&#39;t necessarily need to check all the elements potentially affected by a change. Older browsers, on the other hand, aren&#39;t necessarily as optimized for such tasks. Where you can, you should &lt;strong&gt;reduce the number of invalidated elements&lt;/strong&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 into Web Components, it&#39;s worth noting that style calculations here are a little different, since by default styles do not cross the Shadow DOM boundary, and are scoped to individual components rather than the tree as a whole. Overall, however, the same concept still applies: smaller trees with simpler rules are more efficiently processed than larger trees with more complex rules. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;measure-your-style-recalculation-cost&quot;&gt;Measure your style recalculation cost &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-the-scope-and-complexity-of-style-calculations/#measure-your-style-recalculation-cost&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One way to measure the cost of style recalculations is to use the performance panel in Chrome DevTools. To begin, open DevTools, go to the tab labeled &lt;strong&gt;Performance&lt;/strong&gt;, hit record, and interact with the page. When you stop recording, you&#39;ll see something like the image below:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;DevTools showing style calculations.&quot; decoding=&quot;async&quot; height=&quot;586&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/mpzxBlLCKMupmkZV13pj.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;The strip at the top is a miniature flame chart that also plots frames per second. The closer the activity is to the bottom of the strip, the faster frames are being painted by the browser. If you see the flame chart leveling out at the top with red strips above it, then you have work that&#39;s causing long running frames.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Zooming in on a trouble area in Chrome DevTools in the activity summary of the populated performance panel in Chrome DevTools.&quot; decoding=&quot;async&quot; height=&quot;126&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 769px) 769px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/VfajFwdCmOINMIaN6ApQ.png?auto=format&amp;w=1538 1538w&quot; width=&quot;769&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;If you have a long running frame during an interaction like scrolling, then it bears further scrutiny. If you have a large purple block, zoom in on the activity and select any work labeled &lt;strong&gt;Recalculate Style&lt;/strong&gt; to get more information on potentially expensive style recalculation work.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Getting the details of long-running style calculations, including vital information such as the amount of elements affected by the style recalculation work.&quot; decoding=&quot;async&quot; height=&quot;218&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 647px) 647px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/qUtq3jcEjpzDN9isYF9U.png?auto=format&amp;w=1294 1294w&quot; width=&quot;647&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;In this grab there is long-running style recalculation work that is taking just over 25ms.&lt;/p&gt;
&lt;p&gt;If you click the event itself, you are given a call stack. If the rendering work was due to a user interaction, the place in your JavaScript that is responsible for triggering the style change will be called out. In addition to that, you also get the number of elements that have been affected by the change—just over 900 elements in this case—and how long it took to do the style calculation work. You can use this information to start trying to find a fix in your code.&lt;/p&gt;
&lt;h2 id=&quot;use-block,-element,-modifier&quot;&gt;Use Block, Element, Modifier &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-the-scope-and-complexity-of-style-calculations/#use-block,-element,-modifier&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Approaches to coding like &lt;a href=&quot;https://bem.info/&quot; rel=&quot;noopener&quot;&gt;BEM (Block, Element, Modifier)&lt;/a&gt; actually bake in the selector matching performance benefits above, because it recommends that everything has a single class, and, where you need hierarchy, that gets baked into the name of the class as well:&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;.list&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;/* Styles */&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;.list__list-item&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;/* Styles */&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;If you need some modifier, like in the above where we want to do something special for the last child, you can add that like so:&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;.list__list-item--last-child&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;/* Styles */&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;If you&#39;re looking for a good way to organize your CSS, BEM is a good starting point, both from a structure point-of-view, and also because of the simplifications of style lookup the methodology promotes.&lt;/p&gt;
&lt;p&gt;If you don&#39;t like BEM, there are other ways to approach your CSS, but the performance considerations should be assessed alongside the ergonomics of the approach.&lt;/p&gt;
&lt;h2 id=&quot;resources&quot;&gt;Resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/reduce-the-scope-and-complexity-of-style-calculations/#resources&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.google.com/document/d/1vEW86DaeVs4uQzNFI5R-_xS9TcS1Cs_EUsHRSgCHGu8/edit&quot; rel=&quot;noopener&quot;&gt;Style invalidation in Blink&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bem.info/&quot; rel=&quot;noopener&quot;&gt;BEM (Block, Element, Modifier)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Hero image from &lt;a href=&quot;https://unsplash.com/&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;, by &lt;a href=&quot;https://unsplash.com/@markusspiske&quot; rel=&quot;noopener&quot;&gt;Markus Spiske&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>Paul Lewis</name>
    </author><author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Eliminate unnecessary downloads</title>
    <link href="https://web.dev/optimizing-content-efficiency-eliminate-downloads/"/>
    <updated>2014-03-31T00:00:00Z</updated>
    <id>https://web.dev/optimizing-content-efficiency-eliminate-downloads/</id>
    <content type="html" mode="escaped">&lt;p&gt;The fastest and best-optimized resource is a resource not sent. You should eliminate unnecessary resources from your application. It&#39;s a good practice to question—and periodically revisit—the implicit and explicit assumptions with your team. Here are a few examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You&#39;ve always included resource X on your pages, but does the cost of downloading and displaying it offset the value it delivers to the user? Can you measure and prove its value?&lt;/li&gt;
&lt;li&gt;Does the resource (especially if it&#39;s a third-party resource) deliver consistent performance? Is this resource in the critical path, or need to be? If the resource is in the critical path, could it be a single point of failure for the site? That is, if the resource is unavailable, does it affect performance and the user experience of your pages?&lt;/li&gt;
&lt;li&gt;Does this resource need or have an SLA? Does this resource follow performance best practices: compression, caching, and so on?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Too often, pages contain resources that are unnecessary, or worse, that hinder page performance without delivering much value to the visitor or to the site they&#39;re hosted on. This applies equally to first-party and third-party resources and widgets:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Site A has decided to display a photo carousel on its homepage to allow the visitor to preview multiple photos with a quick click. All of the photos are loaded when the page is loaded, and the user advances through the photos.
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Question:&lt;/strong&gt; Have you measured how many users view multiple photos in the carousel? You might be incurring high overhead by downloading resources that most visitors never view.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Site B has decided to install a third-party widget to display related content, improve social engagement, or provide some other service.
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Question:&lt;/strong&gt; Have you tracked how many visitors use the widget or click-through on the content that the widget provides? Is the engagement that this widget generates enough to justify its overhead? Furthermore, is it feasible for you to use a loading strategy to ensure &lt;a href=&quot;https://web.dev/reduce-javascript-payloads-with-code-splitting/&quot;&gt;the script isn&#39;t loaded until it&#39;s needed&lt;/a&gt;?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Determining whether to eliminate unnecessary downloads often requires a lot of careful thinking and measurement. For best results, periodically inventory and revisit these questions for every asset on your pages.&lt;/p&gt;
&lt;h2 id=&quot;effects-on-core-web-vitals&quot;&gt;Effects on Core Web Vitals &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimizing-content-efficiency-eliminate-downloads/#effects-on-core-web-vitals&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Core Web Vitals initative was introduced by Google to provide a set of metrics that reflect what users are experiencing as they use the web. While there are many optimization strategies for Core Web Vitals, questioning whether to load a particular resource on a page may light a path for you to improve these metrics on your website. Below are a few examples—grouped by each Core Web Vital—that are worth your consideration. Though this isn&#39;t an exhaustive list of examples (and there are many!), reading them over may give you food for thought.&lt;/p&gt;
&lt;h3 id=&quot;largest-contentful-paint-lcp&quot;&gt;Largest Contentful Paint (LCP) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimizing-content-efficiency-eliminate-downloads/#largest-contentful-paint-lcp&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt; measures when the largest content (for example a hero image, or a headline) is loaded. It&#39;s considered an important perceptual metric that gives the user the impression that a site is loading quickly.&lt;/p&gt;
&lt;p&gt;In general, downloading less resources means that the bandwidth the user does have will be allocated across less resources, and may translate to an improvement in LCP. A classic example is that of lazy loading, where images outside of the viewport during page load will not be downloaded until the browser has determined the user is more likely to see them. If you have a large thumbnail gallery of, say, 50 images, lazy loading all but the top ten of them means that the browser can make more efficient use of the bandwidth available to it, and the first images the user will see will load more quickly.&lt;/p&gt;
&lt;p&gt;However, it&#39;s not just about loading less &lt;em&gt;images&lt;/em&gt;, necessarily. The browser has an internal prioritization scheme that determines how much bandwidth each resource should receive. However, even with this &lt;em&gt;all&lt;/em&gt; resources—particularly those downloaded at high priority—have the potential to deprive a potential LCP element&#39;s dependent resource. This is especially true on slow network connections. That dependent resource may be an image file that represents the page&#39;s LCP element, but it could also very well be a web font resource that the browser needs to render a text node that may be determined as the page&#39;s LCP element.&lt;/p&gt;
&lt;p&gt;If your website is heavy on text, it may be the case that a page&#39;s LCP element is a text node. While there are many &lt;a href=&quot;https://web.dev/font-best-practices/&quot;&gt;good font optimization and loading strategies&lt;/a&gt;, it may be worth considering whether a system font is sufficient for your website&#39;s needs, so that LCP elements which are text nodes can load without a dependency on a web font resource and paint almost immediately as the CSS and HTML arrives from the server.&lt;/p&gt;
&lt;h3 id=&quot;cumulative-layout-shift-cls&quot;&gt;Cumulative Layout Shift (CLS) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimizing-content-efficiency-eliminate-downloads/#cumulative-layout-shift-cls&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Every resource you load has the potential to contribute to a page&#39;s &lt;a href=&quot;https://web.dev/cls/&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt;, particularly if has not finished downloading by the time of the initial paint. For images, avoid CLS involves practices such as setting explicit dimensions. For fonts, managing font loading and potentially fallback font matching can minimize shifts during a web font&#39;s swap period. For JavaScript, it could be managing how that script manipulates the DOM so that layout shifts are reduced to an acceptable amount.&lt;/p&gt;
&lt;p&gt;Every resource that contributes to a page&#39;s CLS requires some amount of work to ensure page layout is sufficiently stable. By questioning whether or not you need a specific resource, you&#39;re not just speeding up page loads, you&#39;re also reducing the cognitive effort necessary to preserve layout stability. That leads to not only a much less frustrating user experience, but a less frustrating developer experience, as you&#39;ll have more time to pursue other goals in your projects.&lt;/p&gt;
&lt;h3 id=&quot;interaction-to-next-paint-inp-and-first-input-delay-fid&quot;&gt;Interaction to Next Paint (INP) and First Input Delay (FID) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimizing-content-efficiency-eliminate-downloads/#interaction-to-next-paint-inp-and-first-input-delay-fid&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt; and &lt;a href=&quot;https://web.dev/fid/&quot;&gt;First Input Delay (FID)&lt;/a&gt; are metrics that measure responsiveness to user inputs. &lt;a href=&quot;https://web.dev/inp-cwv/&quot;&gt;While INP is slated to replace FID as a Core Web Vital&lt;/a&gt; in March of 2024, optimization strategies for FID tend to also apply to INP. Furthermore, INP is generally more difficult to optimize for than FID, as it tracks the full interaction latency for &lt;em&gt;all&lt;/em&gt; page interactions, not just the input delay of the first interaction as FID measures.&lt;/p&gt;
&lt;p&gt;INP and FID tend to be most affected by JavaScript, as JavaScript is what drives most of the interactivity one experiences across the web. For both INP and FID, the amount of script resources downloaded during page load will kick off potentially expensive work involved in &lt;a href=&quot;https://web.dev/script-evaluation-and-long-tasks/&quot;&gt;script evaluation and compilation&lt;/a&gt;. The less JavaScript you load during startup, the less work the browser has to do at that critical point in the page experience.&lt;/p&gt;
&lt;p&gt;While there are strategies for reducing the &lt;em&gt;size&lt;/em&gt; of JavaScript resources downloaded during startup—such as code splitting and tree shaking—it&#39;s worth auditing the packages you use in your projects to see if they&#39;re necessary at all. For example, &lt;a href=&quot;https://lodash.com/&quot; rel=&quot;noopener&quot;&gt;lodash&lt;/a&gt; has many methods that are still useful today, but ships with methods that the browser provides out of the box, such as &lt;code&gt;Array&lt;/code&gt;-specific functions for &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/map&quot; rel=&quot;noopener&quot;&gt;mapping&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce&quot; rel=&quot;noopener&quot;&gt;reducing&lt;/a&gt;, and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/filter&quot; rel=&quot;noopener&quot;&gt;filtering&lt;/a&gt;, and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array&quot; rel=&quot;noopener&quot;&gt;many others&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Progressive enhancement is also &lt;a href=&quot;https://web.dev/adaptive-serving-based-on-network-quality/&quot;&gt;a useful approach&lt;/a&gt; to JavaScript, as it enables you to serve a baseline (but still functional) experience for users that you can add to for users with more powerful devices and faster network connections. Whether you adhere to the principle of progressive enhancement or not, the point remains: Every JavaScript resource you can avoid downloading can result in an experience that responds faster to user interactions, which is a vital aspect of web performance.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimizing-content-efficiency-eliminate-downloads/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Auditing your website for unnecessary downloads may be just one aspect of delivering fast user experiences, but it&#39;s one that has the potential for high impact. To recap:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Inventory your own assets and third-party assets on your pages.&lt;/li&gt;
&lt;li&gt;Measure the performance of each asset: its value and its technical performance.&lt;/li&gt;
&lt;li&gt;Determine if the resources are providing sufficient value.&lt;/li&gt;
&lt;li&gt;Understand the effect of unnecessary downloads on &lt;a href=&quot;https://web.dev/vitals/&quot;&gt;Core Web Vitals&lt;/a&gt; and supporting metrics.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By optimizing content efficiency in this way, you&#39;re not only improving performance overall, you&#39;re also taking care not to waste users&#39; bandwidth, as well as potentially improving user-centric metrics and delivering a better user experience.&lt;/p&gt;
</content>
    <author>
      <name>Ilya Grigorik</name>
    </author><author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
</feed>
