<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://web.dev/</id>
  <title>Milica Mihajlija on web.dev</title>
  <updated>2026-04-15T23:21:06Z</updated>
  <author>
    <name>Milica Mihajlija</name>
  </author>
  <link href="https://web.dev/authors/mihajlija/feed.xml" rel="self"/>
  <link href="https://web.dev/"/>
  <icon>https://web-dev.imgix.net/image/admin/WkMOiDtaDgiAA2YkRZ5H.jpg?auto=format</icon>
  <logo>https://web.dev/images/shared/rss-banner.png</logo>
  <subtitle>Our latest news, updates, and stories by Milica Mihajlija.</subtitle>
  
  
  <entry>
    <title>First-party cookie recipes</title>
    <link href="https://web.dev/first-party-cookie-recipes/"/>
    <updated>2022-06-07T00:00:00Z</updated>
    <id>https://web.dev/first-party-cookie-recipes/</id>
    <content type="html" mode="escaped">&lt;p&gt;Cookies can be first-party or third-party relative to the user&#39;s context; depending on which site the user is on at the time. If the cookie&#39;s registrable domain and scheme match the current top-level page, that is, what&#39;s displayed in the browser&#39;s address bar, the cookie is considered to be from the &lt;a href=&quot;https://web.dev/same-site-same-origin/&quot;&gt;same site&lt;/a&gt; as the page and it&#39;s generally referred to as a first-party cookie.&lt;/p&gt;
&lt;img alt=&quot;&quot; decoding=&quot;async&quot; height=&quot;604&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/bN6n3dyOEkhwnVUFGOUQ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/bN6n3dyOEkhwnVUFGOUQ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/bN6n3dyOEkhwnVUFGOUQ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/bN6n3dyOEkhwnVUFGOUQ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/bN6n3dyOEkhwnVUFGOUQ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/bN6n3dyOEkhwnVUFGOUQ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/bN6n3dyOEkhwnVUFGOUQ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/bN6n3dyOEkhwnVUFGOUQ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/bN6n3dyOEkhwnVUFGOUQ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/bN6n3dyOEkhwnVUFGOUQ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/bN6n3dyOEkhwnVUFGOUQ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/bN6n3dyOEkhwnVUFGOUQ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/bN6n3dyOEkhwnVUFGOUQ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/bN6n3dyOEkhwnVUFGOUQ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/bN6n3dyOEkhwnVUFGOUQ.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/bN6n3dyOEkhwnVUFGOUQ.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/bN6n3dyOEkhwnVUFGOUQ.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/bN6n3dyOEkhwnVUFGOUQ.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Cookies from domains other than the current site are generally referred to as &lt;a href=&quot;https://web.dev/samesite-cookie-recipes/#use-cases-for-cross-site-or-third-party-cookies&quot;&gt;third-party cookies&lt;/a&gt;.&lt;/p&gt;
&lt;img alt=&quot;&quot; decoding=&quot;async&quot; height=&quot;693&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HtvSbbHN7Sdj1xJURIqj.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HtvSbbHN7Sdj1xJURIqj.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HtvSbbHN7Sdj1xJURIqj.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HtvSbbHN7Sdj1xJURIqj.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HtvSbbHN7Sdj1xJURIqj.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HtvSbbHN7Sdj1xJURIqj.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HtvSbbHN7Sdj1xJURIqj.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HtvSbbHN7Sdj1xJURIqj.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HtvSbbHN7Sdj1xJURIqj.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HtvSbbHN7Sdj1xJURIqj.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HtvSbbHN7Sdj1xJURIqj.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HtvSbbHN7Sdj1xJURIqj.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HtvSbbHN7Sdj1xJURIqj.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HtvSbbHN7Sdj1xJURIqj.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HtvSbbHN7Sdj1xJURIqj.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HtvSbbHN7Sdj1xJURIqj.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HtvSbbHN7Sdj1xJURIqj.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HtvSbbHN7Sdj1xJURIqj.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The distinction between first-party and third-party context on the web isn&#39;t always obvious and the effect it has on different resources can vary. To address some of the challenges with how browsers treat first-party and third-party cookies, &lt;a href=&quot;https://developer.chrome.com/docs/privacy-sandbox/first-party-sets/&quot;&gt;First-Party Sets&lt;/a&gt; proposes to allow related domain names owned and operated by the same entity to declare themselves as belonging to the same first party. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;the-good-first-party-cookie-recipe&quot;&gt;The good first-party cookie recipe &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/first-party-cookie-recipes/#the-good-first-party-cookie-recipe&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If the cookie you&#39;re setting is not used across sites, for example, it&#39;s used to manage sessions on your site and it&#39;s never used in a cross-site iframe, that cookie is always used in a first-party context.&lt;/p&gt;
&lt;p&gt;By default, cookies can be shared across sites, accessed by JavaScript, and sent over HTTP connections, which comes with some privacy and security risks. While there&#39;s ongoing work to improve the default behavior, through &lt;a href=&quot;https://developer.chrome.com/docs/privacy-sandbox/&quot; rel=&quot;noopener&quot;&gt;Privacy Sandbox&lt;/a&gt; and other proposals such as &lt;a href=&quot;https://github.com/sbingler/Origin-Bound-Cookies&quot; rel=&quot;noopener&quot;&gt;origin-bound cookies&lt;/a&gt;, there&#39;s a lot you can do today by setting additional attributes on your cookies.&lt;/p&gt;
&lt;p&gt;The following configuration is best practice, ensuring security and cross-browser compatibility for most first-party cookies. It will provide you with a safe foundation, which you can adjust to open up permissions only when necessary. This article also covers recipe variations for some specific use-cases.&lt;/p&gt;
&lt;h3 id=&quot;the-recipe&quot;&gt;The recipe &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/first-party-cookie-recipes/#the-recipe&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Set-Cookie:&lt;br /&gt;__Host-cookie-name=cookie-value;&lt;br /&gt;Secure;&lt;br /&gt;Path=/;&lt;br /&gt;HttpOnly;&lt;br /&gt;Max-Age=7776000;&lt;br /&gt;SameSite=Lax;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;details&gt;
&lt;summary&gt;
  Details
&lt;/summary&gt;
&lt;p&gt;&lt;code&gt;__Host&lt;/code&gt; is an optional prefix that makes some attributes mandatory and forbids others:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Secure&lt;/code&gt; must be present&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Domain&lt;/code&gt; must be omitted&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Path&lt;/code&gt; must be &lt;code&gt;/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With &lt;code&gt;__Host&lt;/code&gt; added, you can rely on the browser to check if attributes above are set in line with &lt;code&gt;__Host&lt;/code&gt; rules and reject the cookie if not.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Secure&lt;/code&gt; protects cookies from being stolen on insecure networks because it only allows sending cookies over &lt;a href=&quot;https://web.dev/why-https-matters/&quot;&gt;HTTPS connections&lt;/a&gt;. If you haven&#39;t fully &lt;a href=&quot;https://web.dev/why-https-matters/&quot;&gt;migrated your site to HTTPS&lt;/a&gt;, make that a priority.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Domain&lt;/code&gt; attribute specifies which hosts can receive a cookie. Omitting it restricts the cookie to the current document host, excluding subdomains: the cookie for &lt;code&gt;example.com&lt;/code&gt; will be sent on every request to &lt;code&gt;example.com&lt;/code&gt; but not on requests to &lt;code&gt;images.example.com&lt;/code&gt;. If you have different apps running on different subdomains, this reduces the risk of one compromised domain allowing a door into the others.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Path&lt;/code&gt; indicates the path that must exist in the requested URL for the browser to send the &lt;code&gt;Cookie&lt;/code&gt; header. Setting &lt;code&gt;Path=/&lt;/code&gt; means that the cookie is sent to all URL paths on that domain. The combination of no &lt;code&gt;Domain&lt;/code&gt; and &lt;code&gt;Path=/&lt;/code&gt; makes the cookie bound to the origin as closely as possible, so it behaves similarly to other client-side storage such as &lt;code&gt;LocalStorage&lt;/code&gt;—there&#39;s no confusion that &lt;code&gt;example.com/a&lt;/code&gt; might receive different values to &lt;code&gt;example.com/b&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;HttpOnly&lt;/code&gt; attribute adds some protection against malicious third-party scripts on your sites by &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Cookies#restrict_access_to_cookies&quot; rel=&quot;noopener&quot;&gt;restricting JavaScript access&lt;/a&gt;. It allows a cookie to be sent only in request headers and makes them unavailable to JavaScript via &lt;code&gt;document.cookie&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; Even with &lt;code&gt;HttpOnly&lt;/code&gt;, you can still trigger requests from JavaScript, like fetch or XML HTTP requests⁠—and if you&#39;ve specified including credentials, cookies (including HTTP only cookies) will be sent⁠ on those requests—they&#39;re just not visible to those client-side scripts in any way. If any of those scripts on your site have been compromised or are malicious, their access to potentially sensitive cookie data is limited. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;code&gt;Max-Age&lt;/code&gt; limits the life of a cookie as browser sessions can last a pretty long time and you don&#39;t want stale cookies hanging around forever. It&#39;s good for short-term cookies, such as user sessions or even shorter ones such as tokens for form submission. &lt;code&gt;Max-Age&lt;/code&gt; is defined in seconds and in the example above it&#39;s set to 7776000 seconds which is 90 days. This is a reasonable default, which you can change depending on your use case.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The maximum value of &lt;a href=&quot;https://httpwg.org/http-extensions/draft-ietf-httpbis-rfc6265bis.html#name-the-max-age-attribute&quot;&gt;&lt;code&gt;Max-Age&lt;/code&gt; must not be greater than 400 days&lt;/a&gt; (34560000 seconds).  Another way to limit the life of a cookie is specifying the &lt;code&gt;Expires&lt;/code&gt; attribute which sets an expiration date in the future, instead of an expiration duration. Note that the date and time set in the &lt;code&gt;Expires&lt;/code&gt; attribute are relative to the client the cookie is being set on, not the server. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Set-Cookie/SameSite#lax&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;SameSite=Lax&lt;/code&gt;&lt;/a&gt; restricts the cookie to only be sent on same-site requests. That is, where the request matches the current browsing context–the top level site the user is currently visiting which is displayed in their location bar. &lt;code&gt;SameSite=Lax&lt;/code&gt; is the default in modern browsers but it&#39;s good practice to specify it for compatibility across browsers which may have different defaults. By explicitly marking the cookie as same-site only, you are restricting it to your first-party contexts, and you should not have to make changes to that cookie when third-party cookies go away.&lt;/p&gt;
&lt;p&gt;To learn more about different cookie attributes, check out &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Set-Cookie&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Set-Cookie&lt;/code&gt; documentation on MDN&lt;/a&gt;.&lt;/p&gt;
&lt;/details&gt;
&lt;h2 id=&quot;first-party-cookie-recipe-for-sites-with-subdomains&quot;&gt;First-party cookie recipe for sites with subdomains &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/first-party-cookie-recipes/#first-party-cookie-recipe-for-sites-with-subdomains&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you have a site with subdomains and want to have one session across all of them, the &lt;code&gt;Host&lt;/code&gt; prefix can be too restrictive. For example &lt;code&gt;news.site&lt;/code&gt; could have subdomains for topics, such as &lt;code&gt;finance.news.site&lt;/code&gt; and &lt;code&gt;sport.news.site&lt;/code&gt; and you&#39;d want one user session on all of them. In that case, use the &lt;code&gt;__Secure&lt;/code&gt; prefix instead of &lt;code&gt;__Host&lt;/code&gt; and specify &lt;code&gt;Domain&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;the-recipe-2&quot;&gt;The recipe &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/first-party-cookie-recipes/#the-recipe-2&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Set-Cookie:&lt;br /&gt;__Secure-cookie-name=cookie-value;&lt;br /&gt;Secure;&lt;br /&gt;Domain=news.site;&lt;br /&gt;Path=/;&lt;br /&gt;HttpOnly;&lt;br /&gt;Max-Age=7776000;&lt;br /&gt;SameSite=Lax;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;details&gt;
&lt;summary&gt;
  Details
&lt;/summary&gt;
&lt;p&gt;&lt;code&gt;__Secure&lt;/code&gt; is an optional prefix which asserts fewer requirements than &lt;code&gt;__Host&lt;/code&gt;: it only requires that the cookie be set with the &lt;code&gt;Secure&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; The &lt;code&gt;Secure&lt;/code&gt; attribute restricts the cookie to be sent only over the HTTPS protocol. The &lt;code&gt;__Secure&lt;/code&gt; prefix tells the browser to check if the cookie is set with the &lt;code&gt;Secure&lt;/code&gt; attribute and reject it if it&#39;s not. This ensures that an attacker can not overwrite secure cookies with another cookie that is missing the &lt;code&gt;Secure&lt;/code&gt; attribute.&lt;br /&gt; &lt;/div&gt;&lt;/aside&gt;
&lt;/details&gt;
&lt;h2 id=&quot;restricting-first-party-cookie-access-on-requests-initiated-from-third-party-websites&quot;&gt;Restricting first-party cookie access on requests initiated from third-party websites &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/first-party-cookie-recipes/#restricting-first-party-cookie-access-on-requests-initiated-from-third-party-websites&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While &lt;code&gt;SameSite=Lax&lt;/code&gt; cookies are not sent on cross-site subrequests (for example, when loading embedded images or iframes on a third-party site), they are sent when a user is navigating to the origin site (for example, when following a link from a different site).&lt;/p&gt;
&lt;p&gt;You can further restrict cookies access and disallow sending them along with requests initiated from third-party websites with &lt;code&gt;SameSite=Strict&lt;/code&gt;. This is useful when you have cookies relating to functionality that will always be behind an initial navigation, such as changing a password or making a purchase.&lt;/p&gt;
&lt;h3 id=&quot;the-recipe-3&quot;&gt;The recipe &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/first-party-cookie-recipes/#the-recipe-3&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Set-Cookie:&lt;br /&gt;__Host-cookie-name=cookie-value;&lt;br /&gt;Secure;&lt;br /&gt;Path=/;&lt;br /&gt;HttpOnly;&lt;br /&gt;Max-Age=7776000;&lt;br /&gt;SameSite=Strict;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</content>
    <author>
      <name>Milica Mihajlija</name>
    </author>
  </entry>
  
  <entry>
    <title>Chrome and Firefox soon to reach major version 100</title>
    <link href="https://web.dev/chrome-firefox-100/"/>
    <updated>2022-02-15T00:00:00Z</updated>
    <id>https://web.dev/chrome-firefox-100/</id>
    <content type="html" mode="escaped">&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/blog/force-major-version-to-100/&quot; rel=&quot;noopener&quot;&gt;Chrome&lt;/a&gt;
and &lt;a href=&quot;https://www.otsukare.info/2021/04/20/ua-three-digits-get-ready&quot; rel=&quot;noopener&quot;&gt;Firefox&lt;/a&gt;
will reach version 100 in a couple of months. This has the potential to cause breakage
on sites that rely on identifying the browser version to perform business logic.
This post covers the timeline of events, the strategies that Chrome and Firefox are
taking to mitigate the impact, and how you can help.&lt;/p&gt;
&lt;h2 id=&quot;user-agent-string&quot;&gt;User-Agent string &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/chrome-firefox-100/#user-agent-string&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;User-Agent (UA) is a string that browsers send in HTTP headers, so servers can
identify the browser.  The string is also accessible through JavaScript with
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Navigator/userAgent&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;navigator.userAgent&lt;/code&gt;&lt;/a&gt;.
It&#39;s usually formatted as follows:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;browser_name&amp;gt;/&amp;lt;major_version&amp;gt;.&amp;lt;minor_version&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;For example, the latest release versions of browsers at the time of publishing
this post are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Chrome: &lt;code&gt;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Firefox: &lt;code&gt;Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:96.0) Gecko/20100101 Firefox/96.0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Safari: &lt;code&gt;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.2 Safari/605.1.15&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;major-version-100—three-digit-version-number&quot;&gt;Major version 100—three-digit version number &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/chrome-firefox-100/#major-version-100%E2%80%94three-digit-version-number&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Major version 100 is a big milestone for both Chrome and Firefox. It also has
the potential to cause breakage on websites as we move from a two-digit to a
&lt;em&gt;three-digit version number&lt;/em&gt;.  Web developers use all kinds of techniques for
parsing these strings, from custom code to using User-Agent parsing libraries,
which can then be used to determine the corresponding processing logic. The
User-Agent and any other version reporting mechanisms will soon report a
three-digit version number.&lt;/p&gt;
&lt;h3 id=&quot;version-100-timelines&quot;&gt;Version 100 timelines &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/chrome-firefox-100/#version-100-timelines&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Version 100 browsers will be first released in experimental versions (Chrome
Canary, Firefox Nightly), then beta versions, and then finally on the stable
channel.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Browser&lt;/th&gt;
&lt;th&gt;Timeline&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Chrome (&lt;a href=&quot;https://chromiumdash.appspot.com/schedule&quot;&gt;release
schedule&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;March 29, 2022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Firefox (&lt;a href=&quot;https://wiki.mozilla.org/Release_Management/Calendar&quot;&gt;release
schedule&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;May 3, 2022&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;why-can-a-three-digit-version-number-be-problematic&quot;&gt;Why can a three-digit version number be problematic? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/chrome-firefox-100/#why-can-a-three-digit-version-number-be-problematic&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When browsers first reached version 10 a little over 12 years ago,
&lt;a href=&quot;https://maqentaer.com/devopera-static-backup/http/dev.opera.com/articles/view/opera-ua-string-changes/index.html&quot; rel=&quot;noopener&quot;&gt;many issues were discovered&lt;/a&gt;
with User-Agent parsing libraries as the major version number went from one
digit to two.&lt;/p&gt;
&lt;p&gt;Without a single specification to follow,
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/User-Agent&quot; rel=&quot;noopener&quot;&gt;different browsers have different formats&lt;/a&gt;
for the User-Agent string, and site-specific User-Agent parsing. It&#39;s
possible that some parsing libraries may have hard-coded assumptions or bugs
that don&#39;t take into account three-digit major version numbers.  Many libraries
improved the parsing logic when browsers moved to two-digit version numbers, so
hitting the three-digit milestone is expected to cause fewer problems. &lt;a href=&quot;https://miketaylr.com/posts/2021/09/chrome-version-100-testing.html&quot; rel=&quot;noopener&quot;&gt;Mike
Taylor&lt;/a&gt;,
an engineer on the Chrome team, has done a survey of common UA parsing
libraries which didn&#39;t uncover any issues. Running Chrome experiments in the
field has surfaced some issues, which are being worked on.&lt;/p&gt;
&lt;h2 id=&quot;what-are-browsers-doing-about-it&quot;&gt;What are browsers doing about it? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/chrome-firefox-100/#what-are-browsers-doing-about-it&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Both Firefox and Chrome have been running experiments where current versions of
the browser report being at major version 100 in order to detect possible
website breakage. This has led to a few &lt;a href=&quot;https://github.com/webcompat/web-bugs/labels/version100&quot; rel=&quot;noopener&quot;&gt;reported issues&lt;/a&gt;,
some of which have already been &lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/detail?id=1273958&quot; rel=&quot;noopener&quot;&gt;fixed&lt;/a&gt;.
These experiments will continue to run until the release of version 100.&lt;/p&gt;
&lt;p&gt;There are also backup mitigation strategies in place, in case version 100
release to stable channels causes more damage to websites than anticipated.&lt;/p&gt;
&lt;h3 id=&quot;chrome-mitigation&quot;&gt;Chrome mitigation &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/chrome-firefox-100/#chrome-mitigation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In Chrome, the backup plan is to use a flag to freeze the major version at 99
and report the real major version number in the minor version part of the
User-Agent string (the code has already
&lt;a href=&quot;https://chromium-review.googlesource.com/c/chromium/src/+/3341658&quot; rel=&quot;noopener&quot;&gt;landed&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The Chrome version as reported in the User-Agent string follows the pattern
&lt;code&gt;&amp;lt;major_version&amp;gt;.&amp;lt;minor_version&amp;gt;.&amp;lt;build_number&amp;gt;.&amp;lt;patch_number&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If the backup plan is employed, then the User-Agent string would look like
this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;99.101.4988.0&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Chrome is also running experiments to ensure that reporting a three-digit value
in the minor version part of the string does not result in breakage, since the
minor version in the Chrome User-Agent string has reported 0 for a very long
time. The Chrome team will decide on whether to resort to the backup option
based on the number and severity of the issues reported.&lt;/p&gt;
&lt;h3 id=&quot;firefox-mitigation&quot;&gt;Firefox mitigation &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/chrome-firefox-100/#firefox-mitigation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In Firefox, the strategy will depend on how important the breakage is. Firefox
has a
&lt;a href=&quot;https://wiki.mozilla.org/Compatibility/Interventions_Releases&quot; rel=&quot;noopener&quot;&gt;site interventions mechanism&lt;/a&gt;.
Mozilla webcompat team can hot fix broken websites in Firefox using this
mechanism. If you type &lt;code&gt;about:compat&lt;/code&gt; in the Firefox URL bar, you can see what
is currently being fixed. If a site breaks with the major version being 100 on a
specific domain, it is possible to fix it by sending version 99 instead.&lt;/p&gt;
&lt;p&gt;If the breakage is widespread, it is possible to freeze the major version
number. There are then different possible strategies, each of them with their
pros and cons. Mozilla can send the real version number as a minor version
number, freeze the string entirely as-is, or send the real version number
through other parameters.&lt;/p&gt;
&lt;p&gt;Every strategy that adds complexity to the User-Agent string has a strong
impact on the ecosystem. Let&#39;s work together to avoid yet another quirky
behavior.&lt;/p&gt;
&lt;h2 id=&quot;what-can-you-do-to-help&quot;&gt;What can you do to help? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/chrome-firefox-100/#what-can-you-do-to-help&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In Chrome and Firefox Nightly, you can configure the browser to report the
version as 100 right now and report any issues you come across.&lt;/p&gt;
&lt;h3 id=&quot;configure-chrome-to-report-the-major-version-as-100&quot;&gt;Configure Chrome to report the major version as 100 &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/chrome-firefox-100/#configure-chrome-to-report-the-major-version-as-100&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Go to &lt;code&gt;chrome://flags/#force-major-version-to-100&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Set the option to &lt;code&gt;Enabled&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;configure-firefox-nightly-to-report-the-major-version-as-100&quot;&gt;Configure Firefox Nightly to report the major version as 100 &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/chrome-firefox-100/#configure-firefox-nightly-to-report-the-major-version-as-100&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Open Firefox Nightly&#39;s Settings menu.&lt;/li&gt;
&lt;li&gt;Search for &amp;quot;Firefox 100&amp;quot; and then check the &amp;quot;Firefox 100 User-Agent
String&amp;quot; option.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;test-and-file-reports&quot;&gt;Test and file reports &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/chrome-firefox-100/#test-and-file-reports&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;If you are a website maintainer&lt;/strong&gt;, test your website with Chrome
and Firefox 100. Review your User-Agent parsing code and libraries, and
ensure they are able to handle three-digit version numbers. We have
compiled some of the
&lt;a href=&quot;https://www.otsukare.info/2022/01/14/broken-ua-detection&quot; rel=&quot;noopener&quot;&gt;patterns that are currently breaking&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If you develop a User-Agent parsing library&lt;/strong&gt;, add tests to parse
versions greater than and equal to 100. Our early tests show that recent
versions of libraries can handle it correctly. However, the web has a long legacy,
so if you have old versions of parsing libraries, it&#39;s
time to check for issues and eventually upgrade.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If you are browsing the web&lt;/strong&gt; and notice any issues with the major
version 100,
&lt;a href=&quot;https://webcompat.com/issues/new?label=version100&quot; rel=&quot;noopener&quot;&gt;file a report on webcompat.com&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Ali Beyad</name>
    </author><author>
      <name>Karl Dubost</name>
    </author><author>
      <name>Milica Mihajlija</name>
    </author>
  </entry>
  
  <entry>
    <title>How CLS optimizations increased Yahoo! JAPAN News&#39;s page views per session by 15%</title>
    <link href="https://web.dev/yahoo-japan-news/"/>
    <updated>2021-03-09T00:00:00Z</updated>
    <id>https://web.dev/yahoo-japan-news/</id>
    <content type="html" mode="escaped">&lt;p&gt;&lt;a href=&quot;https://www.yahoo.co.jp/&quot; rel=&quot;noopener&quot;&gt;Yahoo! JAPAN&lt;/a&gt; is one of the largest media companies in Japan,
providing over 79 billion page views per month. Their news platform, &lt;a href=&quot;https://news.yahoo.co.jp/&quot; rel=&quot;noopener&quot;&gt;Yahoo!
JAPAN News&lt;/a&gt; has more than 22 billion page views per
month and an engineering team dedicated to improving the user experience.&lt;/p&gt;
&lt;p&gt;By continuously monitoring Core Web Vitals (CWV), they correlated the site&#39;s
improved &lt;a href=&quot;https://web.dev/cls&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt; score with a 15%
increase in page views per session and 13% increase in session duration.&lt;/p&gt;
&lt;div class=&quot;stats&quot;&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;0.2&lt;/p&gt;
    &lt;p&gt;CLS improvement&lt;/p&gt;
  &lt;/div&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;15.1&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;More page views per session&lt;/p&gt;
  &lt;/div&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;13.3&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;Longer session duration&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; &lt;a href=&quot;https://web.dev/cls&quot;&gt;Cumulative Layout Shift&lt;/a&gt; measures how visually stable a website is—it helps quantify how often users experience unexpected layout shifts. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Page content moving around unexpectedly often causes accidental clicks,
disorientation on the page, and ultimately user frustration. Frustrated users
tend not to stick around for long. To keep users happy, the page layout should
stay stable through the entire lifecycle of the user journey. For Yahoo! JAPAN
News this improvement had a significant positive impact on business critical
engagement metrics.&lt;/p&gt;
&lt;p&gt;For technical details on how they improved the CLS, read the
&lt;a href=&quot;https://techblog.yahoo.co.jp/entry/2021022230076263/&quot; rel=&quot;noopener&quot;&gt;Yahoo! JAPAN News engineering team&#39;s post&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;identifying-the-issue&quot;&gt;Identifying the issue &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/yahoo-japan-news/#identifying-the-issue&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Monitoring Core Web Vitals, including CLS, is crucial in catching issues and
identifying where they&#39;re coming from. At Yahoo! JAPAN News, &lt;a href=&quot;https://search.google.com/search-console/about&quot; rel=&quot;noopener&quot;&gt;Search
Console&lt;/a&gt; provided a great
overview of groups of pages with performance issues and
&lt;a href=&quot;https://web.dev/learn/#lighthouse&quot;&gt;Lighthouse&lt;/a&gt; helped identify per-page
opportunities to improve page experience. Using these tools, they discovered
that the article detail page had poor CLS.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Google Search Console Core Web Vitals Report showing high CLS for article details page.&quot; decoding=&quot;async&quot; height=&quot;719&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/GhORGAous9gyou5yTmHj.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/GhORGAous9gyou5yTmHj.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/GhORGAous9gyou5yTmHj.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/GhORGAous9gyou5yTmHj.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/GhORGAous9gyou5yTmHj.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/GhORGAous9gyou5yTmHj.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/GhORGAous9gyou5yTmHj.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/GhORGAous9gyou5yTmHj.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/GhORGAous9gyou5yTmHj.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/GhORGAous9gyou5yTmHj.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/GhORGAous9gyou5yTmHj.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/GhORGAous9gyou5yTmHj.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/GhORGAous9gyou5yTmHj.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/GhORGAous9gyou5yTmHj.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/GhORGAous9gyou5yTmHj.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/GhORGAous9gyou5yTmHj.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/GhORGAous9gyou5yTmHj.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/GhORGAous9gyou5yTmHj.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Google Search Console Core Web Vitals Report.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Lighthouse Avoid large layout shifts audit showing DOm elements that contribute the most to the CLS on the page.&quot; decoding=&quot;async&quot; height=&quot;265&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/3YwmCzVBl78a9TdOKu90.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/3YwmCzVBl78a9TdOKu90.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/3YwmCzVBl78a9TdOKu90.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/3YwmCzVBl78a9TdOKu90.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/3YwmCzVBl78a9TdOKu90.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/3YwmCzVBl78a9TdOKu90.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/3YwmCzVBl78a9TdOKu90.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/3YwmCzVBl78a9TdOKu90.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/3YwmCzVBl78a9TdOKu90.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/3YwmCzVBl78a9TdOKu90.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/3YwmCzVBl78a9TdOKu90.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/3YwmCzVBl78a9TdOKu90.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/3YwmCzVBl78a9TdOKu90.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/3YwmCzVBl78a9TdOKu90.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/3YwmCzVBl78a9TdOKu90.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/3YwmCzVBl78a9TdOKu90.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/3YwmCzVBl78a9TdOKu90.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/3YwmCzVBl78a9TdOKu90.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Lighthouse &quot;Avoid large layout shifts&quot; audit shows which elements are
    contributing to CLS score and how much.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It&#39;s important to keep in mind the &lt;em&gt;cumulative&lt;/em&gt; part of the Cumulative Layout
Shift—the score is captured through the entire page lifecycle. In the
real-world, the score can include shifts that happen as a result of user
interactions such as scrolling a page or tapping a button. To collect CLS scores
from the &lt;a href=&quot;https://web.dev/how-to-measure-speed/#lab-data-vs-field-data&quot;&gt;field
data&lt;/a&gt;, the team
integrated &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals/&quot; rel=&quot;noopener&quot;&gt;web-vitals&lt;/a&gt; JavaScript
library reporting.&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 a part of performance monitoring strategy, they&#39;re also working on building an internal tool with &lt;a href=&quot;https://web.dev/lighthouse-ci/&quot;&gt;Lighthouse CI&lt;/a&gt; to continuously audit performance across businesses in the company. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The team used Chrome DevTools to identify which elements were making layout
shifts on the page.
&lt;a href=&quot;https://developer.chrome.com/blog/new-in-devtools-77/&quot; rel=&quot;noopener&quot;&gt;Layout Shift Regions&lt;/a&gt;
in DevTools visualizes elements that contribute to CLS by highlighting them with
a blue rectangle whenever a layout shift happens.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Article details page with blue rectangles overlayed on the hero image and the text.&quot; decoding=&quot;async&quot; height=&quot;668&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 376px) 376px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/scvRgLxkZVQoGfOtjZ4z.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/scvRgLxkZVQoGfOtjZ4z.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/scvRgLxkZVQoGfOtjZ4z.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/scvRgLxkZVQoGfOtjZ4z.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/scvRgLxkZVQoGfOtjZ4z.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/scvRgLxkZVQoGfOtjZ4z.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/scvRgLxkZVQoGfOtjZ4z.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/scvRgLxkZVQoGfOtjZ4z.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/scvRgLxkZVQoGfOtjZ4z.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/scvRgLxkZVQoGfOtjZ4z.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/scvRgLxkZVQoGfOtjZ4z.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/scvRgLxkZVQoGfOtjZ4z.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/scvRgLxkZVQoGfOtjZ4z.png?auto=format&amp;w=752 752w&quot; width=&quot;376&quot; /&gt;
  &lt;figcaption&gt;
    Visualized layout shifts.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;They figured out that a layout shift occurred after the hero image at the top of
the article was loaded for the first view.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Screenshots of the article details page showing side by side comparison before and after layout shift.&quot; decoding=&quot;async&quot; height=&quot;455&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 798px) 798px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HdGiCh7O8ZhqcOkfVGEv.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HdGiCh7O8ZhqcOkfVGEv.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HdGiCh7O8ZhqcOkfVGEv.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HdGiCh7O8ZhqcOkfVGEv.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HdGiCh7O8ZhqcOkfVGEv.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HdGiCh7O8ZhqcOkfVGEv.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HdGiCh7O8ZhqcOkfVGEv.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HdGiCh7O8ZhqcOkfVGEv.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HdGiCh7O8ZhqcOkfVGEv.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HdGiCh7O8ZhqcOkfVGEv.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HdGiCh7O8ZhqcOkfVGEv.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HdGiCh7O8ZhqcOkfVGEv.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HdGiCh7O8ZhqcOkfVGEv.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HdGiCh7O8ZhqcOkfVGEv.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HdGiCh7O8ZhqcOkfVGEv.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HdGiCh7O8ZhqcOkfVGEv.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HdGiCh7O8ZhqcOkfVGEv.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/HdGiCh7O8ZhqcOkfVGEv.jpg?auto=format&amp;w=1596 1596w&quot; width=&quot;798&quot; /&gt;
  &lt;figcaption&gt;
    Layout shift on the article detail page.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In the example above, when the image finishes loading, the text gets pushed down
(the position change is indicated with the red line).&lt;/p&gt;
&lt;h2 id=&quot;improving-cls-for-images&quot;&gt;Improving CLS for images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/yahoo-japan-news/#improving-cls-for-images&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For fixed-size images, layout shifts can be prevented by specifying the &lt;code&gt;width&lt;/code&gt;
and &lt;code&gt;height&lt;/code&gt; attributes in the &lt;code&gt;img&lt;/code&gt; element and using the CSS
&lt;a href=&quot;https://web.dev/aspect-ratio&quot;&gt;&lt;code&gt;aspect-ratio&lt;/code&gt;&lt;/a&gt;
property available in modern browsers. However, Yahoo! JAPAN News needed to
support not only modern browsers, but also browsers installed in relatively old
operating systems such as iOS 9.&lt;/p&gt;
&lt;p&gt;They used &lt;a href=&quot;https://css-tricks.com/aspect-ratio-boxes/&quot; rel=&quot;noopener&quot;&gt;Aspect Ratio Boxes&lt;/a&gt;—a
method which uses markup to reserve the space on the page before the image is
loaded. This method requires knowing the aspect ratio of the image in advance,
which they were able to get from the backend API.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Screenshots of the article details page showing side by side comparison before and after CLS optimization.&quot; decoding=&quot;async&quot; height=&quot;439&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/gUbS3jB6zMBZwEU3wmW6.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/gUbS3jB6zMBZwEU3wmW6.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/gUbS3jB6zMBZwEU3wmW6.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/gUbS3jB6zMBZwEU3wmW6.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/gUbS3jB6zMBZwEU3wmW6.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/gUbS3jB6zMBZwEU3wmW6.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/gUbS3jB6zMBZwEU3wmW6.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/gUbS3jB6zMBZwEU3wmW6.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/gUbS3jB6zMBZwEU3wmW6.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/gUbS3jB6zMBZwEU3wmW6.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/gUbS3jB6zMBZwEU3wmW6.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/gUbS3jB6zMBZwEU3wmW6.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/gUbS3jB6zMBZwEU3wmW6.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/gUbS3jB6zMBZwEU3wmW6.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/gUbS3jB6zMBZwEU3wmW6.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/gUbS3jB6zMBZwEU3wmW6.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/gUbS3jB6zMBZwEU3wmW6.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/gUbS3jB6zMBZwEU3wmW6.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Left: reserved blank space for the image at the top of the page; right: the
    hero image loaded in the reserved space without layout shifts.
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;results&quot;&gt;Results &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/yahoo-japan-news/#results&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The number of URLs with poor performance in Search Console decreased by 98% and
CLS in lab data decreased from about 0.2 to 0. More importantly, there were
several
&lt;a href=&quot;https://nicj.net/cumulative-layout-shift-in-the-real-world/&quot; rel=&quot;noopener&quot;&gt;correlated improvements in business metrics&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Search Console report showing a significant drop in pages with performance issues.&quot; decoding=&quot;async&quot; height=&quot;509&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/Q5pRaIvOgzb4tqgv5dWo.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/Q5pRaIvOgzb4tqgv5dWo.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/Q5pRaIvOgzb4tqgv5dWo.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/Q5pRaIvOgzb4tqgv5dWo.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/Q5pRaIvOgzb4tqgv5dWo.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/Q5pRaIvOgzb4tqgv5dWo.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/Q5pRaIvOgzb4tqgv5dWo.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/Q5pRaIvOgzb4tqgv5dWo.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/Q5pRaIvOgzb4tqgv5dWo.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/Q5pRaIvOgzb4tqgv5dWo.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/Q5pRaIvOgzb4tqgv5dWo.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/Q5pRaIvOgzb4tqgv5dWo.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/Q5pRaIvOgzb4tqgv5dWo.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/Q5pRaIvOgzb4tqgv5dWo.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/Q5pRaIvOgzb4tqgv5dWo.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/Q5pRaIvOgzb4tqgv5dWo.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/Q5pRaIvOgzb4tqgv5dWo.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/Q5pRaIvOgzb4tqgv5dWo.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Search Console after improvements.
  &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; Search Console doesn&#39;t reflect improvements in real-time. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;When Yahoo! JAPAN News compared user engagement metrics before and after CLS
optimization, they saw multiple improvements:&lt;/p&gt;
&lt;div class=&quot;stats&quot;&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;15.1&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;More page views per session&lt;/p&gt;
  &lt;/div&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;13.3&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;Longer session duration&lt;/p&gt;
  &lt;/div&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;1.72&lt;sub&gt;%*&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;Lower bounce rate (*percentage points)&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;By improving CLS and other Core Web Vitals metrics, Yahoo! JAPAN News also got
the
&lt;a href=&quot;https://blog.chromium.org/2020/08/highlighting-great-user-experiences-on.html&quot; rel=&quot;noopener&quot;&gt;&amp;quot;Fast page&amp;quot; label&lt;/a&gt;
in the context menu of Chrome Android.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Fast page label in Chrome on Android.&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 400px) 400px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/ivyeENjT9NKZLhdn9WJm.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/ivyeENjT9NKZLhdn9WJm.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/ivyeENjT9NKZLhdn9WJm.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/ivyeENjT9NKZLhdn9WJm.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/ivyeENjT9NKZLhdn9WJm.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/ivyeENjT9NKZLhdn9WJm.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/ivyeENjT9NKZLhdn9WJm.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/ivyeENjT9NKZLhdn9WJm.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/ivyeENjT9NKZLhdn9WJm.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/ivyeENjT9NKZLhdn9WJm.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/ivyeENjT9NKZLhdn9WJm.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/ivyeENjT9NKZLhdn9WJm.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/vgdbNJBYHma2o62ZqYmcnkq3j0o1/ivyeENjT9NKZLhdn9WJm.png?auto=format&amp;w=800 800w&quot; width=&quot;400&quot; /&gt;
  &lt;figcaption&gt;
    &quot;Fast page&quot; label in Chrome on Android.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Layout shifts are frustrating and discourage users from reading more pages, but
that can be improved by using the appropriate tools, identifying issues, and
applying best practices. Improving CLS is a chance to improve your business.&lt;/p&gt;
&lt;p&gt;For more information, read the
&lt;a href=&quot;https://techblog.yahoo.co.jp/entry/2021022230076263/&quot; rel=&quot;noopener&quot;&gt;Yahoo! JAPAN engineering team&#39;s post&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Shunya Shishido</name>
    </author><author>
      <name>Tomoki Kiraku</name>
    </author><author>
      <name>Milica Mihajlija</name>
    </author>
  </entry>
  
  <entry>
    <title>Life of a payment transaction</title>
    <link href="https://web.dev/life-of-a-payment-transaction/"/>
    <updated>2020-05-25T00:00:00Z</updated>
    <id>https://web.dev/life-of-a-payment-transaction/</id>
    <content type="html" mode="escaped">&lt;p&gt;Web Payments APIs are dedicated payment features built into the browser
for the first time. With Web Payments, merchant integration with payment apps
becomes simpler while the customer experience gets streamlined and more secure.&lt;/p&gt;
&lt;p&gt;To learn more about the benefits of using Web Payments check out &lt;a href=&quot;https://web.dev/empowering-payment-apps-with-web-payments&quot;&gt;Empowering
payment apps with Web Payments&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This article walks you through a payment transaction on a merchant website and
helps you understand how payment app integration works.&lt;/p&gt;
&lt;p&gt;The process involves 6 steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The merchant initiates a payment transaction.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The merchant shows a payment button.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The customer presses the payment button.&lt;/p&gt;
 &lt;img alt=&quot;A diagram of a cheese shop website with a BobPay (payment app) button.&quot; decoding=&quot;async&quot; height=&quot;298&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/NQIh5tt5wsQFC5yKLCaU.svg&quot; width=&quot;786&quot; /&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The browser launches the payment app.&lt;/p&gt;
 &lt;img alt=&quot;A diagram of the cheese shop website with BobPay app launched in a modal. The modal shows shipping options and total cost.&quot; decoding=&quot;async&quot; height=&quot;366&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/IHztIcfJKeWDUIkugTkb.svg&quot; width=&quot;671&quot; /&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the customer changes any details (such as shipping options or their
address), the merchant updates the transaction details reflecting the change.&lt;/p&gt;
 &lt;img alt=&quot;A diagram showing the customer choosing a different shipping option in BobPay app modal. A second diagram showing the merchant updating the total cost displayed in BobPay.&quot; decoding=&quot;async&quot; height=&quot;702&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/BR9Od63aOdG9CaaD1z7K.svg&quot; width=&quot;777&quot; /&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After the customer confirms the purchase, the merchant validates the payment
and completes the transaction.&lt;/p&gt;
 &lt;img alt=&quot;A diagram showing the customer pressing the &amp;quot;Pay&amp;quot; button in BobPay, followed by a diagram of the cheese shop page showing &amp;quot;Payment accepted&amp;quot;.&quot; decoding=&quot;async&quot; height=&quot;708&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9Q6VqimbOxJ3ZXHvB8ry.svg&quot; width=&quot;778&quot; /&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;step-1-the-merchant-initiates-a-payment-transaction&quot;&gt;Step 1: The merchant initiates a payment transaction &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/life-of-a-payment-transaction/#step-1-the-merchant-initiates-a-payment-transaction&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When a customer decides to make a purchase, the merchant initiates the payment
transaction by constructing a
&lt;a href=&quot;https://developers.google.com/web/fundamentals/payments/basics/how-payment-request-api-works&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;PaymentRequest&lt;/code&gt;&lt;/a&gt;
object. This object includes important information about the transaction:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Acceptable payment methods and their data to process the transaction.&lt;/li&gt;
&lt;li&gt;Details, such as the total price (required) and information about the items.&lt;/li&gt;
&lt;li&gt;Options in which merchants can request shipping information such as a shipping
address and a shipping option.&lt;/li&gt;
&lt;li&gt;Merchants can also request the billing address, the payer&#39;s name, email, and
phone number.&lt;/li&gt;
&lt;li&gt;Merchants can also include optional &lt;a href=&quot;https://developers.google.com/web/fundamentals/payments/merchant-guide/deep-dive-into-payment-request#changing_the_shipping_type&quot; rel=&quot;noopener&quot;&gt;shipping
type&lt;/a&gt;
(&lt;code&gt;shipping&lt;/code&gt;, &lt;code&gt;delivery&lt;/code&gt;, or &lt;code&gt;pickup&lt;/code&gt;) in the &lt;code&gt;PaymentRequest&lt;/code&gt;. The payment app
can use that as a hint to display the correct labels in its UI.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; request &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;PaymentRequest&lt;/span&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;supportedMethods&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;https://bobpay.xyz/pay&#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;data&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 literal-property property&quot;&gt;transactionId&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;****&#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;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;displayItems&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Anvil L/S Crew Neck - Grey M x1&#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;amount&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 literal-property property&quot;&gt;currency&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;USD&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;22.15&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;total&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 literal-property property&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Total due&#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;amount&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 literal-property property&quot;&gt;currency&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;USD&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;22.15&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;requestShipping&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;requestBillingAddress&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;requestPayerEmail&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;requestPayerPhone&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;requestPayerName&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;shippingType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;delivery&#39;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;details&gt;
&lt;summary&gt;
  Including a transaction ID
&lt;/summary&gt;
&lt;p&gt;Some payment handlers may require the merchant to provide the transaction ID
which they have issued in advance as part of the transaction information. A
typical integration includes communication between the merchant&#39;s and the
payment handler&#39;s server to reserve the total price. This prevents malicious
customers from manipulating the price and cheating the merchant with a
validation at the end of the transaction.&lt;/p&gt;
&lt;p&gt;The merchant can pass a transaction ID as part of the
&lt;a href=&quot;https://www.w3.org/TR/payment-request/#dom-paymentmethoddata&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;PaymentMethodData&lt;/code&gt;&lt;/a&gt;
object&#39;s &lt;code&gt;data&lt;/code&gt; property.&lt;/p&gt;
&lt;/details&gt;
&lt;p&gt;Provided the transaction information, the browser goes through a discovery
process of payment apps specified in the &lt;code&gt;PaymentRequest&lt;/code&gt; based on the payment
method identifiers. This way, the browser can determine the payment app to
launch as soon as the merchant is ready to proceed with the transaction.&lt;/p&gt;
&lt;p&gt;To learn how the discovery process works in detail check out &lt;a href=&quot;https://web.dev/setting-up-a-payment-method#how-a-browser-discovers-a-payment-app&quot;&gt;Setting up a
payment
method&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;step-2-the-merchant-shows-a-payment-button&quot;&gt;Step 2: The merchant shows a payment button &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/life-of-a-payment-transaction/#step-2-the-merchant-shows-a-payment-button&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Merchants can support many payment methods, but should only present the payment
buttons for those that a customer can actually use. Showing a payment button
that is unusable is poor user experience. If a merchant can predict that a
payment method specified in the &lt;code&gt;PaymentRequest&lt;/code&gt; object won&#39;t work for the
customer, they can provide a fallback solution or not show that button at all.&lt;/p&gt;
&lt;p&gt;Using a &lt;code&gt;PaymentRequest&lt;/code&gt; instance, a merchant can query whether a customer has
the payment app available.&lt;/p&gt;
&lt;h3 id=&quot;does-the-customer-have-the-payment-app-available&quot;&gt;Does the customer have the payment app available? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/life-of-a-payment-transaction/#does-the-customer-have-the-payment-app-available&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/PaymentRequest/canMakePayment&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;canMakePayment()&lt;/code&gt;&lt;/a&gt;
method of &lt;code&gt;PaymentRequest&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt; if a payment app is available on the
customer&#39;s device. &amp;quot;Available&amp;quot; means that a payment app that supports the
payment method is discovered, and that the platform-specific payment app is installed, or
the web-based payment app is &lt;a href=&quot;https://web.dev/setting-up-a-payment-method#jit-register&quot;&gt;ready to be
registered&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;const&lt;/span&gt; canMakePayment &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;canMakePayment&lt;/span&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;canMakePayment&lt;span 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;// Fallback to other means of payment or hide the button.&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;h2 id=&quot;show&quot;&gt;Step 3: The customer presses the payment button &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/life-of-a-payment-transaction/#show&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When the customer presses the payment button, the merchant calls the &lt;code&gt;show()&lt;/code&gt;
method of the &lt;code&gt;PaymentRequest&lt;/code&gt; instance which immediately triggers the launch of
the payment UI.&lt;/p&gt;
&lt;p&gt;In case the final total price is set dynamically (for example, retrieved from a
server), the merchant can defer the launch of the payment UI until the total is
known.&lt;/p&gt;
&lt;h3 id=&quot;deferring-the-launch-of-the-payment-ui&quot;&gt;Deferring the launch of the payment UI &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/life-of-a-payment-transaction/#deferring-the-launch-of-the-payment-ui&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Check out a demo of &lt;a href=&quot;https://rsolomakhin.github.io/pr/wait/&quot; rel=&quot;noopener&quot;&gt;deferring the payment
UI&lt;/a&gt; until the final total price is
determined.&lt;/p&gt;
&lt;p&gt;To defer the payment UI, the merchant passes a promise to the &lt;code&gt;show()&lt;/code&gt; method.
The browser will show a loading indicator until the promise resolves and the
transaction is ready to begin.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;getTotalAmount&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&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;// Fetch the total amount from the server, etc.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;try&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; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTotalAmount&lt;/span&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;// Process the result…&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;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span 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;handleError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span 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;If there is no promise specified as an argument for &lt;code&gt;show()&lt;/code&gt;, the browser will
launch the payment UI immediately.&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 the payment handler is designed to return a transaction ID upon setting the total price, the ID can be passed as part of the promise result. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;launch&quot;&gt;Step 4: The browser launches the payment app &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/life-of-a-payment-transaction/#launch&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The browser can launch a platform-specific or a web-based payment app. (You can learn more
about &lt;a href=&quot;https://web.dev/setting-up-a-payment-method#how-chrome-determines-which-payment-app-to-launch&quot;&gt;how Chrome determines which payment app to
launch&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;How the payment app is built is up to the developer for the most part, but the
events emitted from and to the merchant, as well as the structure of the data
passed along with those events, are standardized.&lt;/p&gt;
&lt;p&gt;When the payment app is launched, it receives &lt;a href=&quot;https://w3c.github.io/payment-handler/#the-paymentrequestevent&quot; rel=&quot;noopener&quot;&gt;the transaction
information&lt;/a&gt;
passed to the &lt;code&gt;PaymentRequest&lt;/code&gt; object in Step 1, which includes the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Payment method data&lt;/li&gt;
&lt;li&gt;Total price&lt;/li&gt;
&lt;li&gt;Payment options&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The payment app uses the transaction information to label its UI.&lt;/p&gt;
&lt;h2 id=&quot;step-5-how-a-merchant-can-update-the-transaction-details-depending-on-customers-actions&quot;&gt;Step 5: How a merchant can update the transaction details depending on customer&#39;s actions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/life-of-a-payment-transaction/#step-5-how-a-merchant-can-update-the-transaction-details-depending-on-customers-actions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Customers have an option to change the transaction details such as payment
method and shipping option in the payment app. While the customer makes changes,
the merchant receives the change events and updates the transaction details.&lt;/p&gt;
&lt;p&gt;There are four types of events a merchant can receive:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Payment method change event&lt;/li&gt;
&lt;li&gt;Shipping address change event&lt;/li&gt;
&lt;li&gt;Shipping option change event&lt;/li&gt;
&lt;li&gt;Merchant validation event&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;payment-method-change-event&quot;&gt;Payment method change event &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/life-of-a-payment-transaction/#payment-method-change-event&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A payment app can support multiple payment methods and a merchant may offer a
special discount depending on the customer&#39;s selection. To cover this use case,
the payment method change event can inform the merchant of the new payment
method so that they can update the total price with the discount and return it
back to the payment app.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;request&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;paymentmethodchange&#39;&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;updateWith&lt;/span&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;// Add discount etc.&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;h3 id=&quot;shipping-address-change-event&quot;&gt;Shipping address change event &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/life-of-a-payment-transaction/#shipping-address-change-event&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A payment app can optionally provide the customer&#39;s shipping address. This is
convenient for customers because they don&#39;t have to manually enter any details
into a form and they can store their shipping address in their preferred payment
apps, rather than on multiple different merchant websites.&lt;/p&gt;
&lt;p&gt;If a customer updates their shipping address in a payment app after the
transaction has been initiated, a &lt;code&gt;&#39;shippingaddresschange&#39;&lt;/code&gt; event will be sent
to the merchant. This event helps the merchant determine the shipping cost based
on the new address, update the total price, and return it to the payment app.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;request&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;shippingaddresschange&#39;&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;updateWith&lt;/span&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;// Update the details&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;If the merchant can&#39;t ship to the updated address, they can provide an error
message by adding an error parameter to the transaction details returned to the
payment app.&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; Merchants don&#39;t receive customers&#39; full shipping address until they&#39;ve authorized the payment. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;shipping-option-change-event&quot;&gt;Shipping option change event &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/life-of-a-payment-transaction/#shipping-option-change-event&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A merchant can offer multiple shipping options to the customer and can delegate
that choice to the payment app. The shipping options are displayed as a list of
prices and service names the customer can select from. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Standard shipping - Free&lt;/li&gt;
&lt;li&gt;Express shipping - 5 USD&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When a customer updates the shipping option in a payment app, a
&lt;code&gt;&#39;shippingoptionchange&#39;&lt;/code&gt; event will be sent to the merchant. The merchant can
then determine the shipping cost, update the total price, and return it to the
payment app.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;request&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;shippingoptionchange&#39;&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;updateWith&lt;/span&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;// Update the details&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 merchant can dynamically modify the shipping options based on the customer&#39;s
shipping address as well. This is useful when a merchant wants to offer
different sets of shipping options for domestic and international customers.&lt;/p&gt;
&lt;h3 id=&quot;merchant-validation-event&quot;&gt;Merchant validation event &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/life-of-a-payment-transaction/#merchant-validation-event&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For additional security, a payment app can perform a merchant validation before
proceeding to the payment flow. The design of the validation mechanism is up to
the payment app, but the merchant validation event serves to inform the merchant
of the URL they can use to validate themselves.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;request&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;merchantvalidation&#39;&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;updateWith&lt;/span&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;// Use `e.validateURL` to validate&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;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The support for the merchant validation event is limited to Apple Safari. Chromium-based browsers have not implemented this event as of May 2020. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;step-6-the-merchant-validates-the-payment-and-completes-the-transaction&quot;&gt;Step 6: The merchant validates the payment and completes the transaction &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/life-of-a-payment-transaction/#step-6-the-merchant-validates-the-payment-and-completes-the-transaction&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When the customer successfully authorizes the payment, the &lt;code&gt;show()&lt;/code&gt; method
returns a promise that resolves to a
&lt;a href=&quot;https://w3c.github.io/payment-request/#paymentresponse-interface&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;PaymentResponse&lt;/code&gt;&lt;/a&gt;.
The &lt;code&gt;PaymentResponse&lt;/code&gt; object includes the following information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Payment result details&lt;/li&gt;
&lt;li&gt;Shipping address&lt;/li&gt;
&lt;li&gt;Shipping option&lt;/li&gt;
&lt;li&gt;Contact information&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At this point, the browser UI may still show a loading indicator meaning that
the transaction is not completed yet.&lt;/p&gt;
&lt;p&gt;If the payment app is terminated because of a payment failure or error, the
promise returned from &lt;code&gt;show()&lt;/code&gt; rejects, and the browser terminates the payment
transaction.&lt;/p&gt;
&lt;h3 id=&quot;processing-and-validating-the-payment&quot;&gt;Processing and validating the payment &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/life-of-a-payment-transaction/#processing-and-validating-the-payment&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;details&lt;/code&gt; in &lt;code&gt;PaymentResponse&lt;/code&gt; is the payment credential object returned
from the payment app. The merchant can use the credential to process or validate
the payment. How this critical process works is up to the payment handler.&lt;/p&gt;
&lt;h3 id=&quot;completing-or-retrying-the-transaction&quot;&gt;Completing or retrying the transaction &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/life-of-a-payment-transaction/#completing-or-retrying-the-transaction&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;After the merchant determines whether the transaction has succeded or failed,
they can either:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Call the &lt;code&gt;.complete()&lt;/code&gt; method to complete the transaction and dismiss the
loading indicator.&lt;/li&gt;
&lt;li&gt;Let the customer retry by calling the &lt;code&gt;retry()&lt;/code&gt; method.&lt;/li&gt;
&lt;/ul&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;doPaymentRequest&lt;/span&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;try&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; request &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;PaymentRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;methodData&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; details&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 keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;show&lt;/span&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;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;validateResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span 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;catch&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;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// AbortError, SecurityError&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;error&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;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;&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;validateResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&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;try&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; errors &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;checkAllValuesAreGood&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span 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;errors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span 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;await&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;retry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;errors&lt;span 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 function&quot;&gt;validateResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;complete&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&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;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Something went wrong…&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;complete&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;fail&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Must be called as a result of a click&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// or some explicit user action.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;doPaymentRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;next-steps&quot;&gt;Next Steps &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/life-of-a-payment-transaction/#next-steps&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Learn how to declare a payment method identifier in detail in &lt;a href=&quot;https://web.dev/setting-up-a-payment-method&quot;&gt;Setting up a
payment method&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Learn how to build a platform-specific payment app in
&lt;a href=&quot;https://web.dev/android-payment-apps-developers-guide&quot;&gt;Android payment apps developer&#39;s guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Learn how to build a web-based payment app in &lt;a href=&quot;https://web.dev/web-based-payment-apps-overview&quot;&gt;Web-based payment apps developer&#39;s
guide&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Eiji Kitamura</name>
    </author><author>
      <name>Milica Mihajlija</name>
    </author>
  </entry>
  
  <entry>
    <title>Adaptive loading: improving web performance on slow devices</title>
    <link href="https://web.dev/adaptive-loading-cds-2019/"/>
    <updated>2019-12-16T00:00:00Z</updated>
    <id>https://web.dev/adaptive-loading-cds-2019/</id>
    <content type="html" mode="escaped">&lt;p&gt;Device capabilities and network connections vary a lot. Sites that delight users
on high-end devices can be
&lt;a href=&quot;https://v8.dev/blog/cost-of-javascript-2019&quot; rel=&quot;noopener&quot;&gt;unusable&lt;/a&gt; on low-end ones. Sites
that load smoothly on fast networks can come to a halt on slow ones. Any user
can experience a slow website, that&#39;s why developing &amp;quot;one-size fits all&amp;quot;
solutions may not always work.&lt;/p&gt;
&lt;p&gt;In their &lt;a href=&quot;https://www.youtube.com/watch?v=puUPpVrIRkc&quot; rel=&quot;noopener&quot;&gt;Chrome Dev Summit talk&lt;/a&gt;,
Addy Osmani from Google and Nate Schloss from Facebook explore a solution to that problem—a
pattern for delivering pages that better cater to a variety of user
constraints. They call it &lt;em&gt;&lt;strong&gt;adaptive loading&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&quot;what-is-adaptive-loading&quot;&gt;What is adaptive loading? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/adaptive-loading-cds-2019/#what-is-adaptive-loading&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Adaptive loading involves delivering different experiences to different users
based on their network and hardware constraints, specifically:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A fast core experience for all users (including low-end devices).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Progressively adding high-end-only features, if a user&#39;s network and hardware
can handle it.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By optimizing for specific hardware and network constraints you enable every user to get the best possible experience for their device. Tailoring the experience to users&#39; constraints can include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Serving low-quality images and videos on slow networks.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Throttling the frame-rate of animations on low-end devices.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Avoiding computationally expensive operations on low-end devices.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Blocking third-party scripts on slower devices.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Loading non-critical JavaScript for interactivity only on fast CPUs.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;browser-support-and-how-to-implement-adaptive-loading&quot;&gt;Browser support and how to implement adaptive loading &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/adaptive-loading-cds-2019/#browser-support-and-how-to-implement-adaptive-loading&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The signals you can use for adaptive loading are listed below. Browser support is also included for each signal:&lt;/p&gt;
&lt;h3 id=&quot;navigatordevicememory&quot;&gt;&lt;code&gt;Navigator.deviceMemory&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/adaptive-loading-cds-2019/#navigatordevicememory&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Navigator/deviceMemory&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;navigator.deviceMemory&lt;/code&gt;&lt;/a&gt; property is used to reduce memory consumption on low-end devices. &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;p&gt;&lt;/p&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 63, 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;
      63
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox, Not supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Edge 79, Supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
79
&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Safari, Not supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;p&gt;&lt;/p&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/Navigator/deviceMemory#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;h3 id=&quot;navigatorhardwareconcurrency&quot;&gt;&lt;code&gt;Navigator.hardwareConcurrency&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/adaptive-loading-cds-2019/#navigatorhardwareconcurrency&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/NavigatorConcurrentHardware/hardwareConcurrency&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;navigator.hardwareConcurrency&lt;/code&gt;&lt;/a&gt; property is the CPU core count. It is used to limit costly JavaScript execution and reduce CPU intensive logic when a device can&#39;t handle it well. &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;p&gt;&lt;/p&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 37, 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;
      37
    &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 48, 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;
      48
    &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 15, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      15
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Safari, Not supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;p&gt;&lt;/p&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/Navigator/hardwareConcurrency#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;h3 id=&quot;networkinformationeffectivetype&quot;&gt;&lt;code&gt;NetworkInformation.effectiveType&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/adaptive-loading-cds-2019/#networkinformationeffectivetype&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/NetworkInformation/effectiveType&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;navigator.connection.effectiveType&lt;/code&gt;&lt;/a&gt; property is used to fine-tune data transfer to use less bandwidth. &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;p&gt;&lt;/p&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 61, 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;
      61
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox, Not supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Edge 79, Supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
79
&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Safari, Not supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;p&gt;&lt;/p&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/NetworkInformation/effectiveType#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;h3 id=&quot;networkinformationsavedata&quot;&gt;&lt;code&gt;NetworkInformation.saveData&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/adaptive-loading-cds-2019/#networkinformationsavedata&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://web.dev/optimizing-content-efficiency-save-data/&quot;&gt;&lt;code&gt;navigator.connection.saveData&lt;/code&gt;&lt;/a&gt; property is used to leverage the user&#39;s Data Saver preferences. &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;p&gt;&lt;/p&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 49, 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;
      49
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox, Not supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Edge ≤79, Supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
≤79
&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Safari, Not supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;p&gt;&lt;/p&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Save-Data#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;There are two places where you can make a decision about what to serve to users:
the client and the server. On the client, you have the JavaScript APIs noted
above. On the server, you can use &lt;a href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints/&quot;&gt;client
hints&lt;/a&gt;
to get insight into the user&#39;s device capabilities and the network they&#39;re
connected to.&lt;/p&gt;
&lt;h3 id=&quot;adaptive-loading-in-react&quot;&gt;Adaptive loading in React &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/adaptive-loading-cds-2019/#adaptive-loading-in-react&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/GoogleChromeLabs/react-adaptive-hooks&quot; rel=&quot;noopener&quot;&gt;React Adaptive Loading Hooks &amp;amp;
Utilities&lt;/a&gt; is a suite
for the React ecosystem that makes it easier to adapt your sites to lower-end
devices. It includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;useNetworkStatus()&lt;/code&gt; hook for adapting based on network status (&lt;code&gt;slow-2g&lt;/code&gt;,
&lt;code&gt;2g&lt;/code&gt;, &lt;code&gt;3g&lt;/code&gt;, or &lt;code&gt;4g&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;useSaveData()&lt;/code&gt; hook for adapting based on the user&#39;s Data Saver
preferences.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;useHardwareConcurrency()&lt;/code&gt; hook for adapting based on the number of
logical CPU processor cores on the user&#39;s device.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;useMemoryStatus()&lt;/code&gt; hook for adapting based on the user&#39;s device memory
(RAM).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each hook accepts an optional argument for setting the initial value. This
option is useful in two scenarios: when the user&#39;s browser does not support the
relevant API and for server-side rendering where you can use the client hint
data to set the initial value on the server. For example, the
&lt;code&gt;useNetworkStatus()&lt;/code&gt; hook can use the initial value passed from client hint for
server-side rendering and, when executed on the client, update itself if the
network effective type changes.&lt;/p&gt;
&lt;p&gt;React Adaptive Loading Hooks &amp;amp; Utilities are implemented using web platform APIs
(&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Network_Information_API&quot; rel=&quot;noopener&quot;&gt;Network
Information&lt;/a&gt;,
&lt;a href=&quot;https://developer.chrome.com/blog/device-memory/&quot; rel=&quot;noopener&quot;&gt;Device Memory&lt;/a&gt;
and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/NavigatorConcurrentHardware/hardwareConcurrency&quot; rel=&quot;noopener&quot;&gt;Hardware
Concurrency&lt;/a&gt;).
You can use the same APIs to apply adaptive loading concepts to other frameworks
and libraries, such as
&lt;a href=&quot;https://netbasal.com/connection-aware-components-in-angular-3a66bb0bab6f&quot; rel=&quot;noopener&quot;&gt;Angular&lt;/a&gt;,
&lt;a href=&quot;https://dev.to/vorillaz/serving-adaptive-components-using-the-network-information-api-lbo&quot; rel=&quot;noopener&quot;&gt;Vue&lt;/a&gt;,
and others.&lt;/p&gt;
&lt;h2 id=&quot;adaptive-loading-in-action&quot;&gt;Adaptive loading in action &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/adaptive-loading-cds-2019/#adaptive-loading-in-action&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This section explores demos of how you could use adaptive loading and real-world
examples from sites such as Facebook, eBay, Tinder, and others.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://adaptive-loading.web.app/react-movie-network-aware-loading/&quot; rel=&quot;noopener&quot;&gt;React
Movie&lt;/a&gt; demo
shows how to &lt;a href=&quot;https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/react-movie-network-aware-loading&quot; rel=&quot;noopener&quot;&gt;adapt media serving based on the network
status&lt;/a&gt;.
It&#39;s an application for browsing movies that shows posters, summaries, and cast
lists. Based on the user&#39;s effective connection type, it serves high-quality
posters on fast connections and low-quality posters on slow ones.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/twittersupport/status/1047607749708668928&quot; rel=&quot;noopener&quot;&gt;Twitter has a Data Saver
mode&lt;/a&gt; designed to
reduce the amount of data used. In this mode, preview images load in
low-resolution and large images load only when you tap on the preview. With this
option enabled, users on iOS and Android saved 50% in data-usage from images,
and users on the web saved 80%. Here&#39;s a React
&lt;a href=&quot;https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/react-twitter-save-data-loading(hook)&quot; rel=&quot;noopener&quot;&gt;demo&lt;/a&gt;
that uses the Save Data hook to replicate the Twitter timeline. Try
opening your DevTools &lt;strong&gt;Network&lt;/strong&gt; panel and looking at the difference in the amount
of data transferred as you scroll while Save Data is disabled versus when it&#39;s
enabled.&lt;/p&gt;
  &lt;figure&gt;
    &lt;video controls=&quot;&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot;&gt;
      &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/adaptive-loading-cds-2019/twitter-save-data.mp4&quot; type=&quot;video/mp4&quot; /&gt;
    &lt;/video&gt;
     &lt;figcaption&gt;
      A screencast comparing scrolling the Twitter timeline with Data Saver on and off. With Data Saver on, only image previews are loaded and videos don&#39;t autoplay.
    &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;p&gt;eBay conditionally turns on and off features like zooming when a user&#39;s hardware
or network conditions don&#39;t support them well. You can achieve this through
adaptive &lt;a href=&quot;https://web.dev/reduce-javascript-payloads-with-code-splitting/&quot;&gt;code-splitting&lt;/a&gt; and
code loading—a way to conditionally load more highly interactive components or
run more computationally heavy operations on high-end devices, while not sending
those scripts down to users on slower devices. Check out the video at &lt;a href=&quot;https://youtu.be/puUPpVrIRkc?t=973&quot; rel=&quot;noopener&quot;&gt;16
mins&lt;/a&gt; where Addy shows this pattern
implemented with &lt;a href=&quot;https://web.dev/code-splitting-suspense/&quot;&gt;React.lazy() and Suspense&lt;/a&gt; on a
&lt;a href=&quot;https://github.com/GoogleChromeLabs/adaptive-loading/tree/master/react-ebay-network-aware-code-splitting&quot; rel=&quot;noopener&quot;&gt;demo eBay product
page&lt;/a&gt;.&lt;/p&gt;
&lt;img alt=&quot;A diagram of modules shipped for a product page on low-end and high-end devices: both versions include &amp;quot;image viewer&amp;quot;, while the high-end version includes additional &amp;quot;zoom&amp;quot; and &amp;quot;carousel&amp;quot; modules.&quot; decoding=&quot;async&quot; height=&quot;446&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gdXBknVxIdd8FcSvIrxw.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gdXBknVxIdd8FcSvIrxw.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gdXBknVxIdd8FcSvIrxw.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gdXBknVxIdd8FcSvIrxw.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gdXBknVxIdd8FcSvIrxw.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gdXBknVxIdd8FcSvIrxw.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gdXBknVxIdd8FcSvIrxw.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gdXBknVxIdd8FcSvIrxw.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gdXBknVxIdd8FcSvIrxw.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gdXBknVxIdd8FcSvIrxw.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gdXBknVxIdd8FcSvIrxw.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gdXBknVxIdd8FcSvIrxw.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gdXBknVxIdd8FcSvIrxw.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gdXBknVxIdd8FcSvIrxw.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gdXBknVxIdd8FcSvIrxw.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gdXBknVxIdd8FcSvIrxw.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gdXBknVxIdd8FcSvIrxw.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gdXBknVxIdd8FcSvIrxw.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Tinder is using a number of adaptive loading patterns in its
&lt;a href=&quot;https://medium.com/@addyosmani/a-tinder-progressive-web-app-performance-case-study-78919d98ece0&quot; rel=&quot;noopener&quot;&gt;web&lt;/a&gt;
and &lt;a href=&quot;https://blog.gotinder.com/introducing-tinder-lite/&quot; rel=&quot;noopener&quot;&gt;Lite app&lt;/a&gt; to keep the
experience fast for everyone. If a user is on a slow network or has Data Saver
enabled, they disable video autoplay, limit &lt;a href=&quot;https://web.dev/link-prefetch/&quot;&gt;route prefetching&lt;/a&gt;
and limit loading the next image in the carousel to loading images one at a time
as users swipe. After implementing these optimizations, they&#39;ve seen significant
improvements in average swipe count in countries such as Indonesia.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of two versions of Tinder chat: with autoplaying video and with a video with play button overlay. A screenshot of a Tinder profile with caption &amp;#x27;Limit carousel images on Data Saver or 3G&amp;#x27;. A code snippet for prefetching in-viewport videos only on 4G.&quot; decoding=&quot;async&quot; height=&quot;445&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/N1xJkEMQ9rE513TNm8va.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/N1xJkEMQ9rE513TNm8va.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/N1xJkEMQ9rE513TNm8va.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/N1xJkEMQ9rE513TNm8va.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/N1xJkEMQ9rE513TNm8va.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/N1xJkEMQ9rE513TNm8va.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/N1xJkEMQ9rE513TNm8va.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/N1xJkEMQ9rE513TNm8va.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/N1xJkEMQ9rE513TNm8va.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/N1xJkEMQ9rE513TNm8va.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/N1xJkEMQ9rE513TNm8va.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/N1xJkEMQ9rE513TNm8va.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/N1xJkEMQ9rE513TNm8va.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/N1xJkEMQ9rE513TNm8va.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/N1xJkEMQ9rE513TNm8va.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/N1xJkEMQ9rE513TNm8va.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/N1xJkEMQ9rE513TNm8va.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/N1xJkEMQ9rE513TNm8va.png?auto=format&amp;w=1600 1600w&quot; style=&quot;max-width: 75%&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;adaptive-loading-at-facebook&quot;&gt;Adaptive loading at Facebook &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/adaptive-loading-cds-2019/#adaptive-loading-at-facebook&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One issue that comes up in adaptive loading is grouping devices into high-end
and low-end classes based on available signals. On mobile devices the
&lt;a href=&quot;https://developer.chrome.com/multidevice/user-agent&quot; rel=&quot;noopener&quot;&gt;user-agent (UA)&lt;/a&gt; string
provides the device name which enables Facebook to use publicly available data
on device characteristics to group mobile devices into classes. However, on
desktop devices the only relevant information the UA provides is the device&#39;s
operating system.&lt;/p&gt;
&lt;p&gt;For grouping desktop devices, Facebook logs the data about the operating system,
CPU cores (from &lt;code&gt;navigator.hardwareConcurrency&lt;/code&gt;), and RAM memory
(&lt;code&gt;navigator.deviceMemory&lt;/code&gt;) in their performance monitoring. Looking at the
relationships between different types of hardware and performance, they
classified devices into five categories. With hardware classes integrated into
performance monitoring, they get a more complete picture of how people use
Facebook products depending on their device and can identify regressions more
easily.&lt;/p&gt;
&lt;p&gt;Check out the video at &lt;a href=&quot;https://youtu.be/puUPpVrIRkc?t=1443&quot; rel=&quot;noopener&quot;&gt;24 mins&lt;/a&gt;, where
Nate walks through how Facebook approaches device grouping and uses adaptive
loading for animations and loading JavaScript.&lt;/p&gt;
&lt;h2 id=&quot;learn-more-about-adaptive-loading&quot;&gt;Learn more about adaptive loading &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/adaptive-loading-cds-2019/#learn-more-about-adaptive-loading&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Adaptive loading is all about designing your sites with inclusivity in mind.
Build a core experience that works great for everyone, then toggle or layer
features that make it even more awesome if a user has enough memory, CPU, or a
fast network. To learn more about adaptive loading, check out the available
&lt;a href=&quot;https://github.com/GoogleChromeLabs/adaptive-loading#full-applications&quot; rel=&quot;noopener&quot;&gt;demos&lt;/a&gt;
and watch the Chrome Dev Summit talk:&lt;/p&gt;
&lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;puUPpVrIRkc&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
</content>
    <author>
      <name>Milica Mihajlija</name>
    </author>
  </entry>
  
  <entry>
    <title>Route prefetching in Next.js</title>
    <link href="https://web.dev/route-prefetching-in-nextjs/"/>
    <updated>2019-11-08T00:00:00Z</updated>
    <id>https://web.dev/route-prefetching-in-nextjs/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;what-will-you-learn&quot;&gt;What will you learn? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/route-prefetching-in-nextjs/#what-will-you-learn&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this post you&#39;ll learn how routing in Next.js works, how it&#39;s optimized for speed, and how to customize it to best fit your needs.&lt;/p&gt;
&lt;h2 id=&quot;the-lesslinkgreater-component&quot;&gt;The &lt;code&gt;&amp;lt;Link&amp;gt;&lt;/code&gt; component &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/route-prefetching-in-nextjs/#the-lesslinkgreater-component&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In &lt;a href=&quot;https://nextjs.org/&quot; rel=&quot;noopener&quot;&gt;Next.js&lt;/a&gt;, you don&#39;t need to set up routing manually.
Next.js uses file-system-based routing, which lets you just create files and folders
inside the &lt;code&gt;./pages/&lt;/code&gt; directory:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Screenshot of the pages directory containting three files: index.js, margherita.js, and pineapple-pizza.js.&quot; decoding=&quot;async&quot; height=&quot;348&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 376px) 376px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cwpyvEgBCIbkqrbsbL0.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cwpyvEgBCIbkqrbsbL0.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cwpyvEgBCIbkqrbsbL0.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cwpyvEgBCIbkqrbsbL0.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cwpyvEgBCIbkqrbsbL0.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cwpyvEgBCIbkqrbsbL0.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cwpyvEgBCIbkqrbsbL0.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cwpyvEgBCIbkqrbsbL0.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cwpyvEgBCIbkqrbsbL0.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cwpyvEgBCIbkqrbsbL0.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cwpyvEgBCIbkqrbsbL0.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cwpyvEgBCIbkqrbsbL0.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7cwpyvEgBCIbkqrbsbL0.png?auto=format&amp;w=752 752w&quot; width=&quot;376&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;To link to different pages, use the
&lt;a href=&quot;https://nextjs.org/docs/api-reference/next/link&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;&amp;lt;Link&amp;gt;&lt;/code&gt;&lt;/a&gt; component, similarly to how you&#39;d
use the good old &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; element:&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 operator&quot;&gt;&amp;lt;&lt;/span&gt;Link href&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/margherita&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Margherita&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;a&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Link&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;When you use the &lt;code&gt;&amp;lt;Link&amp;gt;&lt;/code&gt; component for navigation, Next.js does a little bit
more for you. Normally, a page is downloaded when you follow a link to it, but
Next.js automatically prefetches the JavaScript needed to render the page.&lt;/p&gt;
&lt;p&gt;When you load a page with a few links, odds are that by the time you follow
a link, the component behind it has already been fetched. This improves
application responsiveness by making navigations to new pages quicker.&lt;/p&gt;
&lt;p&gt;In the example app below, the &lt;code&gt;index.js&lt;/code&gt; page links to &lt;code&gt;margherita.js&lt;/code&gt; with a
&lt;code&gt;&amp;lt;Link&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 480px; 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/nextjs-prefetching?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=pages%2Findex.js&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;nextjs-prefetching on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Use Chrome DevTools to verify that &lt;code&gt;margherita.js&lt;/code&gt; is prefetched:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;To preview the site, press &lt;strong&gt;View App&lt;/strong&gt;. Then press
&lt;strong&gt;Fullscreen&lt;/strong&gt;
&lt;img src=&quot;https://web.dev/images/glitch/fullscreen.svg&quot; alt=&quot;fullscreen&quot; style=&quot;padding: 4px 8px; opacity: .5; border: 1px solid #c3c3c3; border-radius: 5px; margin-top: 0;&quot; /&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the &lt;strong&gt;Network&lt;/strong&gt; tab.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select the &lt;strong&gt;Disable cache&lt;/strong&gt; checkbox.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reload the page.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When you load &lt;code&gt;index.js&lt;/code&gt;, the &lt;strong&gt;Network&lt;/strong&gt; tab shows that &lt;code&gt;margherita.js&lt;/code&gt; is
downloaded too:&lt;/p&gt;
&lt;img alt=&quot;DevTools Network tab with margherita.js highlighted.&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/admin/ajJKWGvPidRa1nvqzXKL.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/ajJKWGvPidRa1nvqzXKL.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/ajJKWGvPidRa1nvqzXKL.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/ajJKWGvPidRa1nvqzXKL.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/ajJKWGvPidRa1nvqzXKL.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/ajJKWGvPidRa1nvqzXKL.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/ajJKWGvPidRa1nvqzXKL.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/ajJKWGvPidRa1nvqzXKL.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/ajJKWGvPidRa1nvqzXKL.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/ajJKWGvPidRa1nvqzXKL.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/ajJKWGvPidRa1nvqzXKL.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/ajJKWGvPidRa1nvqzXKL.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/ajJKWGvPidRa1nvqzXKL.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/ajJKWGvPidRa1nvqzXKL.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/ajJKWGvPidRa1nvqzXKL.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/ajJKWGvPidRa1nvqzXKL.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/ajJKWGvPidRa1nvqzXKL.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/ajJKWGvPidRa1nvqzXKL.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h2 id=&quot;how-automatic-prefetching-works&quot;&gt;How automatic prefetching works &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/route-prefetching-in-nextjs/#how-automatic-prefetching-works&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Next.js prefetches only links that appear in the viewport and uses the &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 them. It also disables prefetching when the network connection is slow
or when users have
&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. Based on these checks, Next.js dynamically injects &lt;a href=&quot;https://web.dev/preload-critical-assets/&quot;&gt;&lt;code&gt;&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&lt;/code&gt;&lt;/a&gt; tags to download components for
subsequent navigations.&lt;/p&gt;
&lt;p&gt;Next.js only &lt;em&gt;fetches&lt;/em&gt; the JavaScript; it doesn&#39;t execute it. That way, it&#39;s not
downloading any additional content that the prefetched page might request until you visit
the link.&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; Glitch examples are running in production mode because prefetching depends on the browsing conditions and it&#39;s enabled only in optimized production builds. To switch to development mode, check the &lt;code&gt;README.md&lt;/code&gt; in Glitch examples. &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; Because &lt;code&gt;&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&lt;/code&gt; requests resources with high priority, the browser expects them to be used right away, which triggers Console warnings. &lt;a href=&quot;https://web.dev/fetch-priority/&quot;&gt;Fetch Priority&lt;/a&gt; will is available in Chrome, which will allow Next.js to indicate lower priority for resources that are not needed immediately with &lt;code&gt;&amp;lt;link rel=&amp;quot;preload&amp;quot; fetchpriority=&amp;quot;low&amp;quot;&amp;gt;&lt;/code&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;avoid-unnecessary-prefetching&quot;&gt;Avoid unnecessary prefetching &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/route-prefetching-in-nextjs/#avoid-unnecessary-prefetching&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To avoid downloading unnecessary content, you can disable prefetching for rarely
visited pages by setting the &lt;code&gt;prefetch&lt;/code&gt; property on &lt;code&gt;&amp;lt;Link&amp;gt;&lt;/code&gt; to &lt;code&gt;false&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 operator&quot;&gt;&amp;lt;&lt;/span&gt;Link href&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/pineapple-pizza&quot;&lt;/span&gt; prefetch&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 boolean&quot;&gt;false&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;br /&gt;  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Pineapple pizza&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;a&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Link&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In this second example app, the &lt;code&gt;index.js&lt;/code&gt; page has a &lt;code&gt;&amp;lt;Link&amp;gt;&lt;/code&gt; to
&lt;code&gt;pineapple-pizza.js&lt;/code&gt; with &lt;code&gt;prefetch&lt;/code&gt; set to &lt;code&gt;false&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 480px; 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/nextjs-noprefetch?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=pages%2Findex.js%3A12%3A50&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;nextjs-noprefetch on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;To inspect the network activity, follow the steps from the first example. When
you load &lt;code&gt;index.js&lt;/code&gt;, the DevTools &lt;strong&gt;Network&lt;/strong&gt; tab shows that &lt;code&gt;margherita.js&lt;/code&gt; is
downloaded, but &lt;code&gt;pineapple-pizza.js&lt;/code&gt; is not:&lt;/p&gt;
&lt;img alt=&quot;DevTools Network tab with margherita.js highlighted.&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/admin/8YTg0ym7vJbQm9oCVQYz.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/8YTg0ym7vJbQm9oCVQYz.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/8YTg0ym7vJbQm9oCVQYz.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/8YTg0ym7vJbQm9oCVQYz.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/8YTg0ym7vJbQm9oCVQYz.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/8YTg0ym7vJbQm9oCVQYz.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/8YTg0ym7vJbQm9oCVQYz.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/8YTg0ym7vJbQm9oCVQYz.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/8YTg0ym7vJbQm9oCVQYz.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/8YTg0ym7vJbQm9oCVQYz.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/8YTg0ym7vJbQm9oCVQYz.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/8YTg0ym7vJbQm9oCVQYz.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/8YTg0ym7vJbQm9oCVQYz.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/8YTg0ym7vJbQm9oCVQYz.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/8YTg0ym7vJbQm9oCVQYz.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/8YTg0ym7vJbQm9oCVQYz.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/8YTg0ym7vJbQm9oCVQYz.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/8YTg0ym7vJbQm9oCVQYz.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h2 id=&quot;prefetching-with-custom-routing&quot;&gt;Prefetching with custom routing &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/route-prefetching-in-nextjs/#prefetching-with-custom-routing&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;Link&amp;gt;&lt;/code&gt; component is suitable for most use cases, but you can also build
your own component to do routing. Next.js makes this easy for you with the
router API available in &lt;a href=&quot;https://nextjs.org/docs/api-reference/next/router#userouter&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;next/router&lt;/code&gt;&lt;/a&gt;.
If you want to do something (for example, submit a form) before navigating to a new
route, you can define that in your custom routing code.&lt;/p&gt;
&lt;p&gt;When you use custom components for routing, you can add prefetching to them too.
To implement prefetching in your routing code, use the &lt;code&gt;prefetch&lt;/code&gt; method from
&lt;code&gt;useRouter&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Take a look at &lt;code&gt;components/MyLink.js&lt;/code&gt; in this example app:&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 480px; 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/custom-routing-nextjs?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=components%2FMyLink.js&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;custom-routing-nextjs on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Prefetching is done inside the
&lt;a href=&quot;https://reactjs.org/docs/hooks-effect.html&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;useEffect&lt;/code&gt;&lt;/a&gt; hook. If the
&lt;code&gt;prefetch&lt;/code&gt; property on a &lt;code&gt;&amp;lt;MyLink&amp;gt;&lt;/code&gt; is set to &lt;code&gt;true&lt;/code&gt;, the route specified in the
&lt;code&gt;href&lt;/code&gt; property gets prefetched when that &lt;code&gt;&amp;lt;MyLink&amp;gt;&lt;/code&gt; is rendered:&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 function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prefetch&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; router&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prefetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;href&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 click the link, the routing is done in &lt;code&gt;handleClick&lt;/code&gt;. A message gets
logged to the console, and the &lt;code&gt;push&lt;/code&gt; method navigates to the new route
specified in &lt;code&gt;href&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;handleClick&lt;/span&gt; &lt;span class=&quot;token operator&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;    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;&quot;Having fun with Next.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;br /&gt;    router&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;href&lt;span 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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In this example app, the &lt;code&gt;index.js&lt;/code&gt; page has a &lt;code&gt;&amp;lt;MyLink&amp;gt;&lt;/code&gt; to &lt;code&gt;margherita.js&lt;/code&gt; and
&lt;code&gt;pineapple-pizza.js&lt;/code&gt;. The &lt;code&gt;prefetch&lt;/code&gt; property is set to &lt;code&gt;true&lt;/code&gt; on &lt;code&gt;/margherita&lt;/code&gt;
and to &lt;code&gt;false&lt;/code&gt; on &lt;code&gt;/pineapple-pizza&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 operator&quot;&gt;&amp;lt;&lt;/span&gt;MyLink href&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/margherita&quot;&lt;/span&gt; title&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Margherita&quot;&lt;/span&gt; prefetch&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 boolean&quot;&gt;true&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 operator&quot;&gt;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;MyLink href&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/pineapple-pizza&quot;&lt;/span&gt;  title&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Pineapple pizza&quot;&lt;/span&gt; prefetch&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 boolean&quot;&gt;false&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 operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;When you load &lt;code&gt;index.js&lt;/code&gt;, the &lt;strong&gt;Network&lt;/strong&gt; tab shows that &lt;code&gt;margherita.js&lt;/code&gt; is
downloaded and &lt;code&gt;pineapple-pizza.js&lt;/code&gt; is not:&lt;/p&gt;
&lt;img alt=&quot;DevTools Network tab with margherita.js highlighted.&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/admin/MWPy8nvBJCnzy4zGVRln.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/MWPy8nvBJCnzy4zGVRln.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/MWPy8nvBJCnzy4zGVRln.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/MWPy8nvBJCnzy4zGVRln.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/MWPy8nvBJCnzy4zGVRln.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/MWPy8nvBJCnzy4zGVRln.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/MWPy8nvBJCnzy4zGVRln.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/MWPy8nvBJCnzy4zGVRln.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/MWPy8nvBJCnzy4zGVRln.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/MWPy8nvBJCnzy4zGVRln.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/MWPy8nvBJCnzy4zGVRln.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/MWPy8nvBJCnzy4zGVRln.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/MWPy8nvBJCnzy4zGVRln.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/MWPy8nvBJCnzy4zGVRln.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/MWPy8nvBJCnzy4zGVRln.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/MWPy8nvBJCnzy4zGVRln.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/MWPy8nvBJCnzy4zGVRln.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/MWPy8nvBJCnzy4zGVRln.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;When you click on either link, the &lt;strong&gt;Console&lt;/strong&gt; logs &amp;quot;Having fun with Next.js.&amp;quot;
and navigates to the new route:&lt;/p&gt;
&lt;img alt=&quot;DevTools Console displaying the message &amp;#x27;Having fun with Next.js.&amp;#x27;&quot; decoding=&quot;async&quot; height=&quot;690&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/goiEqi3SIWJBUqsk7j6H.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/goiEqi3SIWJBUqsk7j6H.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/goiEqi3SIWJBUqsk7j6H.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/goiEqi3SIWJBUqsk7j6H.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/goiEqi3SIWJBUqsk7j6H.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/goiEqi3SIWJBUqsk7j6H.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/goiEqi3SIWJBUqsk7j6H.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/goiEqi3SIWJBUqsk7j6H.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/goiEqi3SIWJBUqsk7j6H.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/goiEqi3SIWJBUqsk7j6H.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/goiEqi3SIWJBUqsk7j6H.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/goiEqi3SIWJBUqsk7j6H.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/goiEqi3SIWJBUqsk7j6H.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/goiEqi3SIWJBUqsk7j6H.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/goiEqi3SIWJBUqsk7j6H.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/goiEqi3SIWJBUqsk7j6H.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/goiEqi3SIWJBUqsk7j6H.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/goiEqi3SIWJBUqsk7j6H.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/route-prefetching-in-nextjs/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you use &lt;code&gt;&amp;lt;Link&amp;gt;&lt;/code&gt;, Next.js automatically prefetches the JavaScript needed to
render the linked page, which makes navigating to new pages faster. If you are
using custom routing, you can use the Next.js router API to implement
prefetching yourself. Avoid downloading content unnecessarily by disabling
prefetching for rarely visited pages.&lt;/p&gt;
</content>
    <author>
      <name>Milica Mihajlija</name>
    </author>
  </entry>
  
  <entry>
    <title>Code splitting with dynamic imports in Next.js</title>
    <link href="https://web.dev/code-splitting-with-dynamic-imports-in-nextjs/"/>
    <updated>2019-11-08T00:00:00Z</updated>
    <id>https://web.dev/code-splitting-with-dynamic-imports-in-nextjs/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;what-will-you-learn&quot;&gt;What will you learn? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/code-splitting-with-dynamic-imports-in-nextjs/#what-will-you-learn&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This post explains different types of &lt;a href=&quot;https://web.dev/reduce-javascript-payloads-with-code-splitting/&quot;&gt;code
splitting&lt;/a&gt; and how to use
dynamic imports to speed up your Next.js apps.&lt;/p&gt;
&lt;h2 id=&quot;route-based-and-component-based-code-splitting&quot;&gt;Route-based and component-based code splitting &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/code-splitting-with-dynamic-imports-in-nextjs/#route-based-and-component-based-code-splitting&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By default, Next.js splits your JavaScript into separate chunks for each route.
When users load your application, Next.js only sends the code needed for the
initial route. When users navigate around the application, they fetch the chunks
associated with the other routes. Route-based code splitting minimizes the
amount of script that needs to be parsed and compiled at once, which results in
faster page load times.&lt;/p&gt;
&lt;p&gt;While route-based code splitting is a good default, you can further optimize the
loading process with code splitting on the component level. If you have large
components in your app, it&#39;s a great idea to split them into separate chunks.
That way, any large components that are not critical or only render on certain
user interactions (like clicking a button) can be lazy-loaded.&lt;/p&gt;
&lt;p&gt;Next.js supports &lt;a href=&quot;https://v8.dev/features/dynamic-import&quot; rel=&quot;noopener&quot;&gt;dynamic &lt;code&gt;import()&lt;/code&gt;&lt;/a&gt;,
which allows you to import JavaScript modules (including React components)
dynamically and load each import as a separate chunk. This gives you
component-level code splitting and enables you to control resource loading so
that users only download the code they need for the part of the site that
they&#39;re viewing. In Next.js, these components are &lt;a href=&quot;https://web.dev/rendering-on-the-web/&quot;&gt;server-side rendered
(SSR)&lt;/a&gt;
by default.&lt;/p&gt;
&lt;h2 id=&quot;dynamic-imports-in-action&quot;&gt;Dynamic imports in action &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/code-splitting-with-dynamic-imports-in-nextjs/#dynamic-imports-in-action&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This post includes several versions of a sample app that consists of a simple
page with one button. When you click the button, you get to see a cute puppy. As
you move through each version of the app, you&#39;ll see how dynamic imports are
different from &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/import&quot; rel=&quot;noopener&quot;&gt;static
imports&lt;/a&gt;
and how to work with them.&lt;/p&gt;
&lt;p&gt;In the first version of the app, the puppy lives in &lt;code&gt;components/Puppy.js&lt;/code&gt;. To
display the puppy on the page, the app imports the &lt;code&gt;Puppy&lt;/code&gt; component in
&lt;code&gt;index.js&lt;/code&gt; with a static import statement:&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; Puppy &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../components/Puppy&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;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 480px; 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/static-import?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=index.js&amp;previewSize=0&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;static-import on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;To see how Next.js bundles the app, inspect the network trace in DevTools:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;To preview the site, press &lt;strong&gt;View App&lt;/strong&gt;. Then press
&lt;strong&gt;Fullscreen&lt;/strong&gt;
&lt;img src=&quot;https://web.dev/images/glitch/fullscreen.svg&quot; alt=&quot;fullscreen&quot; style=&quot;padding: 4px 8px; opacity: .5; border: 1px solid #c3c3c3; border-radius: 5px; margin-top: 0;&quot; /&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the &lt;strong&gt;Network&lt;/strong&gt; tab.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select the &lt;strong&gt;Disable cache&lt;/strong&gt; checkbox.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reload the page.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When you load the page, all the necessary code, including the &lt;code&gt;Puppy.js&lt;/code&gt;
component, is bundled in &lt;code&gt;index.js&lt;/code&gt;:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;DevTools Network tab showing showing six JavaScript files: index.js, app.js, webpack.js, main.js, 0.js and the dll (dynamic-link library) file.&quot; decoding=&quot;async&quot; height=&quot;665&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/6KWlTYFhoIEIGqnuMwlh.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/6KWlTYFhoIEIGqnuMwlh.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/6KWlTYFhoIEIGqnuMwlh.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/6KWlTYFhoIEIGqnuMwlh.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/6KWlTYFhoIEIGqnuMwlh.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/6KWlTYFhoIEIGqnuMwlh.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/6KWlTYFhoIEIGqnuMwlh.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/6KWlTYFhoIEIGqnuMwlh.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/6KWlTYFhoIEIGqnuMwlh.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/6KWlTYFhoIEIGqnuMwlh.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/6KWlTYFhoIEIGqnuMwlh.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/6KWlTYFhoIEIGqnuMwlh.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/6KWlTYFhoIEIGqnuMwlh.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/6KWlTYFhoIEIGqnuMwlh.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/6KWlTYFhoIEIGqnuMwlh.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/6KWlTYFhoIEIGqnuMwlh.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/6KWlTYFhoIEIGqnuMwlh.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/6KWlTYFhoIEIGqnuMwlh.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;When you press the &lt;strong&gt;Click me&lt;/strong&gt; button, only the request for the puppy JPEG is
added to the &lt;strong&gt;Network&lt;/strong&gt; tab:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;DevTools Network tab after the button click, showing the same six JavaScript files and one image.&quot; decoding=&quot;async&quot; height=&quot;665&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7MkXVqnqfIbW74VV48kB.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7MkXVqnqfIbW74VV48kB.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7MkXVqnqfIbW74VV48kB.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7MkXVqnqfIbW74VV48kB.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7MkXVqnqfIbW74VV48kB.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7MkXVqnqfIbW74VV48kB.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7MkXVqnqfIbW74VV48kB.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7MkXVqnqfIbW74VV48kB.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7MkXVqnqfIbW74VV48kB.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7MkXVqnqfIbW74VV48kB.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7MkXVqnqfIbW74VV48kB.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7MkXVqnqfIbW74VV48kB.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7MkXVqnqfIbW74VV48kB.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7MkXVqnqfIbW74VV48kB.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7MkXVqnqfIbW74VV48kB.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7MkXVqnqfIbW74VV48kB.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7MkXVqnqfIbW74VV48kB.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7MkXVqnqfIbW74VV48kB.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;The downside of this approach is that even if users don&#39;t click the button to
see the puppy, they have to load the &lt;code&gt;Puppy&lt;/code&gt; component because it&#39;s included in
&lt;code&gt;index.js&lt;/code&gt;. In this little example that&#39;s not a big deal, but in real-world
applications it&#39;s often a huge improvement to load large components only when
necessary.&lt;/p&gt;
&lt;p&gt;Now check out a second version of the app, in which the static import is
replaced with a dynamic import. Next.js includes &lt;code&gt;next/dynamic&lt;/code&gt;, which makes it
possible to use dynamic imports for any components in Next:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;del class=&quot;highlight-line highlight-line-remove&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Puppy &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../components/Puppy&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/del&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; dynamic &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;next/dynamic&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/ins&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 comment&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;ins class=&quot;highlight-line highlight-line-add&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; Puppy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dynamic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&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;&quot;../components/Puppy&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;/ins&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 480px; 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/dynamic-import-nextjs?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=pages%2Findex.js%3A29%3A10&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;dynamic-import-nextjs on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Follow the steps from the first example to inspect the network trace.&lt;/p&gt;
&lt;p&gt;When you first load the app, only &lt;code&gt;index.js&lt;/code&gt; is downloaded. This time it&#39;s
0.5 KB smaller (it went down from 37.9 KB to 37.4 KB) because it
doesn&#39;t include the code for the &lt;code&gt;Puppy&lt;/code&gt; component:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;DevTools Network showing the same six JavaScript files, except index.js is now 0.5 KB smaller.&quot; decoding=&quot;async&quot; height=&quot;665&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/K7Ii3bxUkb37LrZjjWT1.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/K7Ii3bxUkb37LrZjjWT1.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/K7Ii3bxUkb37LrZjjWT1.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/K7Ii3bxUkb37LrZjjWT1.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/K7Ii3bxUkb37LrZjjWT1.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/K7Ii3bxUkb37LrZjjWT1.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/K7Ii3bxUkb37LrZjjWT1.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/K7Ii3bxUkb37LrZjjWT1.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/K7Ii3bxUkb37LrZjjWT1.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/K7Ii3bxUkb37LrZjjWT1.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/K7Ii3bxUkb37LrZjjWT1.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/K7Ii3bxUkb37LrZjjWT1.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/K7Ii3bxUkb37LrZjjWT1.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/K7Ii3bxUkb37LrZjjWT1.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/K7Ii3bxUkb37LrZjjWT1.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/K7Ii3bxUkb37LrZjjWT1.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/K7Ii3bxUkb37LrZjjWT1.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/K7Ii3bxUkb37LrZjjWT1.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;The &lt;code&gt;Puppy&lt;/code&gt; component is now in a separate chunk, &lt;code&gt;1.js&lt;/code&gt;, which is loaded only
when you press the button:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;DevTools Network tab after the button click, showing the additional 1.js file and the image added to the bottom of the file list.&quot; decoding=&quot;async&quot; height=&quot;665&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1DfVDv5poQmwXwOKmnvd.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1DfVDv5poQmwXwOKmnvd.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1DfVDv5poQmwXwOKmnvd.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1DfVDv5poQmwXwOKmnvd.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1DfVDv5poQmwXwOKmnvd.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1DfVDv5poQmwXwOKmnvd.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1DfVDv5poQmwXwOKmnvd.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1DfVDv5poQmwXwOKmnvd.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1DfVDv5poQmwXwOKmnvd.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1DfVDv5poQmwXwOKmnvd.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1DfVDv5poQmwXwOKmnvd.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1DfVDv5poQmwXwOKmnvd.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1DfVDv5poQmwXwOKmnvd.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1DfVDv5poQmwXwOKmnvd.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1DfVDv5poQmwXwOKmnvd.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1DfVDv5poQmwXwOKmnvd.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1DfVDv5poQmwXwOKmnvd.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1DfVDv5poQmwXwOKmnvd.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&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; By default, Next.js names these dynamic chunks &lt;em&gt;number&lt;/em&gt;.js, where &lt;em&gt;number&lt;/em&gt; starts from 1. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;In real-world applications, components are often &lt;a href=&quot;https://bundlephobia.com/result?p=moment@2.24.0&quot; rel=&quot;noopener&quot;&gt;much
larger&lt;/a&gt;, and lazy loading them
can trim your initial JavaScript payload by hundreds of kilobytes.&lt;/p&gt;
&lt;h2 id=&quot;dynamic-imports-with-custom-loading-indicator&quot;&gt;Dynamic imports with custom loading indicator &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/code-splitting-with-dynamic-imports-in-nextjs/#dynamic-imports-with-custom-loading-indicator&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you lazy-load resources, it&#39;s good practice to provide a loading indicator
in case there are any delays. In Next.js, you can do that by providing an
additional argument to the &lt;code&gt;dynamic()&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;const&lt;/span&gt; Puppy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dynamic&lt;/span&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 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;&quot;../components/Puppy&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;span class=&quot;token function-variable function&quot;&gt;loading&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 operator&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Loading&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&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: 480px; 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/dynamic-import-loading?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=pages%2Findex.js%3A7%3A27&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;dynamic-import-loading on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;To see the loading indictor in action, simulate slow network connection in
DevTools:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;To preview the site, press &lt;strong&gt;View App&lt;/strong&gt;. Then press
&lt;strong&gt;Fullscreen&lt;/strong&gt;
&lt;img src=&quot;https://web.dev/images/glitch/fullscreen.svg&quot; alt=&quot;fullscreen&quot; style=&quot;padding: 4px 8px; opacity: .5; border: 1px solid #c3c3c3; border-radius: 5px; margin-top: 0;&quot; /&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the &lt;strong&gt;Network&lt;/strong&gt; tab.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select the &lt;strong&gt;Disable cache&lt;/strong&gt; checkbox.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the &lt;strong&gt;Throttling&lt;/strong&gt; drop-down list, select &lt;strong&gt;Fast 3G&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Press the &lt;strong&gt;Click me&lt;/strong&gt; button.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now when you click the button it takes a while to load the component and the app
displays the &amp;quot;Loading…&amp;quot; message in the meantime.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A dark screen with the text &amp;quot;Loading...&amp;quot;.&quot; decoding=&quot;async&quot; height=&quot;663&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tjlpmwolBVp1jh948Fln.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tjlpmwolBVp1jh948Fln.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tjlpmwolBVp1jh948Fln.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tjlpmwolBVp1jh948Fln.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tjlpmwolBVp1jh948Fln.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tjlpmwolBVp1jh948Fln.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tjlpmwolBVp1jh948Fln.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tjlpmwolBVp1jh948Fln.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tjlpmwolBVp1jh948Fln.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tjlpmwolBVp1jh948Fln.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tjlpmwolBVp1jh948Fln.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tjlpmwolBVp1jh948Fln.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tjlpmwolBVp1jh948Fln.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tjlpmwolBVp1jh948Fln.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tjlpmwolBVp1jh948Fln.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tjlpmwolBVp1jh948Fln.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tjlpmwolBVp1jh948Fln.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tjlpmwolBVp1jh948Fln.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;dynamic-imports-without-ssr&quot;&gt;Dynamic imports without SSR &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/code-splitting-with-dynamic-imports-in-nextjs/#dynamic-imports-without-ssr&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you need to render a component only on the client side (for example, a chat
widget) you can do that by setting the &lt;code&gt;ssr&lt;/code&gt; option to &lt;code&gt;false&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; Puppy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dynamic&lt;/span&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 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;&quot;../components/Puppy&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;span class=&quot;token literal-property property&quot;&gt;ssr&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;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: 480px; 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/dynamic-import-no-ssr?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=pages%2Findex.js%3A5%3A0&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;dynamic-import-no-ssr on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/code-splitting-with-dynamic-imports-in-nextjs/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With support for dynamic imports, Next.js gives you component-level code
splitting, which can minimize your JavaScript payloads and improve application
load time. All components are server-side rendered by default and you can
disable this option whenever necessary.&lt;/p&gt;
</content>
    <author>
      <name>Milica Mihajlija</name>
    </author>
  </entry>
  
  <entry>
    <title>Efficiently load third-party JavaScript</title>
    <link href="https://web.dev/efficiently-load-third-party-javascript/"/>
    <updated>2019-08-14T00:00:00Z</updated>
    <id>https://web.dev/efficiently-load-third-party-javascript/</id>
    <content type="html" mode="escaped">&lt;p&gt;If a third-party script is &lt;a href=&quot;https://web.dev/third-party-javascript/&quot;&gt;slowing down&lt;/a&gt; your page load, you have two options to improve performance:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Remove it if it doesn&#39;t add clear value to your site.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Optimize the loading process.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This post explains how to optimize the loading process of third-party scripts with the following techniques:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Using the &lt;code&gt;async&lt;/code&gt; or &lt;code&gt;defer&lt;/code&gt; attribute on &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Establishing early connections to required origins&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Lazy loading&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Optimizing how you serve third-party scripts&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;use-async-or-defer&quot;&gt;Use &lt;code&gt;async&lt;/code&gt; or &lt;code&gt;defer&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/efficiently-load-third-party-javascript/#use-async-or-defer&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Because &lt;a href=&quot;https://web.dev/third-party-javascript/&quot;&gt;synchronous scripts&lt;/a&gt; delay DOM construction and rendering, you should always load third-party scripts asynchronously unless the script has to run before the page can be rendered.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;defer&lt;/code&gt; attributes tell the browser that it may go on parsing the HTML while loading the script in the background, and then execute the script after it loads. This way, script downloads don&#39;t block DOM construction and page rendering. The result is that the user can see the page before all scripts have finished loading.&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;async&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;script.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;defer&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;script.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The difference between &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;defer&lt;/code&gt; is when they start executing the scripts.&lt;/p&gt;
&lt;h3 id=&quot;async&quot;&gt;&lt;code&gt;async&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/efficiently-load-third-party-javascript/#async&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Scripts with the &lt;code&gt;async&lt;/code&gt; attribute execute at the first opportunity after they finish downloading and before the window&#39;s &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Events/load&quot; rel=&quot;noopener&quot;&gt;load&lt;/a&gt; event. This means it&#39;s possible (and likely) that &lt;code&gt;async&lt;/code&gt; scripts will not be executed in the order in which they appear in the HTML. It also means they can interrupt DOM building if they finish downloading while the parser is still at work.&lt;/p&gt;
&lt;img alt=&quot;Diagram of parser blocking script with async attribute&quot; decoding=&quot;async&quot; height=&quot;252&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/tCqsJ3E7m4lpKOprXu5B.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/tCqsJ3E7m4lpKOprXu5B.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/tCqsJ3E7m4lpKOprXu5B.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/tCqsJ3E7m4lpKOprXu5B.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/tCqsJ3E7m4lpKOprXu5B.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/tCqsJ3E7m4lpKOprXu5B.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/tCqsJ3E7m4lpKOprXu5B.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/tCqsJ3E7m4lpKOprXu5B.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/tCqsJ3E7m4lpKOprXu5B.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/tCqsJ3E7m4lpKOprXu5B.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/tCqsJ3E7m4lpKOprXu5B.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/tCqsJ3E7m4lpKOprXu5B.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/tCqsJ3E7m4lpKOprXu5B.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/tCqsJ3E7m4lpKOprXu5B.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/tCqsJ3E7m4lpKOprXu5B.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/tCqsJ3E7m4lpKOprXu5B.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/tCqsJ3E7m4lpKOprXu5B.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/tCqsJ3E7m4lpKOprXu5B.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h3 id=&quot;defer&quot;&gt;&lt;code&gt;defer&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/efficiently-load-third-party-javascript/#defer&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Scripts with the &lt;code&gt;defer&lt;/code&gt; attribute execute after HTML parsing is completely finished, but before the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Events/DOMContentLoaded&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;DOMContentLoaded&lt;/code&gt;&lt;/a&gt; event. &lt;code&gt;defer&lt;/code&gt; guarantees scripts will be executed in the order they appear in the HTML and will not block the parser.&lt;/p&gt;
&lt;img alt=&quot;Diagram of parser flow with a script with defer attribute&quot; decoding=&quot;async&quot; height=&quot;253&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/Eq0mcvDALKibHe15HspN.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/Eq0mcvDALKibHe15HspN.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/Eq0mcvDALKibHe15HspN.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/Eq0mcvDALKibHe15HspN.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/Eq0mcvDALKibHe15HspN.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/Eq0mcvDALKibHe15HspN.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/Eq0mcvDALKibHe15HspN.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/Eq0mcvDALKibHe15HspN.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/Eq0mcvDALKibHe15HspN.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/Eq0mcvDALKibHe15HspN.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/Eq0mcvDALKibHe15HspN.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/Eq0mcvDALKibHe15HspN.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/Eq0mcvDALKibHe15HspN.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/Eq0mcvDALKibHe15HspN.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/Eq0mcvDALKibHe15HspN.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/Eq0mcvDALKibHe15HspN.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/Eq0mcvDALKibHe15HspN.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/Eq0mcvDALKibHe15HspN.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Use &lt;code&gt;async&lt;/code&gt; if it&#39;s important to have the script run earlier in the loading process.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use &lt;code&gt;defer&lt;/code&gt; for less critical resources. A video player that&#39;s below-the-fold, for example.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Using these attributes can significantly speed up page load. For example, &lt;a href=&quot;https://medium.com/p/a0a1000be5#4123&quot; rel=&quot;noopener&quot;&gt;Telegraph recently deferred all of their scripts&lt;/a&gt;, including ads and analytics, and improved the ad loading time by an average of four seconds.&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; Analytics scripts are usually loaded early so you don&#39;t miss any valuable analytics data. Fortunately, there are &lt;a href=&quot;https://philipwalton.com/articles/the-google-analytics-setup-i-use-on-every-site-i-build/&quot;&gt;patterns to initialize analytics lazily&lt;/a&gt; while retaining early page-load data. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;establish-early-connections-to-required-origins&quot;&gt;Establish early connections to required origins &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/efficiently-load-third-party-javascript/#establish-early-connections-to-required-origins&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can save 100–500 ms by &lt;a href=&quot;https://web.dev/preconnect-and-dns-prefetch/&quot;&gt;establishing early connections&lt;/a&gt; to important third-party origins.&lt;/p&gt;
&lt;p&gt;Two &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/link&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt;&lt;/a&gt; types can help here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;preconnect&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;dns-prefetch&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;preconnect&quot;&gt;&lt;code&gt;preconnect&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/efficiently-load-third-party-javascript/#preconnect&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;link rel=&amp;quot;preconnect&amp;quot;&amp;gt;&lt;/code&gt; informs the browser that your page intends to establish a connection to another origin, and that you&#39;d like the process to start as soon as possible. When the request for a resource from the pre-connected origin is made, the download starts immediately.&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://cdn.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;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;h3 id=&quot;dns-prefetch&quot;&gt;&lt;code&gt;dns-prefetch&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/efficiently-load-third-party-javascript/#dns-prefetch&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;link rel=&amp;quot;dns-prefetch&amp;gt;&lt;/code&gt; handles a small subset of what is handled by &lt;code&gt;&amp;lt;link rel=&amp;quot;preconnect&amp;quot;&amp;gt;&lt;/code&gt;.  Establishing a connection involves the DNS lookup and TCP handshake, and for secure origins, TLS negotiations. &lt;code&gt;dns-prefetch&lt;/code&gt; instructs the browser to only resolve the DNS of a specific domain before it has been explicitly called.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;preconnect&lt;/code&gt; hint is best used for only the most critical connections; for less critical third-party domains use &lt;code&gt;&amp;lt;link rel=dns-prefetch&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;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; 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;. Use separate link tags to implement this safely:&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;h2 id=&quot;lazy-load-third-party-resources&quot;&gt;Lazy-load third-party resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/efficiently-load-third-party-javascript/#lazy-load-third-party-resources&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Embedded third-party resources can be a big contributor to slow page speed when constructed poorly. If they aren&#39;t critical or are below the fold (that is, if users have to scroll to view them), lazy loading is a good way to improve page speed and paint metrics. This way, users will get the main page content faster and have a better experience.&lt;/p&gt;
&lt;figure data-float=&quot;left&quot;&gt;
&lt;img alt=&quot;A diagram of a webpage shown on a mobile device with scrollable content extending beyond the screen. The content that&amp;#x27;s below-the-fold is desaturated because it&amp;#x27;s not loaded yet.&quot; decoding=&quot;async&quot; height=&quot;438&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 366px) 366px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/uzPZzkgzfrv2Oy3UQPrN.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/uzPZzkgzfrv2Oy3UQPrN.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/uzPZzkgzfrv2Oy3UQPrN.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/uzPZzkgzfrv2Oy3UQPrN.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/uzPZzkgzfrv2Oy3UQPrN.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/uzPZzkgzfrv2Oy3UQPrN.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/uzPZzkgzfrv2Oy3UQPrN.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/uzPZzkgzfrv2Oy3UQPrN.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/uzPZzkgzfrv2Oy3UQPrN.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/uzPZzkgzfrv2Oy3UQPrN.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/uzPZzkgzfrv2Oy3UQPrN.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/uzPZzkgzfrv2Oy3UQPrN.png?auto=format&amp;w=732 732w&quot; width=&quot;366&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;One effective approach is to lazy-load third-party content after the main page content loads. Ads are a good candidate for this approach.&lt;/p&gt;
&lt;p&gt;Ads are an important source of income for many sites, but users come for the content. By lazy loading ads and delivering the main content faster, you can increase the overall viewability percentage of an ad. For example, MediaVine switched to &lt;a href=&quot;https://www.mediavine.com/lazy-loading-ads-mediavine-ads-load-200-faster/&quot; rel=&quot;noopener&quot;&gt;lazy-loading ads&lt;/a&gt; and saw a 200% improvement in page load speed. DoubleClick have guidance on how to lazy-load ads in their &lt;a href=&quot;https://support.google.com/dfp_premium/answer/4578089#lazyloading&quot; rel=&quot;noopener&quot;&gt;official documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;An alternative approach is to load third-party content only when users scroll down to that section of the page.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/blog/intersectionobserver/&quot; rel=&quot;noopener&quot;&gt;Intersection Observer&lt;/a&gt; is a browser API that efficiently detects when an element enters or exits the browser&#39;s viewport and it can be used to implement this technique. &lt;a href=&quot;https://web.dev/use-lazysizes-to-lazyload-images/&quot;&gt;lazysizes&lt;/a&gt; is a popular JavaScript library for lazy loading images and &lt;a href=&quot;http://afarkas.github.io/lazysizes/#examples&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;iframes&lt;/code&gt;&lt;/a&gt;. It supports YouTube embeds and &lt;a href=&quot;https://github.com/aFarkas/lazysizes/tree/gh-pages/plugins/unveilhooks&quot; rel=&quot;noopener&quot;&gt;widgets&lt;/a&gt;. It also has &lt;a href=&quot;https://github.com/aFarkas/lazysizes/blob/097a9878817dd17be3366633e555f3929a7eaaf1/src/lazysizes-intersection.js&quot; rel=&quot;noopener&quot;&gt;optional support&lt;/a&gt; for IntersectionObserver.&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 careful when lazy loading resources with JavaScript. If JavaScript fails to load, perhaps due to flaky network conditions, your resources won&#39;t load at all. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Using the &lt;a href=&quot;https://web.dev/browser-level-image-lazy-loading/&quot;&gt;&lt;code&gt;loading&lt;/code&gt; attribute for lazy loading images and iframes&lt;/a&gt; is a great alternative to JavaScript techniques, and it has recently become available in Chrome 76!&lt;/p&gt;
&lt;h2 id=&quot;optimize-how-you-serve-third-party-scripts&quot;&gt;Optimize how you serve third-party scripts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/efficiently-load-third-party-javascript/#optimize-how-you-serve-third-party-scripts&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;third-party-cdn-hosting&quot;&gt;Third-party CDN hosting &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/efficiently-load-third-party-javascript/#third-party-cdn-hosting&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It&#39;s common for third-party vendors to provide URLs of JavaScript files that they host, usually on a &lt;a href=&quot;https://en.wikipedia.org/wiki/Content_delivery_network&quot; rel=&quot;noopener&quot;&gt;content delivery network (CDN)&lt;/a&gt;. The benefits of this approach are that you can get started quickly—just copy and paste the URL—and there&#39;s no maintenance overhead. The third-party vendor handles server configuration and script updates.&lt;/p&gt;
&lt;p&gt;But because they are not on the same origin as the rest of your resources, loading files from a public CDN comes with a network cost. The browser needs to perform a DNS lookup, establish a new HTTP connection, and, on secure origins, perform an SSL handshake with the vendor&#39;s server.&lt;/p&gt;
&lt;p&gt;When you use files from third-party servers, you rarely have control over caching. Relying on someone else&#39;s caching strategy might cause scripts to be unnecessarily re-fetched from the network too often.&lt;/p&gt;
&lt;h3 id=&quot;self-host-third-party-scripts&quot;&gt;Self-host third-party scripts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/efficiently-load-third-party-javascript/#self-host-third-party-scripts&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Self-hosting third-party scripts is an option that gives you more control over a script&#39;s loading process. By self-hosting you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reduce DNS lookup and round-trip times.&lt;/li&gt;
&lt;li&gt;Improve &lt;a href=&quot;https://web.dev/http-cache/&quot;&gt;HTTP caching&lt;/a&gt; headers.&lt;/li&gt;
&lt;li&gt;Take advantage of &lt;a href=&quot;https://web.dev/performance-http2/&quot;&gt;HTTP/2&lt;/a&gt;, or the newer HTTP/3.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, Casper managed to &lt;a href=&quot;https://medium.com/caspertechteam/we-shaved-1-7-seconds-off-casper-com-by-self-hosting-optimizely-2704bcbff8ec&quot; rel=&quot;noopener&quot;&gt;shave 1.7 seconds&lt;/a&gt; off load time by self-hosting an A/B testing script.&lt;/p&gt;
&lt;p&gt;Self-hosting comes with one big downside though: scripts can go out of date and won&#39;t get automatic updates when there&#39;s an API change or a security fix.&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;  Manually updating scripts can add a lot of overhead to your development process and you might miss out on important updates. If you are not using CDN hosting for serving all the resources, you are also missing out on &lt;a href=&quot;https://www.cloudflare.com/learning/cdn/glossary/edge-server/&quot;&gt;edge-caching&lt;/a&gt; and you have to optimize your server&#39;s compression. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;use-service-workers-to-cache-scripts-from-third-party-servers&quot;&gt;Use service workers to cache scripts from third-party servers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/efficiently-load-third-party-javascript/#use-service-workers-to-cache-scripts-from-third-party-servers&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;An alternative to self-hosting that allows you greater control over caching while still getting the third-party CDN benefits is &lt;a href=&quot;https://developer.chrome.com/docs/workbox/caching-resources-during-runtime/#cross-origin-considerations&quot; rel=&quot;noopener&quot;&gt;using service workers to cache scripts from third-party servers&lt;/a&gt;. This gives you control over how often scripts are re-fetched from the network and makes it possible to create a loading strategy that throttles requests for non-essential third-party resources until the page reaches a key user moment. Using &lt;code&gt;preconnect&lt;/code&gt; to establish early connections in this case can also mitigate the network costs to an extent.&lt;/p&gt;
</content>
    <author>
      <name>Milica Mihajlija</name>
    </author>
  </entry>
  
  <entry>
    <title>Identify slow third-party JavaScript</title>
    <link href="https://web.dev/identify-slow-third-party-javascript/"/>
    <updated>2019-08-14T00:00:00Z</updated>
    <id>https://web.dev/identify-slow-third-party-javascript/</id>
    <content type="html" mode="escaped">&lt;p&gt;As a developer, you often don&#39;t have control over &lt;a href=&quot;https://web.dev/third-party-javascript/#network&quot;&gt;which third-party scripts&lt;/a&gt; your site loads. Before you can optimize third-party content you have to do some detective work to find out what&#39;s making your site slow. 🕵️&lt;/p&gt;
&lt;p&gt;In this post, you&#39;ll learn how to use &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt; and &lt;a href=&quot;https://developer.chrome.com/docs/devtools/&quot; rel=&quot;noopener&quot;&gt;Chrome DevTools&lt;/a&gt; to identify slow third-party resources. The post walks through increasingly robust techniques which are best used in combination.&lt;/p&gt;
&lt;h2 id=&quot;if-you-only-have-5-minutes&quot;&gt;If you only have 5 minutes &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/identify-slow-third-party-javascript/#if-you-only-have-5-minutes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Lighthouse &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/&quot; rel=&quot;noopener&quot;&gt;Performance audit&lt;/a&gt; helps you discover opportunities to speed up page loads. Slow third-party scripts are likely to appear in the &lt;strong&gt;Diagnostics&lt;/strong&gt; section under the &lt;strong&gt;Reduce JavaScript execution time&lt;/strong&gt; and &lt;strong&gt;Avoid enormous network payloads&lt;/strong&gt; audits.&lt;/p&gt;
&lt;p&gt;To run an audit:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Lighthouse&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Mobile&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;Performance&lt;/strong&gt; checkbox. (You can clear the rest of the checkboxes in the Audits section.)&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Simulated Fast 3G, 4x CPU Slowdown&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;Clear Storage&lt;/strong&gt; checkbox.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Run audits&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;img alt=&quot;Screenshot of the Chrome DevTools Audits panel.&quot; decoding=&quot;async&quot; height=&quot;1068&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/XLNFxdEOc7739bcIwERq.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/XLNFxdEOc7739bcIwERq.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/XLNFxdEOc7739bcIwERq.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/XLNFxdEOc7739bcIwERq.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/XLNFxdEOc7739bcIwERq.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/XLNFxdEOc7739bcIwERq.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/XLNFxdEOc7739bcIwERq.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/XLNFxdEOc7739bcIwERq.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/XLNFxdEOc7739bcIwERq.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/XLNFxdEOc7739bcIwERq.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/XLNFxdEOc7739bcIwERq.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/XLNFxdEOc7739bcIwERq.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/XLNFxdEOc7739bcIwERq.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/XLNFxdEOc7739bcIwERq.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/XLNFxdEOc7739bcIwERq.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/XLNFxdEOc7739bcIwERq.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/XLNFxdEOc7739bcIwERq.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/XLNFxdEOc7739bcIwERq.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h3 id=&quot;third-party-usage&quot;&gt;Third-party usage &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/identify-slow-third-party-javascript/#third-party-usage&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Lighthouse &lt;strong&gt;Third-party usage&lt;/strong&gt; audit shows a list of the third-party providers a page uses. This overview can help you better understand the big picture and identify redundant third-party code. The audit is available in the &lt;a href=&quot;https://chrome.google.com/webstore/detail/lighthouse/blipmdconlkpinefehnmjammfjpmpbjk?hl=en&quot; rel=&quot;noopener&quot;&gt;Lighthouse extension&lt;/a&gt; and will soon be added to DevTools in Chrome 77.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot showing that 51 third-parties were found and a list of imaginary startups.&quot; decoding=&quot;async&quot; height=&quot;646&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 728px) 728px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/4JXHK0FkgJIfKED16BnF.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/4JXHK0FkgJIfKED16BnF.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/4JXHK0FkgJIfKED16BnF.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/4JXHK0FkgJIfKED16BnF.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/4JXHK0FkgJIfKED16BnF.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/4JXHK0FkgJIfKED16BnF.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/4JXHK0FkgJIfKED16BnF.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/4JXHK0FkgJIfKED16BnF.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/4JXHK0FkgJIfKED16BnF.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/4JXHK0FkgJIfKED16BnF.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/4JXHK0FkgJIfKED16BnF.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/4JXHK0FkgJIfKED16BnF.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/4JXHK0FkgJIfKED16BnF.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/4JXHK0FkgJIfKED16BnF.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/4JXHK0FkgJIfKED16BnF.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/4JXHK0FkgJIfKED16BnF.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/4JXHK0FkgJIfKED16BnF.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/4JXHK0FkgJIfKED16BnF.png?auto=format&amp;w=1456 1456w&quot; width=&quot;728&quot; /&gt;
  &lt;figcaption&gt;
  Third-party provider names generated with &lt;a href=&quot;http://tiffzhang.com/startup/?s=641553836036&quot;&gt;Startup generator&lt;/a&gt;. Any similarity to actual startups, living or dead, is purely coincidental.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;reduce-javascript-execution-time&quot;&gt;Reduce JavaScript execution time &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/identify-slow-third-party-javascript/#reduce-javascript-execution-time&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Lighthouse &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/bootup-time/&quot; rel=&quot;noopener&quot;&gt;Reduce JavaScript execution time&lt;/a&gt; audit highlights scripts that take a long time to parse, compile, or evaluate. Select the &lt;strong&gt;Show 3rd-party resources&lt;/strong&gt; checkbox to discover CPU-intensive third-party scripts.&lt;/p&gt;
&lt;img alt=&quot;Screenshot showing that the &amp;#x27;Show third-party resources&amp;#x27; checkbox is checked.&quot; decoding=&quot;async&quot; height=&quot;981&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/O7vN1En6dtbL3Q8TbufC.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/O7vN1En6dtbL3Q8TbufC.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/O7vN1En6dtbL3Q8TbufC.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/O7vN1En6dtbL3Q8TbufC.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/O7vN1En6dtbL3Q8TbufC.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/O7vN1En6dtbL3Q8TbufC.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/O7vN1En6dtbL3Q8TbufC.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/O7vN1En6dtbL3Q8TbufC.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/O7vN1En6dtbL3Q8TbufC.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/O7vN1En6dtbL3Q8TbufC.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/O7vN1En6dtbL3Q8TbufC.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/O7vN1En6dtbL3Q8TbufC.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/O7vN1En6dtbL3Q8TbufC.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/O7vN1En6dtbL3Q8TbufC.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/O7vN1En6dtbL3Q8TbufC.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/O7vN1En6dtbL3Q8TbufC.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/O7vN1En6dtbL3Q8TbufC.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/O7vN1En6dtbL3Q8TbufC.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h3 id=&quot;avoid-enormous-network-payloads&quot;&gt;Avoid enormous network payloads &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/identify-slow-third-party-javascript/#avoid-enormous-network-payloads&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Lighthouse &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/total-byte-weight/&quot; rel=&quot;noopener&quot;&gt;Avoid enormous network payloads&lt;/a&gt; audit identifies network requests—including those from third-parties—that may slow down page load time. The audit fails when your network payload exceeds 4,000 KB.&lt;/p&gt;
&lt;img alt=&quot;Screenshot of the Chrome DevTools &amp;#x27;Avoid enormous network payloads&amp;#x27; audit.&quot; decoding=&quot;async&quot; height=&quot;631&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 799px) 799px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/9Pnoz73MLeNzooUQLuam.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/9Pnoz73MLeNzooUQLuam.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/9Pnoz73MLeNzooUQLuam.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/9Pnoz73MLeNzooUQLuam.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/9Pnoz73MLeNzooUQLuam.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/9Pnoz73MLeNzooUQLuam.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/9Pnoz73MLeNzooUQLuam.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/9Pnoz73MLeNzooUQLuam.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/9Pnoz73MLeNzooUQLuam.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/9Pnoz73MLeNzooUQLuam.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/9Pnoz73MLeNzooUQLuam.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/9Pnoz73MLeNzooUQLuam.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/9Pnoz73MLeNzooUQLuam.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/9Pnoz73MLeNzooUQLuam.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/9Pnoz73MLeNzooUQLuam.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/9Pnoz73MLeNzooUQLuam.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/9Pnoz73MLeNzooUQLuam.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/9Pnoz73MLeNzooUQLuam.png?auto=format&amp;w=1598 1598w&quot; width=&quot;799&quot; /&gt;
&lt;h2 id=&quot;block-network-requests-in-chrome-devtools&quot;&gt;Block network requests in Chrome DevTools &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/identify-slow-third-party-javascript/#block-network-requests-in-chrome-devtools&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Chrome DevTools &lt;a href=&quot;https://developer.chrome.com/docs/devtools/network/#block&quot; rel=&quot;noopener&quot;&gt;network request blocking&lt;/a&gt; allows you to see how your page behaves when a particular script, stylesheet, or other resource isn&#39;t available. After you identify third-party scripts that you suspect affect performance, measure how your load time changes by blocking the requests to those scripts.&lt;/p&gt;
&lt;p&gt;To enable request blocking:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Network&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Right-click any request in the &lt;strong&gt;Network&lt;/strong&gt; panel.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Block request URL&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;img alt=&quot;A screenshot of the context menu in the Chrome DevTools Performance panel. The &amp;#x27;Block request URL&amp;#x27; option is highlighted.&quot; decoding=&quot;async&quot; height=&quot;529&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/UbedvjrtP9si1l0X2QVA.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/UbedvjrtP9si1l0X2QVA.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/UbedvjrtP9si1l0X2QVA.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/UbedvjrtP9si1l0X2QVA.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/UbedvjrtP9si1l0X2QVA.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/UbedvjrtP9si1l0X2QVA.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/UbedvjrtP9si1l0X2QVA.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/UbedvjrtP9si1l0X2QVA.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/UbedvjrtP9si1l0X2QVA.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/UbedvjrtP9si1l0X2QVA.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/UbedvjrtP9si1l0X2QVA.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/UbedvjrtP9si1l0X2QVA.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/UbedvjrtP9si1l0X2QVA.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/UbedvjrtP9si1l0X2QVA.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/UbedvjrtP9si1l0X2QVA.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/UbedvjrtP9si1l0X2QVA.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/UbedvjrtP9si1l0X2QVA.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/UbedvjrtP9si1l0X2QVA.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;A &lt;strong&gt;Request blocking&lt;/strong&gt; tab will appear in the DevTools drawer. You can manage which requests have been blocked there.&lt;/p&gt;
&lt;p&gt;To measure the impact of third-party scripts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Measure how long your page takes to load using the &lt;strong&gt;Network&lt;/strong&gt; panel. To emulate real-world conditions, turn on &lt;a href=&quot;https://developer.chrome.com/docs/devtools/network/#throttle&quot; rel=&quot;noopener&quot;&gt;network throttling&lt;/a&gt; and &lt;a href=&quot;https://developer.chrome.com/blog/new-in-devtools-61/#throttling&quot; rel=&quot;noopener&quot;&gt;CPU throttling&lt;/a&gt;. (On faster connections and desktop hardware, the impact of expensive scripts may not be as representative as it would be on a mobile phone.)&lt;/li&gt;
&lt;li&gt;Block the URLs or domains responsible for third-party scripts you believe are an issue.&lt;/li&gt;
&lt;li&gt;Reload the page and re-measure how long it takes to load without the blocked third-party scripts.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You should hopefully see a speed improvement, but occasionally blocking third-party scripts might not have the effect you expect. If that&#39;s the case, reduce the list of blocked URLs until you isolate the one that&#39;s causing slowness.&lt;/p&gt;
&lt;p&gt;Note that doing three or more runs of measurement and looking at the median values will likely produce more stable results. As third-party content can occasionally pull in different resources per page load, this approach can give you a more realistic estimate. &lt;a href=&quot;https://twitter.com/ChromeDevTools/status/963820146388221952&quot; rel=&quot;noopener&quot;&gt;DevTools now supports multiple recordings&lt;/a&gt; in the &lt;strong&gt;Performance&lt;/strong&gt; panel, making this a little easier.&lt;/p&gt;
</content>
    <author>
      <name>Milica Mihajlija</name>
    </author>
  </entry>
  
  <entry>
    <title>Third-party JavaScript performance</title>
    <link href="https://web.dev/third-party-javascript/"/>
    <updated>2019-08-13T00:00:00Z</updated>
    <id>https://web.dev/third-party-javascript/</id>
    <content type="html" mode="escaped">&lt;p&gt;Third-party JavaScript generally refers to scripts embedded in your website that are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Not authored by you&lt;/li&gt;
&lt;li&gt;Served from third-party servers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sites use these scripts for various purposes, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Social sharing buttons&lt;/li&gt;
&lt;li&gt;Video player embeds&lt;/li&gt;
&lt;li&gt;Chat services&lt;/li&gt;
&lt;li&gt;Advertising iframes&lt;/li&gt;
&lt;li&gt;Analytics and metrics scripts&lt;/li&gt;
&lt;li&gt;A/B testing scripts for experiments&lt;/li&gt;
&lt;li&gt;Helper libraries (like date formatting, animation, and functional libraries)&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
  &lt;video autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; playsinline=&quot;&quot;&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/tcFciHGuF3MxnTr1y5ue01OGLBn2/uLXJ72jZAlzK56ctPwXd.mp4&quot; type=&quot;video/mp4&quot; /&gt;    &lt;/video&gt;
&lt;/figure&gt;
&lt;p&gt;Third-party scripts can provide powerful functionality, but that&#39;s not the whole story. They also affect privacy, security, and page behavior⁠—and they can be particularly problematic for performance.&lt;/p&gt;
&lt;h2 id=&quot;performance&quot;&gt;Performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/third-party-javascript/#performance&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Any significant amount of &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/bootup-time/&quot; rel=&quot;noopener&quot;&gt;JavaScript can slow down performance&lt;/a&gt;. But because third-party JavaScript is usually outside your control, it can bring additional issues.&lt;/p&gt;
&lt;h3 id=&quot;network&quot;&gt;Network &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/third-party-javascript/#network&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Setting up connections takes time, and sending too many requests to multiple servers causes slowdowns. That time is even longer for secure connections, which may involve DNS lookups, redirects, and several round trips to the final server that handles the user&#39;s request.&lt;/p&gt;
&lt;p&gt;Third-party scripts often add to network overhead with things such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Firing additional network requests&lt;/li&gt;
&lt;li&gt;Pulling in unoptimized images and videos&lt;/li&gt;
&lt;li&gt;Insufficient &lt;a href=&quot;https://web.dev/http-cache/&quot;&gt;HTTP caching&lt;/a&gt;, which forces frequent fetching of network resources&lt;/li&gt;
&lt;li&gt;Insufficient &lt;a href=&quot;https://web.dev/optimizing-content-efficiency-optimize-encoding-and-transfer/&quot;&gt;server compression&lt;/a&gt; of resources&lt;/li&gt;
&lt;li&gt;Multiple instances of frameworks and libraries pulled in by different third-party embeds&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;rendering&quot;&gt;Rendering &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/third-party-javascript/#rendering&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The way third-party JavaScript is loaded matters a lot. If it&#39;s done synchronously in the critical rendering path it delays parsing of the rest of the document.&lt;/p&gt;
&lt;aside class=&quot;aside flow color-secondary-box-text bg-secondary-box-bg&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Highlighter pen&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M10.22 9.49l-5.91 6c-.77.8-.7 2.05.08 2.85L.77 22h5.68l.74-.75c.78.81 1.95.86 2.73.05l5.96-6.05-5.66-5.76zm12.46-4l-2.82-2.87c-.78-.8-2.07-.84-2.84-.04l-5.75 5.85 5.66 5.75 5.69-5.78c.77-.81.83-2.11.06-2.91z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Key Term&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; The &lt;strong&gt;critical rendering path&lt;/strong&gt; includes all resources that the browser needs to display the first screen&#39;s worth of content. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;If a third party has server issues and fails to deliver a resource, rendering is blocked until the request times out, which can be anywhere from 10 to 80 seconds. You can test and simulate this problem with &lt;a href=&quot;https://css-tricks.com/use-webpagetest-api/#single-point-of-failure&quot; rel=&quot;noopener&quot;&gt;WebPageTest Single-Point-of-Failure tests&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; &lt;a href=&quot;https://web.dev/third-party-javascript/h/optimizing-content-efficiency-loading-third-party-javascript/#ab-test-smaller-samples-of-users&quot;&gt;A/B testing scripts&lt;/a&gt; can also often delay rendering. Most of them block content display until they complete processing—which can be true even for asynchronous A/B testing scripts. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;what-to-do-about-it&quot;&gt;What to do about it &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/third-party-javascript/#what-to-do-about-it&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Using third-party JavaScript is often unavoidable, but there are things you can do to minimize adverse effects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When choosing third-party resources, favor those that send the least amount of code while still giving you the functionality you need.&lt;/li&gt;
&lt;li&gt;Use &lt;a href=&quot;https://web.dev/use-lighthouse-for-performance-budgets/&quot;&gt;performance budgets&lt;/a&gt; for third-party content to keep their cost in check.&lt;/li&gt;
&lt;li&gt;Don&#39;t use the same functionality from two different vendors. You probably don&#39;t need two tag managers or two analytics platforms.&lt;/li&gt;
&lt;li&gt;Routinely audit and clean out redundant third-party scripts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To learn how to audit third-party content and load it efficiently for better performance and user experience, check out the other posts in the &lt;a href=&quot;https://web.dev/fast/#optimize-your-third-party-resources&quot;&gt;Optimize your third-party resources&lt;/a&gt; section.&lt;/p&gt;
</content>
    <author>
      <name>Milica Mihajlija</name>
    </author>
  </entry>
  
  <entry>
    <title>Optimize third-party JavaScript</title>
    <link href="https://web.dev/codelab-optimize-third-party-javascript/"/>
    <updated>2019-08-08T00:00:00Z</updated>
    <id>https://web.dev/codelab-optimize-third-party-javascript/</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; This codelab uses Chrome DevTools. &lt;a href=&quot;https://www.google.com/chrome&quot;&gt;Download Chrome&lt;/a&gt; if you don&#39;t already have it. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/third-party-javascript&quot;&gt;Third-party scripts impact performance&lt;/a&gt;, which is why it&#39;s important to &lt;a href=&quot;https://web.dev/identify-slow-third-party-javascript&quot;&gt;audit them regularly&lt;/a&gt; and use &lt;a href=&quot;https://web.dev/efficiently-load-third-party-javascript&quot;&gt;efficient techniques for loading&lt;/a&gt; them. This codelab shows you how to optimize the loading of third-party resources. It covers the following techniques:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Deferring script loading&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Lazy loading non-critical resources&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Preconnecting to required origins&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The included sample app features a simple web page with three features coming from third-party sources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A video embed&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A data-visualization library for rendering a line graph&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A social media sharing widget&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of the page with third-party resources highlighted.&quot; decoding=&quot;async&quot; height=&quot;1294&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/cuWcC16X6oKRbXJXIIt7.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/cuWcC16X6oKRbXJXIIt7.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/cuWcC16X6oKRbXJXIIt7.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/cuWcC16X6oKRbXJXIIt7.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/cuWcC16X6oKRbXJXIIt7.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/cuWcC16X6oKRbXJXIIt7.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/cuWcC16X6oKRbXJXIIt7.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/cuWcC16X6oKRbXJXIIt7.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/cuWcC16X6oKRbXJXIIt7.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/cuWcC16X6oKRbXJXIIt7.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/cuWcC16X6oKRbXJXIIt7.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/cuWcC16X6oKRbXJXIIt7.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/cuWcC16X6oKRbXJXIIt7.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/cuWcC16X6oKRbXJXIIt7.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/cuWcC16X6oKRbXJXIIt7.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/cuWcC16X6oKRbXJXIIt7.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/cuWcC16X6oKRbXJXIIt7.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/cuWcC16X6oKRbXJXIIt7.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Third-party resources in the sample app.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You&#39;ll start by measuring the performance of the app and then apply each technique to improve different aspects of app performance.&lt;/p&gt;
&lt;h2 id=&quot;measure-performance&quot;&gt;Measure performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-optimize-third-party-javascript/#measure-performance&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;First open the sample app in the fullscreen view:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Remix to Edit&lt;/strong&gt; to make the project editable.&lt;/li&gt;
&lt;li&gt;To preview the site, press &lt;strong&gt;View App&lt;/strong&gt;. Then press
&lt;strong&gt;Fullscreen&lt;/strong&gt;
&lt;img src=&quot;https://web.dev/images/glitch/fullscreen.svg&quot; alt=&quot;fullscreen&quot; style=&quot;padding: 4px 8px; opacity: .5; border: 1px solid #c3c3c3; border-radius: 5px; margin-top: 0;&quot; /&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Run a &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt; &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/&quot; rel=&quot;noopener&quot;&gt;performance audit&lt;/a&gt; on the page to establish baseline performance:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Lighthouse&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Mobile&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;Performance&lt;/strong&gt; checkbox. (You can clear the rest of the checkboxes in the Audits section.)&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Simulated Fast 3G, 4x CPU Slowdown&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;Clear Storage&lt;/strong&gt; checkbox.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Run audits&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When you run an audit on your machine, &lt;a href=&quot;https://developers.google.com/web/tools/lighthouse/variability&quot; rel=&quot;noopener&quot;&gt;the exact results may vary&lt;/a&gt;, but you should notice that the &lt;a href=&quot;https://web.dev/fcp/&quot;&gt;First Contentful Paint (FCP)&lt;/a&gt; time is pretty high, and that Lighthouse suggests two opportunities to investigate: &lt;strong&gt;Eliminate render-blocking resources&lt;/strong&gt; and &lt;strong&gt;Preconnect to required origins&lt;/strong&gt;. (Even if the metrics are all in the green, optimizations will still yield improvements.)&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of Lighthouse audit showing 2.4 second FCP and two opportunities: Eliminate render-blocking resources and Preconnect to required origins.&quot; decoding=&quot;async&quot; height=&quot;700&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 741px) 741px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/gIkIJM2OaocxImjLLjev.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/gIkIJM2OaocxImjLLjev.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/gIkIJM2OaocxImjLLjev.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/gIkIJM2OaocxImjLLjev.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/gIkIJM2OaocxImjLLjev.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/gIkIJM2OaocxImjLLjev.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/gIkIJM2OaocxImjLLjev.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/gIkIJM2OaocxImjLLjev.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/gIkIJM2OaocxImjLLjev.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/gIkIJM2OaocxImjLLjev.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/gIkIJM2OaocxImjLLjev.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/gIkIJM2OaocxImjLLjev.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/gIkIJM2OaocxImjLLjev.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/gIkIJM2OaocxImjLLjev.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/gIkIJM2OaocxImjLLjev.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/gIkIJM2OaocxImjLLjev.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/gIkIJM2OaocxImjLLjev.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/gIkIJM2OaocxImjLLjev.png?auto=format&amp;w=1482 1482w&quot; width=&quot;741&quot; /&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;defer-third-party-javascript&quot;&gt;Defer third-party JavaScript &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-optimize-third-party-javascript/#defer-third-party-javascript&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;Eliminate render-blocking resources&lt;/strong&gt; audit identified that you can save some time by deferring a script coming from d3js.org:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of Eliminate render-blocking resources audit with the d3.v3.min.js script highlighted.&quot; decoding=&quot;async&quot; height=&quot;337&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 718px) 718px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/P9ejh4JMzdpu8N3aZ7bC.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/P9ejh4JMzdpu8N3aZ7bC.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/P9ejh4JMzdpu8N3aZ7bC.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/P9ejh4JMzdpu8N3aZ7bC.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/P9ejh4JMzdpu8N3aZ7bC.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/P9ejh4JMzdpu8N3aZ7bC.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/P9ejh4JMzdpu8N3aZ7bC.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/P9ejh4JMzdpu8N3aZ7bC.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/P9ejh4JMzdpu8N3aZ7bC.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/P9ejh4JMzdpu8N3aZ7bC.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/P9ejh4JMzdpu8N3aZ7bC.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/P9ejh4JMzdpu8N3aZ7bC.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/P9ejh4JMzdpu8N3aZ7bC.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/P9ejh4JMzdpu8N3aZ7bC.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/P9ejh4JMzdpu8N3aZ7bC.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/P9ejh4JMzdpu8N3aZ7bC.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/P9ejh4JMzdpu8N3aZ7bC.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/P9ejh4JMzdpu8N3aZ7bC.png?auto=format&amp;w=1436 1436w&quot; width=&quot;718&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;https://d3js.org/&quot; rel=&quot;noopener&quot;&gt;D3.js&lt;/a&gt; is a JavaScript library for creating data visualizations. The &lt;code&gt;script.js&lt;/code&gt; file in the sample app uses D3 utility functions to create the SVG line chart and append it to the page. The order of operations here matters: &lt;code&gt;script.js&lt;/code&gt; has to run after the document is parsed and the D3 library has loaded, which is why it&#39;s included right before the closing &lt;code&gt;&amp;lt;/body&amp;gt;&lt;/code&gt; tag in &lt;code&gt;index.html&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;However, the D3 script is included in the page&#39;s &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;, which blocks the parsing of the rest document:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of index.html with highlighted script tag in the head.&quot; decoding=&quot;async&quot; height=&quot;265&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 718px) 718px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/vRP2oYmijq0sVyLRb2nU.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/vRP2oYmijq0sVyLRb2nU.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/vRP2oYmijq0sVyLRb2nU.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/vRP2oYmijq0sVyLRb2nU.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/vRP2oYmijq0sVyLRb2nU.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/vRP2oYmijq0sVyLRb2nU.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/vRP2oYmijq0sVyLRb2nU.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/vRP2oYmijq0sVyLRb2nU.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/vRP2oYmijq0sVyLRb2nU.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/vRP2oYmijq0sVyLRb2nU.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/vRP2oYmijq0sVyLRb2nU.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/vRP2oYmijq0sVyLRb2nU.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/vRP2oYmijq0sVyLRb2nU.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/vRP2oYmijq0sVyLRb2nU.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/vRP2oYmijq0sVyLRb2nU.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/vRP2oYmijq0sVyLRb2nU.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/vRP2oYmijq0sVyLRb2nU.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/vRP2oYmijq0sVyLRb2nU.png?auto=format&amp;w=1436 1436w&quot; width=&quot;718&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Two magic attributes can unblock the parser when added to the script tag:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&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; ensures that scripts download in the background and execute &lt;strong&gt;at the first opportunity&lt;/strong&gt; after they finish downloading.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/script#attr-defer&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;defer&lt;/code&gt;&lt;/a&gt; ensures that scripts download in the background and execute &lt;strong&gt;after parsing&lt;/strong&gt; is completely finished.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since this chart is not really critical to the overall page and will most likely be below the fold, use &lt;code&gt;defer&lt;/code&gt; to make sure there&#39;s no parser blocking.&lt;/p&gt;
&lt;h3 id=&quot;step-1-load-the-script-asynchronously-with-the-defer-attribute&quot;&gt;Step 1: Load the script asynchronously with the &lt;code&gt;defer&lt;/code&gt; attribute &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-optimize-third-party-javascript/#step-1-load-the-script-asynchronously-with-the-defer-attribute&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;On line 17 in &lt;code&gt;index.html&lt;/code&gt;, add the &lt;code&gt;defer&lt;/code&gt; attribute to the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; element:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;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;https://d3js.org/d3.v3.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;defer&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;h3 id=&quot;step-2-ensure-the-correct-order-of-operations&quot;&gt;Step 2: Ensure the correct order of operations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-optimize-third-party-javascript/#step-2-ensure-the-correct-order-of-operations&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Now that D3 is deferred, &lt;code&gt;script.js&lt;/code&gt; will run before D3 is ready, resulting in an error.&lt;/p&gt;
&lt;p&gt;Scripts with the &lt;code&gt;defer&lt;/code&gt; attribute execute in the order in which they were specified. To ensure &lt;code&gt;script.js&lt;/code&gt; gets executed after D3 is ready, add &lt;code&gt;defer&lt;/code&gt; to it and move it up to the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of the document, right after the D3 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; element. Now it no longer blocks the parser, and the download starts sooner.&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;https://d3js.org/d3.v3.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;defer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;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;./script.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;defer&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;h2 id=&quot;lazy-load-third-party-resources&quot;&gt;Lazy-load third-party resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-optimize-third-party-javascript/#lazy-load-third-party-resources&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;All resources that are below the fold are good candidates for &lt;a href=&quot;https://web.dev/efficiently-load-third-party-javascript/#lazy-load-third-party-resources&quot;&gt;lazy loading&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The sample app has a YouTube video embedded in an &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/iframe&quot; rel=&quot;noopener&quot;&gt;iframe&lt;/a&gt;. To check out how many requests the page makes and which come from the embedded YouTube iframe:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;To preview the site, press &lt;strong&gt;View App&lt;/strong&gt;. Then press
&lt;strong&gt;Fullscreen&lt;/strong&gt;
&lt;img src=&quot;https://web.dev/images/glitch/fullscreen.svg&quot; alt=&quot;fullscreen&quot; style=&quot;padding: 4px 8px; opacity: .5; border: 1px solid #c3c3c3; border-radius: 5px; margin-top: 0;&quot; /&gt;.&lt;/li&gt;
&lt;li&gt;Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Network&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;Disable cache&lt;/strong&gt; checkbox.&lt;/li&gt;
&lt;li&gt;Select &lt;em&gt;Fast 3G&lt;/em&gt; in the &lt;strong&gt;Throttling&lt;/strong&gt; dropdown menu.&lt;/li&gt;
&lt;li&gt;Reload the page.&lt;/li&gt;
&lt;/ol&gt;
&lt;img alt=&quot;Screenshot of DevTools Network panel.&quot; decoding=&quot;async&quot; height=&quot;618&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 783px) 783px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/DjnRutx27trV4YkYcKhr.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/DjnRutx27trV4YkYcKhr.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/DjnRutx27trV4YkYcKhr.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/DjnRutx27trV4YkYcKhr.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/DjnRutx27trV4YkYcKhr.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/DjnRutx27trV4YkYcKhr.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/DjnRutx27trV4YkYcKhr.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/DjnRutx27trV4YkYcKhr.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/DjnRutx27trV4YkYcKhr.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/DjnRutx27trV4YkYcKhr.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/DjnRutx27trV4YkYcKhr.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/DjnRutx27trV4YkYcKhr.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/DjnRutx27trV4YkYcKhr.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/DjnRutx27trV4YkYcKhr.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/DjnRutx27trV4YkYcKhr.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/DjnRutx27trV4YkYcKhr.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/DjnRutx27trV4YkYcKhr.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/DjnRutx27trV4YkYcKhr.png?auto=format&amp;w=1566 1566w&quot; width=&quot;783&quot; /&gt;
&lt;p&gt;The &lt;strong&gt;Network&lt;/strong&gt; panel reveals that the page made a total of 28 requests and transferred almost 1 MB of compressed resources.&lt;/p&gt;
&lt;p&gt;To identify the requests that the YouTube &lt;code&gt;iframe&lt;/code&gt; made, look for the video ID &lt;code&gt;6lfaiXM6waw&lt;/code&gt; in the &lt;strong&gt;Initiator&lt;/strong&gt; column. To group together all the requests by domain:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;In the &lt;strong&gt;Network&lt;/strong&gt; panel, right-click a column title.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the dropdown menu, select the &lt;strong&gt;Domains&lt;/strong&gt; column.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To sort the requests by domain, click the &lt;strong&gt;Domains&lt;/strong&gt; column title.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The new sorting reveals that there are additional requests to Google domains. In total, the YouTube iframe makes 14 requests for scripts, stylesheets, images, and fonts. But unless users actually scroll down to play the video, they don&#39;t really need all those assets.&lt;/p&gt;
&lt;p&gt;By waiting to lazy-load the video until a user scrolls down to that section of the page, you cut the number of requests the page initially makes. This approach saves users&#39; data and speeds up the initial load.&lt;/p&gt;
&lt;p&gt;One way to implement lazy loading is by using the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Intersection_Observer_API&quot; rel=&quot;noopener&quot;&gt;Intersection Observer&lt;/a&gt;, a browser API that notifies you when an element enters or exits the browser&#39;s viewport.&lt;/p&gt;
&lt;h3 id=&quot;step-1-prevent-video-from-loading-initially&quot;&gt;Step 1: Prevent video from loading initially &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-optimize-third-party-javascript/#step-1-prevent-video-from-loading-initially&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To lazy-load the video iframe, you must first prevent it from loading in the usual way. Do that by replacing the &lt;code&gt;src&lt;/code&gt; attribute with the &lt;code&gt;data-src&lt;/code&gt; attribute to specify the video URL:&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;iframe&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;560&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;315&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;https://www.youtube.com/embed/lS9D6w1GzGY&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;frameborder&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;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&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;allow&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;allowfullscreen&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;iframe&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;code&gt;data-src&lt;/code&gt; is a &lt;a href=&quot;https://developer.mozilla.org/docs/Learn/HTML/Howto/Use_data_attributes&quot; rel=&quot;noopener&quot;&gt;data attribute&lt;/a&gt;, which allows you to store extra information on standard HTML elements. A data attribute can be named anything, as long as it starts with &amp;quot;data-&amp;quot;.&lt;/p&gt;
&lt;p&gt;An iframe without a &lt;code&gt;src&lt;/code&gt; simply won&#39;t load.&lt;/p&gt;
&lt;h3 id=&quot;step-2-use-intersection-observer-to-lazy-load-the-video&quot;&gt;Step 2: Use Intersection Observer to lazy-load the video &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-optimize-third-party-javascript/#step-2-use-intersection-observer-to-lazy-load-the-video&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To load the video when a user scrolls to it you need to know when that happens. That&#39;s where the Intersection Observer API steps in. The Intersection Observer API lets you register a callback function that&#39;s executed whenever an element you want to track enters or exits the viewport.&lt;/p&gt;
&lt;p&gt;To get started, create a new file and name it &lt;code&gt;lazy-load.js&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;New File&lt;/strong&gt; and give it a name.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add This File&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Add the script tag to your document head:&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;/lazy-load.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;defer&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;In &lt;code&gt;lazy-load.js&lt;/code&gt;, create a new &lt;code&gt;IntersectionObserver&lt;/code&gt; and pass it a callback function to run:&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;// create a new Intersection Observer&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;let&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;IntersectionObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;callback&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;Now give &lt;code&gt;observer&lt;/code&gt; a target element to watch (the video iframe in this case) by passing it as an argument in the &lt;code&gt;observe&lt;/code&gt; method:&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;// the element that you want to watch&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; element &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;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;iframe&#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;// register the element with the observe method&lt;/span&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;element&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;callback&lt;/code&gt; receives a list of &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/IntersectionObserverEntry&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;IntersectionObserverEntry&lt;/code&gt;&lt;/a&gt; objects and the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/IntersectionObserver&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;IntersectionObserver&lt;/code&gt;&lt;/a&gt; object itself. Each entry contains a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/IntersectionObserverEntry/target&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;target&lt;/code&gt;&lt;/a&gt; element and properties that describe its dimensions, position, the time it entered the viewport, and more. One of the properties of &lt;code&gt;IntersectionObserverEntry&lt;/code&gt; is &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/IntersectionObserverEntry/isIntersecting&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;isIntersecting&lt;/code&gt;&lt;/a&gt;—a boolean value that equals &lt;code&gt;true&lt;/code&gt; when the element enters the viewport.&lt;/p&gt;
&lt;p&gt;In this example, the &lt;code&gt;target&lt;/code&gt; is the &lt;code&gt;iframe&lt;/code&gt;. &lt;code&gt;isIntersecting&lt;/code&gt; equals &lt;code&gt;true&lt;/code&gt; when &lt;code&gt;target&lt;/code&gt; enters the viewport. To see this in action, replace &lt;code&gt;callback&lt;/code&gt; with the following function:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;del class=&quot;highlight-line highlight-line-remove&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&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;IntersectionObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;callback&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/del&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&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;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;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&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 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;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&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;target&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&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;isIntersecting&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&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;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&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;/ins&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;To preview the site, press &lt;strong&gt;View App&lt;/strong&gt;. Then press
&lt;strong&gt;Fullscreen&lt;/strong&gt;
&lt;img src=&quot;https://web.dev/images/glitch/fullscreen.svg&quot; alt=&quot;fullscreen&quot; style=&quot;padding: 4px 8px; opacity: .5; border: 1px solid #c3c3c3; border-radius: 5px; margin-top: 0;&quot; /&gt;.&lt;/li&gt;
&lt;li&gt;Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Console&lt;/strong&gt; tab.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Try scrolling up and down. You should see the value of &lt;code&gt;isIntersecting&lt;/code&gt; change and the target element logged to the console.&lt;/p&gt;
&lt;p&gt;To load the video when the user scrolls to its position, use &lt;code&gt;isIntersecting&lt;/code&gt; as a condition to run a &lt;code&gt;loadElement&lt;/code&gt; function, which gets the value from the &lt;code&gt;iframe&lt;/code&gt; element&#39;s &lt;code&gt;data-src&lt;/code&gt; and sets it as the &lt;code&gt;iframe&lt;/code&gt; element&#39;s &lt;code&gt;src&lt;/code&gt; attribute. That replacement triggers the loading of the video. Then, once the video is loaded, call the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/IntersectionObserver/unobserve&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;unobserve&lt;/code&gt;&lt;/a&gt; method on the &lt;code&gt;observer&lt;/code&gt; to stop watching the target element:&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;&lt;span class=&quot;token keyword&quot;&gt;let&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;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;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&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 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;/span&gt;&lt;br /&gt;&lt;del class=&quot;highlight-line highlight-line-remove&quot;&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;target&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/del&gt;&lt;br /&gt;&lt;del class=&quot;highlight-line highlight-line-remove&quot;&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;isIntersecting&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/del&gt;&lt;br /&gt;&lt;del class=&quot;highlight-line highlight-line-remove&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;/del&gt;&lt;br /&gt;&lt;del class=&quot;highlight-line highlight-line-remove&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;/del&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&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;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;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;      &lt;span class=&quot;token comment&quot;&gt;// do this when the element enters the viewport&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;      &lt;span class=&quot;token function&quot;&gt;loadElement&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;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;      &lt;span class=&quot;token comment&quot;&gt;// stop watching&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;      observer&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;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&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;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&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;/ins&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;loadElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; element&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;data-src&#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;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;  element&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; src&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/ins&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;step-3-reevaluate-performance&quot;&gt;Step 3: Reevaluate performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-optimize-third-party-javascript/#step-3-reevaluate-performance&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To see how the size and number of resources changed, open the DevTools &lt;strong&gt;Network&lt;/strong&gt; panel and reload the page again. The &lt;strong&gt;Network&lt;/strong&gt; panel reveals that the page made 14 requests and only 260 KB. That&#39;s a meaningful improvement!&lt;/p&gt;
&lt;p&gt;Now scroll down the page and keep an eye on the &lt;strong&gt;Network&lt;/strong&gt; panel. When you get to the video, you should see the page trigger additional requests.&lt;/p&gt;
&lt;p&gt;&lt;video autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; playsinline=&quot;&quot;&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/tcFciHGuF3MxnTr1y5ue01OGLBn2/bJHFfNjxvIthfRWeidbx.mp4&quot; type=&quot;video/mp4&quot; /&gt;    &lt;/video&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; In this example, the element is loaded when it enters the viewport, which you can see happening in the video above. You can avoid that delay and create a smoother user experience by loading elements a little before they enter the viewport. To do that, use the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Intersection_Observer_API#Creating_an_intersection_observer&quot;&gt;&lt;code&gt;rootMargin&lt;/code&gt;&lt;/a&gt; property to define margins around the target element, which effectively grows (or shrinks) the area that triggers the &lt;code&gt;isIntersecting&lt;/code&gt; change. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;preconnect-to-required-origins&quot;&gt;Preconnect to required origins &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-optimize-third-party-javascript/#preconnect-to-required-origins&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You&#39;ve deferred non-critical JavaScript and lazy-loaded the YouTube requests, so now it&#39;s time to optimize the remaining third-party content.&lt;/p&gt;
&lt;p&gt;Adding the &lt;code&gt;rel=preconnect&lt;/code&gt; attribute to a link tells the browser to establish a connection to a domain before the request for that resource is made. This attribute is best used on origins that provide resources you are certain the page needs.&lt;/p&gt;
&lt;p&gt;The Lighthouse audit you ran in the first step suggested in &lt;strong&gt;Preconnect to required origins&lt;/strong&gt; that you can save around 400 ms by &lt;a href=&quot;https://web.dev/preconnect-and-dns-prefetch/&quot;&gt;establishing early connections&lt;/a&gt; to staticxx.facebook.com and youtube.com:&lt;/p&gt;
&lt;img alt=&quot;Preconnect to required origins audit with the staticxx.facebook.com domain highlighted.&quot; decoding=&quot;async&quot; height=&quot;279&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 720px) 720px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/k4PDdjh77dXS1ZdGBSko.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/k4PDdjh77dXS1ZdGBSko.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/k4PDdjh77dXS1ZdGBSko.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/k4PDdjh77dXS1ZdGBSko.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/k4PDdjh77dXS1ZdGBSko.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/k4PDdjh77dXS1ZdGBSko.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/k4PDdjh77dXS1ZdGBSko.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/k4PDdjh77dXS1ZdGBSko.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/k4PDdjh77dXS1ZdGBSko.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/k4PDdjh77dXS1ZdGBSko.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/k4PDdjh77dXS1ZdGBSko.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/k4PDdjh77dXS1ZdGBSko.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/k4PDdjh77dXS1ZdGBSko.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/k4PDdjh77dXS1ZdGBSko.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/k4PDdjh77dXS1ZdGBSko.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/k4PDdjh77dXS1ZdGBSko.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/k4PDdjh77dXS1ZdGBSko.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/k4PDdjh77dXS1ZdGBSko.png?auto=format&amp;w=1440 1440w&quot; width=&quot;720&quot; /&gt;
&lt;p&gt;Since the YouTube video is now lazy-loaded, that leaves only staticxx.facebook.com, the source of the social media sharing widget. Establishing an early connection to this domain is as simple as adding a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag to the document&#39;s &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;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://staticxx.facebook.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;h2 id=&quot;reevaluate-performance&quot;&gt;Reevaluate performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-optimize-third-party-javascript/#reevaluate-performance&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here&#39;s the state of the page &lt;a href=&quot;https://glitch.com/~3rd-party-optimizations&quot; rel=&quot;noopener&quot;&gt;after optimization&lt;/a&gt;. Follow the steps from the &lt;a href=&quot;https://web.dev/codelab-optimize-third-party-javascript/#measure-performance&quot;&gt;Measure performance&lt;/a&gt; section of the codelab to run another Lighthouse audit.&lt;/p&gt;
&lt;img alt=&quot;Lighthouse audit showing 1 second FCP and the performance score of 99.&quot; decoding=&quot;async&quot; height=&quot;511&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 727px) 727px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/Iv12shtGoURq2CTqO6jN.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/Iv12shtGoURq2CTqO6jN.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/Iv12shtGoURq2CTqO6jN.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/Iv12shtGoURq2CTqO6jN.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/Iv12shtGoURq2CTqO6jN.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/Iv12shtGoURq2CTqO6jN.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/Iv12shtGoURq2CTqO6jN.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/Iv12shtGoURq2CTqO6jN.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/Iv12shtGoURq2CTqO6jN.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/Iv12shtGoURq2CTqO6jN.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/Iv12shtGoURq2CTqO6jN.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/Iv12shtGoURq2CTqO6jN.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/Iv12shtGoURq2CTqO6jN.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/Iv12shtGoURq2CTqO6jN.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/Iv12shtGoURq2CTqO6jN.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/Iv12shtGoURq2CTqO6jN.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/Iv12shtGoURq2CTqO6jN.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/Iv12shtGoURq2CTqO6jN.png?auto=format&amp;w=1454 1454w&quot; width=&quot;727&quot; /&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;Success&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; You should see that FCP and the performance score have improved! 🎉 The page now loads faster and consumes less data⁠—and all without negatively affecting the user-experience. 🤝 &lt;/div&gt;&lt;/aside&gt;
</content>
    <author>
      <name>Milica Mihajlija</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>Adapt video to image serving based on network quality</title>
    <link href="https://web.dev/codelab-adapt-video-to-image-serving-based-on-network-quality/"/>
    <updated>2019-07-08T00:00:00Z</updated>
    <id>https://web.dev/codelab-adapt-video-to-image-serving-based-on-network-quality/</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; This codelab uses Chrome DevTools. &lt;a href=&quot;https://www.google.com/chrome&quot;&gt;Download Chrome&lt;/a&gt; if you don&#39;t already have it. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;In this codelab, you&#39;ll learn how to adapt your content based on the network
quality. This page&#39;s background video will load only when users are on a fast
network. On slower networks, an image will load instead.&lt;/p&gt;
&lt;p&gt;The
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/NetworkInformation&quot; rel=&quot;noopener&quot;&gt;Network Information API&lt;/a&gt;
enables you to access information about the user&#39;s connection quality. You will
use its &lt;code&gt;effectiveType&lt;/code&gt; property to decide when to serve a video and when to
serve an image. &lt;code&gt;effectiveType&lt;/code&gt; can be &lt;code&gt;&#39;slow-2g&#39;&lt;/code&gt;, &lt;code&gt;&#39;2g&#39;&lt;/code&gt;, &lt;code&gt;&#39;3g&#39;&lt;/code&gt;, or &lt;code&gt;&#39;4g&#39;&lt;/code&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 61, 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;
      61
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox, Not supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Edge 79, Supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
79
&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Safari, Not supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;p&gt;&lt;/p&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/NetworkInformation#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;h2 id=&quot;step-1-check-connection-type&quot;&gt;Step 1: Check connection type &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-adapt-video-to-image-serving-based-on-network-quality/#step-1-check-connection-type&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;index.html&lt;/code&gt; file contains a &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag to display the background video (line 22). The code in &lt;code&gt;script.js&lt;/code&gt; loads the video by setting the video tag&#39;s &lt;code&gt;src&lt;/code&gt; attribute. (The video loading code is described in more detail in &lt;a href=&quot;https://web.dev/codelab-adapt-video-to-image-serving-based-on-network-quality/#step-2-load-video&quot;&gt;Step 2&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;To load the video conditionally, first check if the Network Information API is available; if it is, check the connection type.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In &lt;code&gt;script.js&lt;/code&gt;, add an &lt;code&gt;if&lt;/code&gt; statement that tests whether the &lt;code&gt;navigator.connection&lt;/code&gt; object exists and whether it has the &lt;code&gt;effectiveType&lt;/code&gt; property.&lt;/li&gt;
&lt;li&gt;Add an &lt;code&gt;if&lt;/code&gt; statement to check the &lt;code&gt;effectiveType&lt;/code&gt; of the network.&lt;/li&gt;
&lt;/ol&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;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connection &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&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;effectiveType&lt;span 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;effectiveType &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;4g&#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 load video on the fastest connections.&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;// In any other case load the image.&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;Wrap the existing video loading code in an &lt;code&gt;else&lt;/code&gt; statement, so that video will
still load in browsers that don&#39;t support the Network Information API.&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;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connection &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&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;effectiveType&lt;span 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;effectiveType &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;4g&#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;// video loading code&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;// image loading code&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 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 keyword&quot;&gt;const&lt;/span&gt; video &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;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;coverVideo&#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; videoSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;data-src&#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;  video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;src&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; videoSource&lt;span 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;  video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;style&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;height: 100%; width: 100%; display:inline&#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;h2 id=&quot;step-2-load-video&quot;&gt;Step 2: Load video &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-adapt-video-to-image-serving-based-on-network-quality/#step-2-load-video&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If the &lt;code&gt;effectiveType&lt;/code&gt; is &lt;code&gt;&#39;4g&#39;&lt;/code&gt;, use the video loading code from the
beginning of the codelab.&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;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;effectiveType &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;4g&#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; video &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;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;coverVideo&#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; videoSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;data-src&#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;  video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;src&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; videoSource&lt;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;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;style&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;height: 100%; width: 100%; display:inline&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// image loading code&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;Here&#39;s how the video loading code works: the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag doesn&#39;t download or display anything at first because its &lt;code&gt;src&lt;/code&gt; attribute is not set. The video URL to load is specified using the &lt;code&gt;data-src&lt;/code&gt; attribute.&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;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;coverVideo&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;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;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;https://cdn.glitch.com/b6491350-b058-4eb6-aa6c-55c93122073e%2FMatrix%2C%20Console%2C%20Hacking%2C%20Code.mp4?1551464245607&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;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;&lt;a href=&quot;https://developer.mozilla.org/docs/Learn/HTML/Howto/Use_data_attributes&quot; rel=&quot;noopener&quot;&gt;Data attributes&lt;/a&gt; allow you to store extra information on standard HTML elements. A data element can be named anything, as long as it starts with &amp;quot;data-&amp;quot;.&lt;/p&gt;
&lt;p&gt;To actually display the video on the page, you need to get the value from &lt;code&gt;data-src&lt;/code&gt; and set it as the video element&#39;s &lt;code&gt;src&lt;/code&gt; attribute.&lt;/p&gt;
&lt;p&gt;First, get the DOM element that contains the asset:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; video &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;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;coverVideo&#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;Then get the resource location from the &lt;code&gt;data-src&lt;/code&gt; attribute:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; videoSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;data-src&#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;And finally set that as the &lt;code&gt;src&lt;/code&gt; attribute of the video element:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;src&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; videoSource&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 last line takes care of CSS positioning:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;style&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;height: 100%; width: 100%; display:inline&#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;h2 id=&quot;step-3-load-image&quot;&gt;Step 3: Load image &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-adapt-video-to-image-serving-based-on-network-quality/#step-3-load-image&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To conditionally load an image on slower networks, use the same strategy as for
the video.&lt;/p&gt;
&lt;p&gt;Add an image element to &lt;code&gt;index.html&lt;/code&gt; (right after the video element), and use
the &lt;code&gt;data-src&lt;/code&gt; attribute instead of the &lt;code&gt;src&lt;/code&gt; attribute.&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;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;coverImage&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;https://cdn.glitch.com/36529535-5976-40f8-979b-40c898b86bd0%2F36529535-5976-40f8-979b-40c898b86bd0_1_Sn80dgiwpMjBVrqjfiDbnA.jpg?1553003835358&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;In &lt;code&gt;script.js&lt;/code&gt;, add code to set the image&#39;s &lt;code&gt;src&lt;/code&gt; attribute depending on the
&lt;code&gt;effectiveType&lt;/code&gt; of the network.&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;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;effectiveType &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;4g&#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; video &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;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;coverVideo&#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; videoSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;data-src&#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;  video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;src&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; videoSource&lt;span 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;  video&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;style&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;height: 100%; width: 100%; display:inline&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; image &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;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;coverImage&#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; imageSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; image&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;data-src&#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;  image&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;src&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; imageSource&lt;span 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;  image&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;style&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;height: 100%; width: 100%; display:inline&#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;h2 id=&quot;try-it-out&quot;&gt;Try it out &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-adapt-video-to-image-serving-based-on-network-quality/#try-it-out&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To test it yourself:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;To preview the site, press &lt;strong&gt;View App&lt;/strong&gt;. Then press
&lt;strong&gt;Fullscreen&lt;/strong&gt;
&lt;img src=&quot;https://web.dev/images/glitch/fullscreen.svg&quot; alt=&quot;fullscreen&quot; style=&quot;padding: 4px 8px; opacity: .5; border: 1px solid #c3c3c3; border-radius: 5px; margin-top: 0;&quot; /&gt;.&lt;/li&gt;
&lt;li&gt;Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Network&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Throttling&lt;/strong&gt; drop-down, which is set to &lt;strong&gt;No throttling&lt;/strong&gt; by default. Select &lt;strong&gt;Fast 3G&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;img alt=&quot;DevTools Network tab with Fast 3G throttling option highlighted&quot; decoding=&quot;async&quot; height=&quot;198&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 723px) 723px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/um0BRQOmsqGgZzWeLDHY.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/um0BRQOmsqGgZzWeLDHY.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/um0BRQOmsqGgZzWeLDHY.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/um0BRQOmsqGgZzWeLDHY.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/um0BRQOmsqGgZzWeLDHY.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/um0BRQOmsqGgZzWeLDHY.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/um0BRQOmsqGgZzWeLDHY.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/um0BRQOmsqGgZzWeLDHY.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/um0BRQOmsqGgZzWeLDHY.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/um0BRQOmsqGgZzWeLDHY.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/um0BRQOmsqGgZzWeLDHY.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/um0BRQOmsqGgZzWeLDHY.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/um0BRQOmsqGgZzWeLDHY.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/um0BRQOmsqGgZzWeLDHY.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/um0BRQOmsqGgZzWeLDHY.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/um0BRQOmsqGgZzWeLDHY.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/um0BRQOmsqGgZzWeLDHY.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/um0BRQOmsqGgZzWeLDHY.png?auto=format&amp;w=1446 1446w&quot; width=&quot;723&quot; /&gt;
&lt;p&gt;Now reload the page with Fast 3G still enabled. The app loads an image in the background instead of the video:&lt;/p&gt;
&lt;img alt=&quot;Matrix-like image background with &amp;#x27;NETWORK INFORMATION&amp;#x27; text overlay&quot; decoding=&quot;async&quot; height=&quot;456&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/wtQs6oCU8c5q23SQOnHo.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/wtQs6oCU8c5q23SQOnHo.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/wtQs6oCU8c5q23SQOnHo.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/wtQs6oCU8c5q23SQOnHo.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/wtQs6oCU8c5q23SQOnHo.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/wtQs6oCU8c5q23SQOnHo.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/wtQs6oCU8c5q23SQOnHo.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/wtQs6oCU8c5q23SQOnHo.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/wtQs6oCU8c5q23SQOnHo.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/wtQs6oCU8c5q23SQOnHo.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/wtQs6oCU8c5q23SQOnHo.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/wtQs6oCU8c5q23SQOnHo.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/wtQs6oCU8c5q23SQOnHo.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/wtQs6oCU8c5q23SQOnHo.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/wtQs6oCU8c5q23SQOnHo.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/wtQs6oCU8c5q23SQOnHo.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/wtQs6oCU8c5q23SQOnHo.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/wtQs6oCU8c5q23SQOnHo.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h2 id=&quot;extra-credit-respond-to-changes&quot;&gt;Extra Credit: Respond to changes &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-adapt-video-to-image-serving-based-on-network-quality/#extra-credit-respond-to-changes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Remember how this API has an &lt;code&gt;onchange&lt;/code&gt;
&lt;a href=&quot;https://web.dev/adaptive-serving-based-on-network-quality#how-it-works&quot;&gt;event listener&lt;/a&gt;?
You can use it for many things: dynamically adapting content such as video quality; restarting deferred data transfers when a change to a high-bandwidth network type is detected; or notifying users when the network quality changes.&lt;/p&gt;
&lt;p&gt;Here&#39;s a simple example of how to use this listener. Add it to &lt;code&gt;script.js&lt;/code&gt;. This
code will call the &lt;code&gt;displayNetworkInfo&lt;/code&gt; function whenever the network
information changes.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&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;&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;change&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; displayNetworkInfo&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;There&#39;s already an empty &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; element on the &lt;code&gt;index.html&lt;/code&gt; page. Now define the
&lt;code&gt;displayNetworkInfo&lt;/code&gt; function so that it displays the network information in the
&lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; element and invoke the 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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;displayNetworkInfo&lt;/span&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;  document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&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 punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;innerHTML &lt;span class=&quot;token operator&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;effectiveType&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;displayNetworkInfo&lt;/span&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;Here&#39;s the final state of the &lt;a href=&quot;https://glitch.com/~adaptive-serving-netinfo&quot; rel=&quot;noopener&quot;&gt;app on Glitch&lt;/a&gt;.&lt;/p&gt;
&lt;img alt=&quot;Matrix-like video background with &amp;#x27;NETWORK INFORMATION 4g&amp;#x27; text overlay&quot; decoding=&quot;async&quot; height=&quot;447&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/0cTTeeAMgl5lD1PKRjy2.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/0cTTeeAMgl5lD1PKRjy2.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/0cTTeeAMgl5lD1PKRjy2.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/0cTTeeAMgl5lD1PKRjy2.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/0cTTeeAMgl5lD1PKRjy2.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/0cTTeeAMgl5lD1PKRjy2.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/0cTTeeAMgl5lD1PKRjy2.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/0cTTeeAMgl5lD1PKRjy2.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/0cTTeeAMgl5lD1PKRjy2.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/0cTTeeAMgl5lD1PKRjy2.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/0cTTeeAMgl5lD1PKRjy2.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/0cTTeeAMgl5lD1PKRjy2.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/0cTTeeAMgl5lD1PKRjy2.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/0cTTeeAMgl5lD1PKRjy2.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/0cTTeeAMgl5lD1PKRjy2.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/0cTTeeAMgl5lD1PKRjy2.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/0cTTeeAMgl5lD1PKRjy2.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/0cTTeeAMgl5lD1PKRjy2.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;To test it again:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;To preview the site, press &lt;strong&gt;View App&lt;/strong&gt;. Then press
&lt;strong&gt;Fullscreen&lt;/strong&gt;
&lt;img src=&quot;https://web.dev/images/glitch/fullscreen.svg&quot; alt=&quot;fullscreen&quot; style=&quot;padding: 4px 8px; opacity: .5; border: 1px solid #c3c3c3; border-radius: 5px; margin-top: 0;&quot; /&gt;.&lt;/li&gt;
&lt;li&gt;Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Network&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Throttling&lt;/strong&gt; drop-down, which is set to &lt;strong&gt;No throttling&lt;/strong&gt; by default. Select &lt;strong&gt;Fast 3G&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Reload the app.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The app will update the network information to &lt;strong&gt;3g&lt;/strong&gt;:&lt;/p&gt;
&lt;img alt=&quot;Matrix-like video background with &amp;#x27;NETWORK INFORMATION 3g&amp;#x27; text overlay&quot; decoding=&quot;async&quot; height=&quot;447&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/WRp4ceBiuDQZoWmuaZx4.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/WRp4ceBiuDQZoWmuaZx4.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/WRp4ceBiuDQZoWmuaZx4.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/WRp4ceBiuDQZoWmuaZx4.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/WRp4ceBiuDQZoWmuaZx4.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/WRp4ceBiuDQZoWmuaZx4.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/WRp4ceBiuDQZoWmuaZx4.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/WRp4ceBiuDQZoWmuaZx4.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/WRp4ceBiuDQZoWmuaZx4.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/WRp4ceBiuDQZoWmuaZx4.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/WRp4ceBiuDQZoWmuaZx4.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/WRp4ceBiuDQZoWmuaZx4.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/WRp4ceBiuDQZoWmuaZx4.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/WRp4ceBiuDQZoWmuaZx4.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/WRp4ceBiuDQZoWmuaZx4.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/WRp4ceBiuDQZoWmuaZx4.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/WRp4ceBiuDQZoWmuaZx4.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/WRp4ceBiuDQZoWmuaZx4.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
</content>
    <author>
      <name>Milica Mihajlija</name>
    </author>
  </entry>
  
  <entry>
    <title>Cumulative Layout Shift (CLS)</title>
    <link href="https://web.dev/cls/"/>
    <updated>2019-06-11T00:00:00Z</updated>
    <id>https://web.dev/cls/</id>
    <content type="html" mode="escaped">&lt;div class=&quot;wdi-browser-compat&quot;&gt;
  &lt;span class=&quot;wdi-browser-compat__label&quot;&gt;Browser support&lt;/span&gt;
  &lt;ul class=&quot;wdi-browser-compat__items&quot;&gt;
    &lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;chrome&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Chrome 77, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      77
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox, Not supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Edge 79, Supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
79
&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Safari, Not supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;p&gt;&lt;/p&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/LayoutShift#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&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; Cumulative Layout Shift (CLS) is a stable Core Web Vital metric. It is an important, user-centric metric for measuring &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/#types-of-metrics&quot;&gt;visual stability&lt;/a&gt; because it helps quantify how often users experience unexpected layout shifts—a low CLS helps ensure that the page is &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/#questions&quot;&gt;delightful&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Have you ever been reading an article online when something suddenly changes on
the page? Without warning, the text moves, and you&#39;ve lost your place. Or even
worse: you&#39;re about to tap a link or a button, but in the instant before your
finger lands—BOOM—the link moves, and you end up clicking something
else!&lt;/p&gt;
&lt;p&gt;Most of the time these kinds of experiences are just annoying, but in some
cases, they can cause real damage.&lt;/p&gt;
&lt;figure&gt;
  &lt;video autoplay=&quot;&quot; controls=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; poster=&quot;https://storage.googleapis.com/web-dev-assets/layout-instability-api/layout-instability-poster.png&quot; width=&quot;658&quot; height=&quot;510&quot;&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/layout-instability-api/layout-instability2.webm&quot; type=&quot;video/webm; codecs=vp8&quot; /&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/layout-instability-api/layout-instability2.mp4&quot; type=&quot;video/mp4; codecs=h264&quot; /&gt;
  &lt;/video&gt;
  &lt;figcaption&gt;
    A screencast illustrating how layout instability can negatively affect
    users.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Unexpected movement of page content usually happens because resources are loaded
asynchronously or DOM elements get dynamically added to the page above existing
content. The culprit might be an image or video with unknown dimensions, a font
that renders larger or smaller than its fallback, or a third-party ad or widget
that dynamically resizes itself.&lt;/p&gt;
&lt;p&gt;What makes this issue even more problematic is that how a site functions in
development is often quite different from how users experience it. Personalized
or third-party content often doesn&#39;t behave the same in development as it does
in production, test images are often already in the developer&#39;s browser cache,
and API calls that run locally are often so fast that the delay isn&#39;t
noticeable.&lt;/p&gt;
&lt;p&gt;The Cumulative Layout Shift (CLS) metric helps you address this problem by
measuring how often it&#39;s occurring for real users.&lt;/p&gt;
&lt;h2 id=&quot;what-is-cls&quot;&gt;What is CLS? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cls/#what-is-cls&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;CLS is a measure of the largest burst of &lt;em&gt;layout shift scores&lt;/em&gt; for every
&lt;a href=&quot;https://web.dev/cls/#expected-vs-unexpected-layout-shifts&quot;&gt;unexpected&lt;/a&gt; layout shift that
occurs during the entire lifespan of a page.&lt;/p&gt;
&lt;p&gt;A &lt;em&gt;layout shift&lt;/em&gt; occurs any time a visible element changes its position from one
rendered frame to the next. (See below for details on how individual &lt;a href=&quot;https://web.dev/cls/#layout-shift-score&quot;&gt;layout
shift scores&lt;/a&gt; are calculated.)&lt;/p&gt;
&lt;p&gt;A burst of layout shifts, known as a &lt;a href=&quot;https://web.dev/evolving-cls/#why-a-session-window&quot;&gt;&lt;em&gt;session
window&lt;/em&gt;&lt;/a&gt;, is when one or more individual
layout shifts occur in rapid succession with less than 1-second in between each
shift and a maximum of 5 seconds for the total window duration.&lt;/p&gt;
&lt;p&gt;The largest burst is the session window with the maximum cumulative score of all
layout shifts within that window.&lt;/p&gt;
&lt;figure&gt;
  &lt;video controls=&quot;&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; width=&quot;658&quot; height=&quot;452&quot;&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/better-layout-shift-metric/session-window.webm&quot; type=&quot;video/webm&quot; /&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/better-layout-shift-metric/session-window.mp4&quot; type=&quot;video/mp4&quot; /&gt;
  &lt;/video&gt;
  &lt;figcaption&gt;
    Example of session windows. Blue bars represent the scores of each individual layout shift.
  &lt;/figcaption&gt;
&lt;/figure&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; Previously CLS measured the sum total of &lt;em&gt;all individual layout shift scores&lt;/em&gt; that occurred during the entire lifespan of the page. To see which tools still provide the ability to benchmark against the original implementation, check out &lt;a href=&quot;https://web.dev/cls-web-tooling&quot;&gt;Evolving Cumulative Layout Shift in web tooling&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;what-is-a-good-cls-score&quot;&gt;What is a good CLS score? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cls/#what-is-a-good-cls-score&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To provide a good user experience, sites should strive to have a CLS score of
&lt;strong&gt;0.1&lt;/strong&gt; or less. To ensure you&#39;re hitting this target for most of your users, a
good threshold to measure is the &lt;strong&gt;75th percentile&lt;/strong&gt; of page loads, segmented
across mobile and desktop devices.&lt;/p&gt;
&lt;figure&gt;
  &lt;picture&gt;
    &lt;source srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9mWVASbWDLzdBUpVcjE1.svg&quot; media=&quot;(min-width: 640px)&quot; width=&quot;800&quot; height=&quot;200&quot; /&gt;
    &lt;img alt=&quot;Good CLS values are 0.1 or less, poor values are greater than 0.25, and anything in between needs improvement&quot; decoding=&quot;async&quot; height=&quot;480&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uqclEgIlTHhwIgNTXN3Y.svg&quot; width=&quot;640&quot; /&gt;
  &lt;/picture&gt;
&lt;/figure&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; To learn more about the research and methodology behind this recommendation, see: &lt;a href=&quot;https://web.dev/defining-core-web-vitals-thresholds/&quot;&gt;Defining the Core Web Vitals metrics thresholds&lt;/a&gt; &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;layout-shifts-in-detail&quot;&gt;Layout shifts in detail &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cls/#layout-shifts-in-detail&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Layout shifts are defined by the &lt;a href=&quot;https://github.com/WICG/layout-instability&quot; rel=&quot;noopener&quot;&gt;Layout Instability
API&lt;/a&gt;, which reports &lt;code&gt;layout-shift&lt;/code&gt;
entries any time an element that is visible within the viewport changes its
start position (for example, its top and left position in the default &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/writing-mode&quot; rel=&quot;noopener&quot;&gt;writing
mode&lt;/a&gt;) between
two frames. Such elements are considered &lt;em&gt;unstable elements&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Note that layout shifts only occur when existing elements change their start
position. If a new element is added to the DOM or an existing element changes
size, it doesn&#39;t count as a layout shift—as long as the change doesn&#39;t
cause other visible elements to change their start position.&lt;/p&gt;
&lt;h3 id=&quot;layout-shift-score&quot;&gt;Layout shift score &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cls/#layout-shift-score&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To calculate the &lt;em&gt;layout shift score&lt;/em&gt;, the browser looks at the viewport size
and the movement of &lt;em&gt;unstable elements&lt;/em&gt; in the viewport between two rendered
frames. The layout shift score is a product of two measures of that movement:
the &lt;em&gt;impact fraction&lt;/em&gt; and the &lt;em&gt;distance fraction&lt;/em&gt; (both defined below).&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;layout shift score = impact fraction * distance fraction&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;impact-fraction&quot;&gt;Impact fraction &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cls/#impact-fraction&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/WICG/layout-instability#Impact-Fraction&quot; rel=&quot;noopener&quot;&gt;impact
fraction&lt;/a&gt; measures
how &lt;em&gt;unstable elements&lt;/em&gt; impact the viewport area between two frames.&lt;/p&gt;
&lt;p&gt;The union of the visible areas of all &lt;em&gt;unstable elements&lt;/em&gt; for the previous frame
&lt;em&gt;and&lt;/em&gt; the current frame—as a fraction of the total area of the
viewport—is the &lt;em&gt;impact fraction&lt;/em&gt; for the current frame.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/BbpE9rFQbF8aU6iXN1U6.png?auto=format&quot;&gt;&lt;img alt=&quot;Impact fraction example with one _unstable element_&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/BbpE9rFQbF8aU6iXN1U6.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/BbpE9rFQbF8aU6iXN1U6.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/BbpE9rFQbF8aU6iXN1U6.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/BbpE9rFQbF8aU6iXN1U6.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/BbpE9rFQbF8aU6iXN1U6.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/BbpE9rFQbF8aU6iXN1U6.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/BbpE9rFQbF8aU6iXN1U6.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/BbpE9rFQbF8aU6iXN1U6.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/BbpE9rFQbF8aU6iXN1U6.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/BbpE9rFQbF8aU6iXN1U6.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/BbpE9rFQbF8aU6iXN1U6.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/BbpE9rFQbF8aU6iXN1U6.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/BbpE9rFQbF8aU6iXN1U6.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/BbpE9rFQbF8aU6iXN1U6.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/BbpE9rFQbF8aU6iXN1U6.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/BbpE9rFQbF8aU6iXN1U6.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/BbpE9rFQbF8aU6iXN1U6.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/BbpE9rFQbF8aU6iXN1U6.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the image above there&#39;s an element that takes up half of the viewport in one
frame. Then, in the next frame, the element shifts down by 25% of the viewport
height. The red, dotted rectangle indicates the union of the element&#39;s visible
area in both frames, which, in this case, is 75% of the total viewport, so its
&lt;em&gt;impact fraction&lt;/em&gt; is &lt;code&gt;0.75&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;distance-fraction&quot;&gt;Distance fraction &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cls/#distance-fraction&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The other part of the layout shift score equation measures the distance that
unstable elements have moved, relative to the viewport. The &lt;em&gt;distance fraction&lt;/em&gt;
is the greatest distance any &lt;em&gt;unstable element&lt;/em&gt; has moved in the frame (either
horizontally or vertically) divided by the viewport&#39;s largest dimension (width
or height, whichever is greater).&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ASnfpVs2n9winu6mmzdk.png?auto=format&quot;&gt;&lt;img alt=&quot;Distance fraction example with one _unstable element_&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ASnfpVs2n9winu6mmzdk.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ASnfpVs2n9winu6mmzdk.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ASnfpVs2n9winu6mmzdk.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ASnfpVs2n9winu6mmzdk.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ASnfpVs2n9winu6mmzdk.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ASnfpVs2n9winu6mmzdk.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ASnfpVs2n9winu6mmzdk.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ASnfpVs2n9winu6mmzdk.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ASnfpVs2n9winu6mmzdk.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ASnfpVs2n9winu6mmzdk.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ASnfpVs2n9winu6mmzdk.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ASnfpVs2n9winu6mmzdk.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ASnfpVs2n9winu6mmzdk.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ASnfpVs2n9winu6mmzdk.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ASnfpVs2n9winu6mmzdk.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ASnfpVs2n9winu6mmzdk.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ASnfpVs2n9winu6mmzdk.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ASnfpVs2n9winu6mmzdk.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the example above, the largest viewport dimension is the height, and the
unstable element has moved by 25% of the viewport height, which makes the
&lt;em&gt;distance fraction&lt;/em&gt; 0.25.&lt;/p&gt;
&lt;p&gt;So, in this example the &lt;em&gt;impact fraction&lt;/em&gt; is &lt;code&gt;0.75&lt;/code&gt; and the &lt;em&gt;distance fraction&lt;/em&gt;
is &lt;code&gt;0.25&lt;/code&gt;, so the &lt;em&gt;layout shift score&lt;/em&gt; is &lt;code&gt;0.75 * 0.25 = 0.1875&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; Initially, the layout shift score was calculated based only on &lt;em&gt;impact fraction&lt;/em&gt;. The &lt;em&gt;distance fraction&lt;/em&gt; was introduced to avoid overly penalizing cases where large elements shift by a small amount. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The next example illustrates how adding content to an existing element affects
the layout shift score:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xhN81DazXCs8ZawoCj0T.png?auto=format&quot;&gt;&lt;img alt=&quot;Layout shift example with multiple stable and _unstable elements_&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xhN81DazXCs8ZawoCj0T.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xhN81DazXCs8ZawoCj0T.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xhN81DazXCs8ZawoCj0T.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xhN81DazXCs8ZawoCj0T.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xhN81DazXCs8ZawoCj0T.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xhN81DazXCs8ZawoCj0T.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xhN81DazXCs8ZawoCj0T.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xhN81DazXCs8ZawoCj0T.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xhN81DazXCs8ZawoCj0T.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xhN81DazXCs8ZawoCj0T.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xhN81DazXCs8ZawoCj0T.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xhN81DazXCs8ZawoCj0T.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xhN81DazXCs8ZawoCj0T.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xhN81DazXCs8ZawoCj0T.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xhN81DazXCs8ZawoCj0T.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xhN81DazXCs8ZawoCj0T.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xhN81DazXCs8ZawoCj0T.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xhN81DazXCs8ZawoCj0T.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The &amp;quot;Click Me!&amp;quot; button is appended to the bottom of the gray box with black
text, which pushes the green box with white text down (and partially out of the
viewport).&lt;/p&gt;
&lt;p&gt;In this example, the gray box changes size, but its start position does not
change so it&#39;s not an &lt;em&gt;unstable element&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The &amp;quot;Click Me!&amp;quot; button was not previously in the DOM, so its start position
doesn&#39;t change either.&lt;/p&gt;
&lt;p&gt;The start position of the green box, however, does change, but since it&#39;s been
moved partially out of the viewport, the invisible area is not considered when
calculating the &lt;em&gt;impact fraction&lt;/em&gt;. The union of the visible areas for the green
box in both frames (illustrated by the red, dotted rectangle) is the same as the
area of the green box in the first frame—50% of the viewport. The &lt;em&gt;impact
fraction&lt;/em&gt; is &lt;code&gt;0.5&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;distance fraction&lt;/em&gt; is illustrated with the purple arrow. The green box has
moved down by about 14% of the viewport so the &lt;em&gt;distance fraction&lt;/em&gt; is &lt;code&gt;0.14&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The layout shift score is &lt;code&gt;0.5 x 0.14 = 0.07&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This last example illustrates multiple &lt;em&gt;unstable elements&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/J8AWG72qYlmbAHxjxuLg.png?auto=format&quot;&gt;&lt;img alt=&quot;Layout shift example with stable and _unstable elements_ and viewport clipping&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/J8AWG72qYlmbAHxjxuLg.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/J8AWG72qYlmbAHxjxuLg.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/J8AWG72qYlmbAHxjxuLg.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/J8AWG72qYlmbAHxjxuLg.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/J8AWG72qYlmbAHxjxuLg.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/J8AWG72qYlmbAHxjxuLg.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/J8AWG72qYlmbAHxjxuLg.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/J8AWG72qYlmbAHxjxuLg.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/J8AWG72qYlmbAHxjxuLg.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/J8AWG72qYlmbAHxjxuLg.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/J8AWG72qYlmbAHxjxuLg.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/J8AWG72qYlmbAHxjxuLg.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/J8AWG72qYlmbAHxjxuLg.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/J8AWG72qYlmbAHxjxuLg.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/J8AWG72qYlmbAHxjxuLg.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/J8AWG72qYlmbAHxjxuLg.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/J8AWG72qYlmbAHxjxuLg.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/J8AWG72qYlmbAHxjxuLg.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the first frame above there are four results of an API request for animals,
sorted in alphabetical order. In the second frame, more results are added to the
sorted list.&lt;/p&gt;
&lt;p&gt;The first item in the list (&amp;quot;Cat&amp;quot;) does not change its start position between
frames, so it&#39;s stable. Similarly, the new items added to the list were not
previously in the DOM, so their start positions don&#39;t change either. But the
items labelled &amp;quot;Dog&amp;quot;, &amp;quot;Horse&amp;quot;, and &amp;quot;Zebra&amp;quot; all shift their start positions,
making them &lt;em&gt;unstable elements&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Again, the red, dotted rectangles represent the union of these three &lt;em&gt;unstable
elements&lt;/em&gt;&#39; before and after areas, which in this case is around 60% of the
viewport&#39;s area (&lt;em&gt;impact fraction&lt;/em&gt; of &lt;code&gt;0.60&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The arrows represent the distances that &lt;em&gt;unstable elements&lt;/em&gt; have moved from
their starting positions. The &amp;quot;Zebra&amp;quot; element, represented by the blue arrow,
has moved the most, by about 30% of the viewport height. That makes the
&lt;em&gt;distance fraction&lt;/em&gt; in this example &lt;code&gt;0.3&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The layout shift score is &lt;code&gt;0.60 x 0.3 = 0.18&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;expected-vs-unexpected-layout-shifts&quot;&gt;Expected vs. unexpected layout shifts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cls/#expected-vs-unexpected-layout-shifts&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Not all layout shifts are bad. In fact, many dynamic web applications frequently
change the start position of elements on the page.&lt;/p&gt;
&lt;h4 id=&quot;user-initiated-layout-shifts&quot;&gt;User-initiated layout shifts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cls/#user-initiated-layout-shifts&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A layout shift is only bad if the user isn&#39;t expecting it. On the other hand,
layout shifts that occur in response to user interactions (clicking a link,
pressing a button, typing in a search box and similar) are generally fine, as
long as the shift occurs close enough to the interaction that the relationship
is clear to the user.&lt;/p&gt;
&lt;p&gt;For example, if a user interaction triggers a network request that may take a
while to complete, it&#39;s best to create some space right away and show a loading
indicator to avoid an unpleasant layout shift when the request completes. If the
user doesn&#39;t realize something is loading, or doesn&#39;t have a sense of when the
resource will be ready, they may try to click something else while
waiting—something that could move out from under them.&lt;/p&gt;
&lt;p&gt;Layout shifts that occur within 500 milliseconds of user input will have the
&lt;a href=&quot;https://wicg.github.io/layout-instability/#dom-layoutshift-hadrecentinput&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;hadRecentInput&lt;/code&gt;&lt;/a&gt;
flag set, so they can be excluded from calculations.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; The &lt;code&gt;hadRecentInput&lt;/code&gt; flag will only be true for discrete input events like tap, click, or keypress. Continuous interactions such as scrolls, drags, or pinch and zoom gestures are not considered &amp;quot;recent input&amp;quot;. See the &lt;a href=&quot;https://github.com/WICG/layout-instability#recent-input-exclusion&quot;&gt;Layout Instability Spec&lt;/a&gt; for more details. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;animations-and-transitions&quot;&gt;Animations and transitions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cls/#animations-and-transitions&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Animations and transitions, when done well, are a great way to update content on
the page without surprising the user. Content that shifts abruptly and
unexpectedly on the page almost always creates a bad user experience. But
content that moves gradually and naturally from one position to the next can
often help the user better understand what&#39;s going on, and guide them between
state changes.&lt;/p&gt;
&lt;p&gt;Be sure to respect &lt;a href=&quot;https://web.dev/prefers-reduced-motion/&quot;&gt;&lt;code&gt;prefers-reduced-motion&lt;/code&gt;&lt;/a&gt; browser settings, as some site visitors
can experience ill effects or attention issues from animation.&lt;/p&gt;
&lt;p&gt;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 allows you to animate elements without triggering layout shifts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Instead of changing the &lt;code&gt;height&lt;/code&gt; and &lt;code&gt;width&lt;/code&gt; properties, use &lt;code&gt;transform: scale()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;To move elements around, avoid changing the &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;right&lt;/code&gt;, &lt;code&gt;bottom&lt;/code&gt;, or
&lt;code&gt;left&lt;/code&gt; properties and use &lt;code&gt;transform: translate()&lt;/code&gt; instead.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-to-measure-cls&quot;&gt;How to measure CLS &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cls/#how-to-measure-cls&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;CLS can be measured &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/#in-the-lab&quot;&gt;in the lab&lt;/a&gt;
or &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/#in-the-field&quot;&gt;in the field&lt;/a&gt;, and it&#39;s
available in the following tools:&lt;/p&gt;
&lt;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; Lab tools typically load pages in a synthetic environment and are thus only able to measure layout shifts that occur during page load. As a result, CLS values reported by lab tools for a given page may be less than what real users experience in the field. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;field-tools&quot;&gt;Field tools &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cls/#field-tools&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/crux/&quot; rel=&quot;noopener&quot;&gt;Chrome User Experience
Report&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pagespeed.web.dev/&quot; rel=&quot;noopener&quot;&gt;PageSpeed Insights&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://support.google.com/webmasters/answer/9205520&quot; rel=&quot;noopener&quot;&gt;Search Console (Core Web Vitals
report)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/GoogleChrome/web-vitals&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;web-vitals&lt;/code&gt; JavaScript library&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;lab-tools&quot;&gt;Lab tools &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cls/#lab-tools&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/devtools/&quot; rel=&quot;noopener&quot;&gt;Chrome DevTools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pagespeed.web.dev/&quot; rel=&quot;noopener&quot;&gt;PageSpeed Insights&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webpagetest.org/&quot; rel=&quot;noopener&quot;&gt;WebPageTest&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;measure-layout-shifts-in-javascript&quot;&gt;Measure layout shifts in JavaScript &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cls/#measure-layout-shifts-in-javascript&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To measure layout shifts in JavaScript, you use the &lt;a href=&quot;https://github.com/WICG/layout-instability&quot; rel=&quot;noopener&quot;&gt;Layout Instability API&lt;/a&gt;.&lt;/p&gt;
&lt;p&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; to log &lt;code&gt;layout-shift&lt;/code&gt; entries to the console:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PerformanceObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entryList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; entry &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; entryList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEntries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Layout shift:&#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;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;layout-shift&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;buffered&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;measure-cls-in-javascript&quot;&gt;Measure CLS in JavaScript &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cls/#measure-cls-in-javascript&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To measure CLS in JavaScript, you need to group these unexpected &lt;code&gt;layout-shift&lt;/code&gt; entries into sessions, and calculate the maximum session value. You can refer to the &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals/blob/main/src/onCLS.ts&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;web vitals&lt;/code&gt; JavaScript library source code&lt;/a&gt; which contains a reference implementation on how CLS is calculated.&lt;/p&gt;
&lt;p&gt;In most cases, the current CLS value at the time the page is being unloaded is the final CLS value for that page, but there are a few important exceptions as noted in the next section. The &lt;code&gt;web vitals&lt;/code&gt; JavaScript library accounts for these as much as possible, within the limitations of the Web APIs.&lt;/p&gt;
&lt;h4 id=&quot;differences-between-the-metric-and-the-api&quot;&gt;Differences between the metric and the API &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cls/#differences-between-the-metric-and-the-api&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;If a page is loaded in the background, or if it&#39;s backgrounded prior to the
browser painting any content, then it should not report any CLS value.&lt;/li&gt;
&lt;li&gt;If a page is restored from the &lt;a href=&quot;https://web.dev/bfcache/#impact-on-core-web-vitals&quot;&gt;back/forward
cache&lt;/a&gt;, its CLS value should be reset to
zero since users experience this as a distinct page visit.&lt;/li&gt;
&lt;li&gt;The API does not report &lt;code&gt;layout-shift&lt;/code&gt; entries for shifts that occur within
iframes but the metric does as they are part of the user experience of the
page. This can
&lt;a href=&quot;https://web.dev/crux-and-rum-differences/#iframes&quot;&gt;show as a difference between CrUX and RUM&lt;/a&gt;.
To properly measure CLS you should consider them. Sub-frames can
use the API to report their &lt;code&gt;layout-shift&lt;/code&gt; entries to the parent frame for
&lt;a href=&quot;https://github.com/WICG/layout-instability#cumulative-scores&quot; rel=&quot;noopener&quot;&gt;aggregation&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition to these exceptions, CLS has some added complexity due to the fact
that it measures the entire lifespan of a page:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Users might keep a tab open for a &lt;em&gt;very&lt;/em&gt; long time—days, weeks, months.
In fact, a user might never close a tab.&lt;/li&gt;
&lt;li&gt;On mobile operating systems, browsers typically do not run page unload
callbacks for background tabs, making it difficult to report the &amp;quot;final&amp;quot;
value.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To handle such cases, CLS should be reported any time a page is
background—in addition to any time it&#39;s unloaded (the &lt;a href=&quot;https://developer.chrome.com/blog/page-lifecycle-api/#event-visibilitychange&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;visibilitychange&lt;/code&gt;
event&lt;/a&gt;
covers both of these scenarios). And analytics systems receiving this data will
then need to calculate the final CLS value on the backend.&lt;/p&gt;
&lt;p&gt;Rather than memorizing and grappling with all of these cases yourself, developers can use the
&lt;a href=&quot;https://github.com/GoogleChrome/web-vitals&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;web-vitals&lt;/code&gt; JavaScript library&lt;/a&gt; to
measure CLS, which accounts for everything mentioned above:&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;onCLS&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 CLS in all situations&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// where it needs to be reported.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;onCLS&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;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; In some cases (such as cross-origin iframes) it&#39;s not possible to measure CLS in JavaScript. See the &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals#limitations&quot;&gt;limitations&lt;/a&gt; section of the &lt;code&gt;web-vitals&lt;/code&gt; library for details. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;how-to-improve-cls&quot;&gt;How to improve CLS &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cls/#how-to-improve-cls&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A full guide on &lt;a href=&quot;https://web.dev/optimize-cls/&quot;&gt;optimizing CLS&lt;/a&gt; is available to guide you through the process of identifying layout shifts in the field and using lab data to drill down and optimize them.&lt;/p&gt;
&lt;h2 id=&quot;additional-resources&quot;&gt;Additional resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cls/#additional-resources&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Google Publisher Tag&#39;s guidance on
&lt;a href=&quot;https://developers.google.com/doubleclick-gpt/guides/minimize-layout-shift&quot; rel=&quot;noopener&quot;&gt;minimizing layout shift&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/zIJuY-JCjqw&quot; rel=&quot;noopener&quot;&gt;Understanding Cumulative Layout Shift&lt;/a&gt; by
&lt;a href=&quot;https://anniesullie.com/&quot; rel=&quot;noopener&quot;&gt;Annie Sullivan&lt;/a&gt; and &lt;a href=&quot;https://kobes.ca/&quot; rel=&quot;noopener&quot;&gt;Steve
Kobes&lt;/a&gt; at &lt;a href=&quot;https://perfmattersconf.com/&quot; rel=&quot;noopener&quot;&gt;#PerfMatters&lt;/a&gt;
(2020)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;changelog&quot;&gt;CHANGELOG &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cls/#changelog&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Occasionally, bugs are discovered in the APIs used to measure metrics, and sometimes in the definitions of the metrics themselves. As a result, changes must sometimes be made, and these changes can show up as improvements or regressions in your internal reports and dashboards.&lt;/p&gt;
&lt;p&gt;To help you manage this, all changes to either the implementation or definition of these metrics will be surfaced in this &lt;a href=&quot;http://bit.ly/chrome-speed-metrics-changelog&quot; rel=&quot;noopener&quot;&gt;CHANGELOG&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you have feedback for these metrics, you can provide it in the &lt;a href=&quot;https://groups.google.com/g/web-vitals-feedback&quot; rel=&quot;noopener&quot;&gt;web-vitals-feedback Google group&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Philip Walton</name>
    </author><author>
      <name>Milica Mihajlija</name>
    </author>
  </entry>
  
  <entry>
    <title>Extract and inline critical CSS with Critical</title>
    <link href="https://web.dev/codelab-extract-and-inline-critical-css/"/>
    <updated>2019-05-29T00:00:00Z</updated>
    <id>https://web.dev/codelab-extract-and-inline-critical-css/</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; This codelab uses Chrome DevTools. &lt;a href=&quot;https://www.google.com/chrome&quot;&gt;Download Chrome&lt;/a&gt; if you don&#39;t already have it. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Whether you use a UI library or handcraft your styles, shipping a significant amount of CSS delays rendering because the browser must download and parse CSS files before it can show the page.&lt;/p&gt;
&lt;p&gt;This responsive ice cream gallery is built with &lt;a href=&quot;https://getbootstrap.com/&quot; rel=&quot;noopener&quot;&gt;Bootstrap&lt;/a&gt;. UI libraries like Bootstrap speed up development, but that often comes at the expense of bloated and unnecessary CSS, which can slow down your load times. Bootstrap 4 is 187 KB, while &lt;a href=&quot;https://semantic-ui.com/&quot; rel=&quot;noopener&quot;&gt;Semantic UI&lt;/a&gt;, another UI library, is a whopping 730 KB uncompressed. Even when minified and gzipped, Bootstrap still weighs around 20 KB, well over the &lt;a href=&quot;https://web.dev/extract-critical-css/#14KB&quot;&gt;14 KB threshold&lt;/a&gt; for the first roundtrip.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/addyosmani/critical&quot; rel=&quot;noopener&quot;&gt;Critical&lt;/a&gt; is a tool that extracts, minifies and inlines &lt;a href=&quot;https://web.dev/extract-critical-css&quot;&gt;above-the-fold&lt;/a&gt; CSS. This allows above-the-fold content to be rendered as soon as possible, even if CSS for other parts of the page has not yet loaded. In this codelab, you&#39;ll learn how to use Critical&#39;s npm module.&lt;/p&gt;
&lt;h2 id=&quot;measure&quot;&gt;Measure &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-extract-and-inline-critical-css/#measure&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;To preview the site, press &lt;strong&gt;View App&lt;/strong&gt;. Then press
&lt;strong&gt;Fullscreen&lt;/strong&gt;
&lt;img src=&quot;https://web.dev/images/glitch/fullscreen.svg&quot; alt=&quot;fullscreen&quot; style=&quot;padding: 4px 8px; opacity: .5; border: 1px solid #c3c3c3; border-radius: 5px; margin-top: 0;&quot; /&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To run a Lighthouse audit on this site:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Lighthouse&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Mobile&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;Performance&lt;/strong&gt; checkbox.&lt;/li&gt;
&lt;li&gt;Clear the rest of the checkboxes in the Audits section.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Simulated Fast 3G, 4x CPU Slowdown&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;Clear Storage&lt;/strong&gt; checkbox. With this option selected, Lighthouse will not load resources from the cache, which simulates how first-time visitors would experience the page.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Run Audits&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;img alt=&quot;Audits panel of Chrome DevTools, powered by Lighthouse&quot; decoding=&quot;async&quot; height=&quot;1068&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/bk0impRv9Mo0yDpbuIQi.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/bk0impRv9Mo0yDpbuIQi.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/bk0impRv9Mo0yDpbuIQi.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/bk0impRv9Mo0yDpbuIQi.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/bk0impRv9Mo0yDpbuIQi.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/bk0impRv9Mo0yDpbuIQi.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/bk0impRv9Mo0yDpbuIQi.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/bk0impRv9Mo0yDpbuIQi.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/bk0impRv9Mo0yDpbuIQi.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/bk0impRv9Mo0yDpbuIQi.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/bk0impRv9Mo0yDpbuIQi.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/bk0impRv9Mo0yDpbuIQi.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/bk0impRv9Mo0yDpbuIQi.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/bk0impRv9Mo0yDpbuIQi.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/bk0impRv9Mo0yDpbuIQi.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/bk0impRv9Mo0yDpbuIQi.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/bk0impRv9Mo0yDpbuIQi.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/bk0impRv9Mo0yDpbuIQi.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;When you run an audit on your machine, the exact results may vary, but in the filmstrip view, you&#39;ll notice the app has a blank screen for quite a while before finally rendering the content. This is why &lt;a href=&quot;https://web.dev/fcp/&quot;&gt;First Contentful Paint (FCP)&lt;/a&gt; is high and why overall performance score is not great.&lt;/p&gt;
&lt;img alt=&quot;Lighthouse audit showing performance score of 84, FCP 3 seconds and a filmstrip view of loading the app&quot; decoding=&quot;async&quot; height=&quot;356&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 710px) 710px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/otfxnEjhjP9cNpSF8W4o.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/otfxnEjhjP9cNpSF8W4o.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/otfxnEjhjP9cNpSF8W4o.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/otfxnEjhjP9cNpSF8W4o.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/otfxnEjhjP9cNpSF8W4o.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/otfxnEjhjP9cNpSF8W4o.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/otfxnEjhjP9cNpSF8W4o.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/otfxnEjhjP9cNpSF8W4o.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/otfxnEjhjP9cNpSF8W4o.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/otfxnEjhjP9cNpSF8W4o.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/otfxnEjhjP9cNpSF8W4o.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/otfxnEjhjP9cNpSF8W4o.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/otfxnEjhjP9cNpSF8W4o.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/otfxnEjhjP9cNpSF8W4o.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/otfxnEjhjP9cNpSF8W4o.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/otfxnEjhjP9cNpSF8W4o.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/otfxnEjhjP9cNpSF8W4o.png?auto=format&amp;w=1420 1420w&quot; width=&quot;710&quot; /&gt;
&lt;p&gt;Lighthouse is here to help you fix performance issues, so look for solutions in the &lt;strong&gt;Opportunities&lt;/strong&gt; section. &lt;strong&gt;Eliminate render-blocking resources&lt;/strong&gt; is listed as an opportunity and that&#39;s where Critical shines!&lt;/p&gt;
&lt;img alt=&quot;Lighthouse audit &amp;#x27;Opportunities&amp;#x27; section listing &amp;#x27;Eliminate render-blocking resources&amp;#x27;&quot; decoding=&quot;async&quot; height=&quot;449&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 743px) 743px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/L0i6jb8G1eT0vr2V45vH.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/L0i6jb8G1eT0vr2V45vH.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/L0i6jb8G1eT0vr2V45vH.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/L0i6jb8G1eT0vr2V45vH.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/L0i6jb8G1eT0vr2V45vH.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/L0i6jb8G1eT0vr2V45vH.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/L0i6jb8G1eT0vr2V45vH.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/L0i6jb8G1eT0vr2V45vH.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/L0i6jb8G1eT0vr2V45vH.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/L0i6jb8G1eT0vr2V45vH.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/L0i6jb8G1eT0vr2V45vH.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/L0i6jb8G1eT0vr2V45vH.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/L0i6jb8G1eT0vr2V45vH.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/L0i6jb8G1eT0vr2V45vH.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/L0i6jb8G1eT0vr2V45vH.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/L0i6jb8G1eT0vr2V45vH.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/L0i6jb8G1eT0vr2V45vH.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/L0i6jb8G1eT0vr2V45vH.png?auto=format&amp;w=1486 1486w&quot; width=&quot;743&quot; /&gt;
&lt;h2 id=&quot;optimize&quot;&gt;Optimize &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-extract-and-inline-critical-css/#optimize&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Remix to Edit&lt;/strong&gt; to make the project editable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To speed things up, Critical is already installed and an empty configuration file is included in the codelab.&lt;/p&gt;
&lt;p&gt;In the configuration file &lt;code&gt;critical.js&lt;/code&gt;, add a reference to Critical and then invoke the &lt;code&gt;critical.generate()&lt;/code&gt; function. This function accepts an object containing the configuration.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; critical &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;critical&#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;critical&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generate&lt;/span&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;// configuration&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 parameter&quot;&gt;err&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; output&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;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;error&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;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;output&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Generated critical CSS&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Error handling isn&#39;t mandatory, but it&#39;s an easy way to gauge the operation success in the console.&lt;/p&gt;
&lt;h3 id=&quot;configure-critical&quot;&gt;Configure Critical &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-extract-and-inline-critical-css/#configure-critical&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The table below contains some useful Critical options. You can check out all of the &lt;a href=&quot;https://github.com/addyosmani/critical#usage&quot; rel=&quot;noopener&quot;&gt;available options on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;table&gt;
    &lt;th&gt;Option&lt;/th&gt;
    &lt;th&gt;Type&lt;/th&gt;
    &lt;th&gt;Explanation&lt;/th&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;code&gt;base&lt;/code&gt;&lt;/td&gt;
    &lt;td&gt;string&lt;/td&gt;
    &lt;td&gt;The base directory for your files.&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;code&gt;src&lt;/code&gt;&lt;/td&gt;
    &lt;td&gt;string&lt;/td&gt;
    &lt;td&gt;HTML source file.&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;code&gt;dest&lt;/code&gt;&lt;/td&gt;
    &lt;td&gt;string&lt;/td&gt;
    &lt;td&gt;The target for the output file. If CSS is inlined the output file is HTML. If not, the output is a CSS file.&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;code&gt;width&lt;/code&gt;, &lt;code&gt;height&lt;/code&gt;&lt;/td&gt;
    &lt;td&gt;numbers&lt;/td&gt;
    &lt;td&gt;Viewport width and height in pixels.&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;code&gt;dimensions&lt;/code&gt;&lt;/td&gt;
    &lt;td&gt;array&lt;/td&gt;
    &lt;td&gt;Contains objects with width and height properties. These objects represent the viewports you want to target with above-the-fold CSS. If you have media queries in your CSS, this allows you to generate critical CSS that covers multiple viewport sizes.&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;code&gt;inline&lt;/code&gt;&lt;/td&gt;
    &lt;td&gt;boolean&lt;/td&gt;
    &lt;td&gt;When set to true, the generated critical CSS is inlined in the &lt;head&gt; of the HTML source file.&lt;/head&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;code&gt;minify&lt;/code&gt;&lt;/td&gt;
    &lt;td&gt;boolean&lt;/td&gt;
    &lt;td&gt;When set to true, Critical minifies the generated critical CSS. Can be omitted when extracting critical CSS for multiple resolutions because Critical automatically minifies it to avoid duplicate rule inclusion.&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;Below is a configuration example for extracting critical CSS for multiple resolutions. Add it to &lt;code&gt;critical.js&lt;/code&gt; or play around and tweak the options.&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;critical&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generate&lt;/span&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;ins class=&quot;highlight-line highlight-line-add&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;public/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./index.html&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./index.html&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;inline&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;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;dimensions&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;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&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;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;720&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1280&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&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;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/ins&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 class=&quot;token parameter&quot;&gt;err&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; output&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 keyword&quot;&gt;if&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&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&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&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 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;output&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;    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;Generated critical CSS&#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&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;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;In this example, &lt;code&gt;index.html&lt;/code&gt; is both the source file and the destination file because the &lt;code&gt;inline&lt;/code&gt; option is set to true. Critical first reads the HTML source file, extracts critical CSS and then overwrites &lt;code&gt;index.html&lt;/code&gt; with critical CSS inlined in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dimensions&lt;/code&gt; array has two viewport sizes specified: 300 x 500 for extra small screens and 1280 x 720 for standard laptop screens.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;minify&lt;/code&gt; option is omitted because Critical automatically minifies the extracted CSS when there are multiple viewport sizes specified.&lt;/p&gt;
&lt;h3 id=&quot;run-critical&quot;&gt;Run Critical &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-extract-and-inline-critical-css/#run-critical&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Add Critical to your scripts in &lt;code&gt;package.json&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;&lt;span class=&quot;token literal-property property&quot;&gt;scripts&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&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token string-property property&quot;&gt;&quot;start&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;node server.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;  &lt;span class=&quot;token string-property property&quot;&gt;&quot;critical&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;node critical.js&quot;&lt;/span&gt;&lt;/ins&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;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Terminal&lt;/strong&gt; (note: if the Terminal button does not show you may need to use the Fullscreen option).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To generate critical CSS, in the console, run:&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 function&quot;&gt;npm&lt;/span&gt; run critical&lt;br /&gt;refresh&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;figure data-size=&quot;full&quot;&gt;
  &lt;img alt=&quot;Success message saying &amp;#x27;Generated critical CSS&amp;#x27; in the console]()&quot; class=&quot;fixed-width-img&quot; decoding=&quot;async&quot; height=&quot;45&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 243px) 243px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/alfJqwxI2WU3bUOT4xUZ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/alfJqwxI2WU3bUOT4xUZ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/alfJqwxI2WU3bUOT4xUZ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/alfJqwxI2WU3bUOT4xUZ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/alfJqwxI2WU3bUOT4xUZ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/alfJqwxI2WU3bUOT4xUZ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/alfJqwxI2WU3bUOT4xUZ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/alfJqwxI2WU3bUOT4xUZ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/alfJqwxI2WU3bUOT4xUZ.png?auto=format&amp;w=486 486w&quot; width=&quot;243&quot; /&gt;
  &lt;figcaption&gt;Success message in the console&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; Glitch console and editor don&#39;t automatically sync, so &lt;code&gt;refresh&lt;/code&gt; command is neccessary to update the editor with files generated from the console. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Now in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; tag of &lt;code&gt;index.html&lt;/code&gt;, generated critical CSS is inlined between &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tags, followed by a script that loads the rest of the CSS asynchronously.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;index.html with inlined critical CSS&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/admin/NEQAkJJDV712y944PAxo.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/NEQAkJJDV712y944PAxo.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/NEQAkJJDV712y944PAxo.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/NEQAkJJDV712y944PAxo.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/NEQAkJJDV712y944PAxo.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/NEQAkJJDV712y944PAxo.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/NEQAkJJDV712y944PAxo.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/NEQAkJJDV712y944PAxo.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/NEQAkJJDV712y944PAxo.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/NEQAkJJDV712y944PAxo.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/NEQAkJJDV712y944PAxo.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/NEQAkJJDV712y944PAxo.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/NEQAkJJDV712y944PAxo.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/NEQAkJJDV712y944PAxo.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/NEQAkJJDV712y944PAxo.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/NEQAkJJDV712y944PAxo.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/NEQAkJJDV712y944PAxo.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/NEQAkJJDV712y944PAxo.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Inlined critical CSS&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;measure-again&quot;&gt;Measure again &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-extract-and-inline-critical-css/#measure-again&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Follow the steps from the beginning of the codelab to run Lighthouse performance audit again. The results you get will look similar to this:&lt;/p&gt;
 &lt;img alt=&quot;Lighthouse audit showing performance score of 100, FCP 0.9 seconds and improved filmstrip view of loading the app&quot; decoding=&quot;async&quot; height=&quot;407&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 716px) 716px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/7be1SdC4JpavF7338VuT.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/7be1SdC4JpavF7338VuT.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/7be1SdC4JpavF7338VuT.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/7be1SdC4JpavF7338VuT.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/7be1SdC4JpavF7338VuT.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/7be1SdC4JpavF7338VuT.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/7be1SdC4JpavF7338VuT.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/7be1SdC4JpavF7338VuT.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/7be1SdC4JpavF7338VuT.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/7be1SdC4JpavF7338VuT.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/7be1SdC4JpavF7338VuT.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/7be1SdC4JpavF7338VuT.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/7be1SdC4JpavF7338VuT.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/7be1SdC4JpavF7338VuT.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/7be1SdC4JpavF7338VuT.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/7be1SdC4JpavF7338VuT.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/7be1SdC4JpavF7338VuT.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/7be1SdC4JpavF7338VuT.png?auto=format&amp;w=1432 1432w&quot; width=&quot;716&quot; /&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;Success&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; The filmstrip view shows that content is rendered much sooner and this is reflected in improved paint metrics. And &amp;quot;Eliminate render-blocking resources&amp;quot; has been eliminated! 🎉 &lt;/div&gt;&lt;/aside&gt;
</content>
    <author>
      <name>Milica Mihajlija</name>
    </author>
  </entry>
  
  <entry>
    <title>Extract critical CSS</title>
    <link href="https://web.dev/extract-critical-css/"/>
    <updated>2019-05-29T00:00:00Z</updated>
    <id>https://web.dev/extract-critical-css/</id>
    <content type="html" mode="escaped">&lt;p&gt;The browser must download and parse CSS files before it can show the page, which makes CSS a render-blocking resource. If CSS files are big, or network conditions are poor, requests for CSS files can significantly increase the time it takes for a web page to render.&lt;/p&gt;
&lt;aside class=&quot;aside flow color-secondary-box-text bg-secondary-box-bg&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Highlighter pen&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M10.22 9.49l-5.91 6c-.77.8-.7 2.05.08 2.85L.77 22h5.68l.74-.75c.78.81 1.95.86 2.73.05l5.96-6.05-5.66-5.76zm12.46-4l-2.82-2.87c-.78-.8-2.07-.84-2.84-.04l-5.75 5.85 5.66 5.75 5.69-5.78c.77-.81.83-2.11.06-2.91z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Key Term&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Critical CSS is a technique that extracts the CSS for above-the-fold content in order to render content to the user as fast as possible. &lt;/div&gt;&lt;/aside&gt;
&lt;figure&gt;
  &lt;img alt=&quot;An illustration of a laptop and a mobile device with web pages overflowing the edges of screens&quot; decoding=&quot;async&quot; height=&quot;469&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/t3Kkvh265zi6naTBga41.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/t3Kkvh265zi6naTBga41.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/t3Kkvh265zi6naTBga41.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/t3Kkvh265zi6naTBga41.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/t3Kkvh265zi6naTBga41.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/t3Kkvh265zi6naTBga41.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/t3Kkvh265zi6naTBga41.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/t3Kkvh265zi6naTBga41.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/t3Kkvh265zi6naTBga41.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/t3Kkvh265zi6naTBga41.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/t3Kkvh265zi6naTBga41.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/t3Kkvh265zi6naTBga41.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/t3Kkvh265zi6naTBga41.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/t3Kkvh265zi6naTBga41.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/t3Kkvh265zi6naTBga41.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/t3Kkvh265zi6naTBga41.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/t3Kkvh265zi6naTBga41.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/t3Kkvh265zi6naTBga41.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&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; Above-the-fold is all the content a viewer sees on page load, before scrolling. There is no universally defined pixel height of what is considered above the fold content since there is a myriad of devices and screen sizes. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Inlining extracted styles in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of the HTML document eliminates the need to make an additional request to fetch these styles. The remainder of the CSS can be loaded asynchronously.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;HTML file with critical CSS inlined in the head&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/admin/RVU3OphqtjlkrlAtKLEn.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/RVU3OphqtjlkrlAtKLEn.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/RVU3OphqtjlkrlAtKLEn.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/RVU3OphqtjlkrlAtKLEn.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/RVU3OphqtjlkrlAtKLEn.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/RVU3OphqtjlkrlAtKLEn.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/RVU3OphqtjlkrlAtKLEn.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/RVU3OphqtjlkrlAtKLEn.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/RVU3OphqtjlkrlAtKLEn.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/RVU3OphqtjlkrlAtKLEn.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/RVU3OphqtjlkrlAtKLEn.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/RVU3OphqtjlkrlAtKLEn.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/RVU3OphqtjlkrlAtKLEn.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/RVU3OphqtjlkrlAtKLEn.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/RVU3OphqtjlkrlAtKLEn.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/RVU3OphqtjlkrlAtKLEn.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/RVU3OphqtjlkrlAtKLEn.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/RVU3OphqtjlkrlAtKLEn.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
    &lt;figcaption&gt;
    Inlined critical CSS
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Improving render times can make a huge difference in &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/&quot;&gt;perceived performance&lt;/a&gt;, especially under poor network conditions. On mobile networks, high latency is an issue regardless of bandwidth.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Filmstrip view comparison of loading a page with render-blocking CSS (top) and the same page with inlined critical CSS (bottom) on a 3G connection. Top filmstrip shows six blank frames before finally displaying content. Bottom filmstrip displays meaningful content in the first frame.&quot; decoding=&quot;async&quot; height=&quot;363&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/NdQz49RVgdHoh3Fff0yr.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/NdQz49RVgdHoh3Fff0yr.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/NdQz49RVgdHoh3Fff0yr.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/NdQz49RVgdHoh3Fff0yr.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/NdQz49RVgdHoh3Fff0yr.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/NdQz49RVgdHoh3Fff0yr.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/NdQz49RVgdHoh3Fff0yr.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/NdQz49RVgdHoh3Fff0yr.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/NdQz49RVgdHoh3Fff0yr.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/NdQz49RVgdHoh3Fff0yr.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/NdQz49RVgdHoh3Fff0yr.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/NdQz49RVgdHoh3Fff0yr.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/NdQz49RVgdHoh3Fff0yr.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/NdQz49RVgdHoh3Fff0yr.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/NdQz49RVgdHoh3Fff0yr.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/NdQz49RVgdHoh3Fff0yr.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/NdQz49RVgdHoh3Fff0yr.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/NdQz49RVgdHoh3Fff0yr.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Comparison of loading a page with render-blocking CSS (top) and the same page with inlined critical CSS (bottom) on a 3G connection
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If you have poor &lt;a href=&quot;https://web.dev/fcp/&quot;&gt;First Contentful Paint (FCP)&lt;/a&gt; and see &amp;quot;Eliminate render-blocking resource&amp;quot; opportunity in Lighthouse audits it&#39;s a good idea to give critical CSS a go.&lt;/p&gt;
&lt;img alt=&quot;Lighthouse audit with &amp;#x27;Eliminate render-blocking resource&amp;#x27; or &amp;#x27;Defer unused CSS&amp;#x27; opportunities&quot; decoding=&quot;async&quot; height=&quot;449&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 743px) 743px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/0xea7menL90lWHwbjZoP.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/0xea7menL90lWHwbjZoP.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/0xea7menL90lWHwbjZoP.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/0xea7menL90lWHwbjZoP.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/0xea7menL90lWHwbjZoP.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/0xea7menL90lWHwbjZoP.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/0xea7menL90lWHwbjZoP.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/0xea7menL90lWHwbjZoP.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/0xea7menL90lWHwbjZoP.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/0xea7menL90lWHwbjZoP.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/0xea7menL90lWHwbjZoP.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/0xea7menL90lWHwbjZoP.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/0xea7menL90lWHwbjZoP.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/0xea7menL90lWHwbjZoP.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/0xea7menL90lWHwbjZoP.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/0xea7menL90lWHwbjZoP.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/0xea7menL90lWHwbjZoP.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/0xea7menL90lWHwbjZoP.png?auto=format&amp;w=1486 1486w&quot; width=&quot;743&quot; /&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Gotchas&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Keep in mind that if you inline a large amount of CSS, it delays the transmission of the rest of the HTML document. If everything is prioritized then nothing is. Inlining also has some downsides in that it prevents the browser from caching the CSS for reuse on subsequent page loads, so it&#39;s best to use it sparingly. &lt;/div&gt;&lt;/aside&gt;
&lt;p id=&quot;14KB&quot;&gt;To minimize the number of roundtrips to first render, aim to keep above-the-fold content under &lt;strong&gt;14 KB&lt;/strong&gt; (compressed).&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; New &lt;a href=&quot;https://hpbn.co/building-blocks-of-tcp/&quot;&gt;TCP&lt;/a&gt; connections cannot immediately use the full available bandwidth between the client and the server, they all go through &lt;a href=&quot;https://hpbn.co/building-blocks-of-tcp/#slow-start&quot;&gt;slow-start&lt;/a&gt; to avoid overloading the connection with more data than it can carry. In this process, the server starts the transfer with a small amount of data and if it reaches the client in perfect condition, doubles the amount in the next roundtrip. For most servers, 10 packets or approximately 14 KB is the maximum that can be transferred in the first roundtrip. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The performance impact you can achieve with this technique depends on the type of your website. Generally speaking, the more CSS a site has, the greater the possible impact of inlined CSS.&lt;/p&gt;
&lt;h2 id=&quot;overview-of-tools&quot;&gt;Overview of tools &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/extract-critical-css/#overview-of-tools&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are a number of great tools that can automatically determine the critical CSS for a page. This is good news because doing this manually would be a tedious process. It requires analysis of the entire DOM to determine the styles that are applied to each element in the viewport.&lt;/p&gt;
&lt;h3 id=&quot;critical&quot;&gt;Critical &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/extract-critical-css/#critical&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/addyosmani/critical&quot; rel=&quot;noopener&quot;&gt;Critical&lt;/a&gt; extracts, minifies and inlines above-the-fold CSS and is available as &lt;a href=&quot;https://www.npmjs.com/package/critical&quot; rel=&quot;noopener&quot;&gt;npm module&lt;/a&gt;. It can be used with Gulp (directly) or with Grunt (as a &lt;a href=&quot;https://github.com/bezoerb/grunt-critical&quot; rel=&quot;noopener&quot;&gt;plugin&lt;/a&gt;) and there&#39;s a &lt;a href=&quot;https://github.com/anthonygore/html-critical-webpack-plugin&quot; rel=&quot;noopener&quot;&gt;webpack plugin&lt;/a&gt; too.&lt;/p&gt;
&lt;p&gt;It&#39;s a simple tool that takes a lot of thinking out of the process. You don&#39;t even have to specify the stylesheets, Critical automatically detects them. It also supports extracting critical CSS for multiple screen resolutions.&lt;/p&gt;
&lt;h3 id=&quot;criticalcss&quot;&gt;criticalCSS &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/extract-critical-css/#criticalcss&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/filamentgroup/criticalCSS&quot; rel=&quot;noopener&quot;&gt;CriticalCSS&lt;/a&gt; is another &lt;a href=&quot;https://www.npmjs.com/package/criticalcss&quot; rel=&quot;noopener&quot;&gt;npm module&lt;/a&gt; that extracts above-the-fold CSS. It is also available as a CLI.&lt;/p&gt;
&lt;p&gt;It doesn&#39;t have options to inline and minify critical CSS, but it does let you force-include rules that don&#39;t actually belong in critical CSS and gives you more granular control over including &lt;code&gt;@font-face&lt;/code&gt; declarations.&lt;/p&gt;
&lt;h3 id=&quot;penthouse&quot;&gt;Penthouse &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/extract-critical-css/#penthouse&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/pocketjoso/penthouse&quot; rel=&quot;noopener&quot;&gt;Penthouse&lt;/a&gt; is a good choice if your site or app has a large number of styles or styles which are being dynamically injected into the DOM (common in Angular apps). It uses &lt;a href=&quot;https://github.com/GoogleChrome/puppeteer&quot; rel=&quot;noopener&quot;&gt;Puppeteer&lt;/a&gt; under the hood and even features an &lt;a href=&quot;https://jonassebastianohlsson.com/criticalpathcssgenerator/&quot; rel=&quot;noopener&quot;&gt;online hosted version&lt;/a&gt; too.&lt;/p&gt;
&lt;p&gt;Penthouse doesn&#39;t detect stylesheets automatically, you have to specify the HTML and CSS files that you want to generate critical CSS for. The upside is that it&#39;s good at running many jobs in parallel.&lt;/p&gt;
</content>
    <author>
      <name>Milica Mihajlija</name>
    </author>
  </entry>
  
  <entry>
    <title>Adaptive serving based on network quality</title>
    <link href="https://web.dev/adaptive-serving-based-on-network-quality/"/>
    <updated>2019-05-06T00:00:00Z</updated>
    <id>https://web.dev/adaptive-serving-based-on-network-quality/</id>
    <content type="html" mode="escaped">&lt;p&gt;Loading a website can be a very different experience depending on the network
conditions. Everything is usually smooth when you are on a fast network, but
when you&#39;re on the go with a limited data plan and spotty connection, or stuck
with a laptop on slow coffee-shop Wi-Fi, it&#39;s a different story.&lt;/p&gt;
&lt;p&gt;One way to deal with this is by adapting which assets you&#39;re serving to users
based on the quality of their connection. This is now possible with 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;
which enables web applications to access information about the user&#39;s network.&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 61, 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;
      61
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox, Not supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Edge 79, Supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
79
&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Safari, Not supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;p&gt;&lt;/p&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/NetworkInformation#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;h2 id=&quot;usage&quot;&gt;Usage &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/adaptive-serving-based-on-network-quality/#usage&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are many ways you can use this network information to improve the user
experience:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Switch between serving high-definition and low-definition content based on the
user&#39;s network.&lt;/li&gt;
&lt;li&gt;Decide whether to preload resources.&lt;/li&gt;
&lt;li&gt;Defer uploads and downloads when users are on a slow connection.&lt;/li&gt;
&lt;li&gt;Enable offline mode if the network quality is not good enough to load the app
and use the features.&lt;/li&gt;
&lt;li&gt;Warn users that doing something (for example, watching video) over cellular could cost
them money.&lt;/li&gt;
&lt;li&gt;Use it in your analytics to gather data on your users&#39; network quality.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Many applications are already doing something similar. For example, YouTube,
Netflix and most other video (or video calling) services automatically adjust
the resolution during streaming. When Gmail is loading, it provides users with a
link to &amp;quot;load basic HTML (for slow connections)&amp;quot;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A link to load basic HTML version of Gmail when users are on slow connections&quot; decoding=&quot;async&quot; height=&quot;236&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 528px) 528px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/zSTd1IMrb3UJfefdeRAt.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/zSTd1IMrb3UJfefdeRAt.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/zSTd1IMrb3UJfefdeRAt.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/zSTd1IMrb3UJfefdeRAt.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/zSTd1IMrb3UJfefdeRAt.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/zSTd1IMrb3UJfefdeRAt.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/zSTd1IMrb3UJfefdeRAt.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/zSTd1IMrb3UJfefdeRAt.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/zSTd1IMrb3UJfefdeRAt.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/zSTd1IMrb3UJfefdeRAt.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/zSTd1IMrb3UJfefdeRAt.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/zSTd1IMrb3UJfefdeRAt.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/zSTd1IMrb3UJfefdeRAt.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/zSTd1IMrb3UJfefdeRAt.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/zSTd1IMrb3UJfefdeRAt.png?auto=format&amp;w=1056 1056w&quot; width=&quot;528&quot; /&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;how-it-works&quot;&gt;How it works &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/adaptive-serving-based-on-network-quality/#how-it-works&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;navigator.connection&lt;/code&gt; object contains information about a client&#39;s
connection. Its properties are explained in the table bellow.&lt;/p&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;tr&gt;
      &lt;th&gt;Property&lt;/th&gt;
      &lt;th&gt;Explanation&lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;downlink&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;The bandwidth estimate in megabits per second.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;effectiveType&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;The effective type of the connection, with possible values &lt;code&gt;&#39;slow-2g&#39;&lt;/code&gt;, &lt;code&gt;&#39;2g&#39;&lt;/code&gt;, &lt;code&gt;&#39;3g&#39;&lt;/code&gt;, or &lt;code&gt;&#39;4g&#39;&lt;/code&gt; (covers 4g and higher). Determined based on the combination of &lt;a href=&quot;https://wicg.github.io/netinfo/#effective-connection-types&quot;&gt;round-trip time and downlink speed&lt;/a&gt;. For example, fast downlink combined with high latency will have lower effectiveType due to latency.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;onchange&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;An event handler that fires when connection information changes.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;rtt&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;The estimated round-trip latency of the connection in milliseconds.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;saveData&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;A boolean that defines whether the user has requested a reduced data usage mode.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Here&#39;s what this looks like when you run it in the browser&#39;s console:&lt;/p&gt;
&lt;img alt=&quot;Chrome DevTools console displaying the values of navigator.connection object&amp;#x27;s properties&quot; decoding=&quot;async&quot; height=&quot;523&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/ayW8uxhh3S6I4MTipr0m.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/ayW8uxhh3S6I4MTipr0m.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/ayW8uxhh3S6I4MTipr0m.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/ayW8uxhh3S6I4MTipr0m.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/ayW8uxhh3S6I4MTipr0m.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/ayW8uxhh3S6I4MTipr0m.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/ayW8uxhh3S6I4MTipr0m.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/ayW8uxhh3S6I4MTipr0m.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/ayW8uxhh3S6I4MTipr0m.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/ayW8uxhh3S6I4MTipr0m.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/ayW8uxhh3S6I4MTipr0m.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/ayW8uxhh3S6I4MTipr0m.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/ayW8uxhh3S6I4MTipr0m.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/ayW8uxhh3S6I4MTipr0m.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/ayW8uxhh3S6I4MTipr0m.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/ayW8uxhh3S6I4MTipr0m.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/ayW8uxhh3S6I4MTipr0m.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/ayW8uxhh3S6I4MTipr0m.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;The &lt;code&gt;effectiveType&lt;/code&gt; values are also available via
&lt;a href=&quot;https://www.chromestatus.com/feature/5407907378102272&quot; rel=&quot;noopener&quot;&gt;Client Hints&lt;/a&gt;
and allow you to communicate the browser&#39;s connection type to servers.&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 access Network Information API inside &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/ServiceWorker&quot;&gt;Service Workers&lt;/a&gt; to adapt to situations when users are offline. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The &lt;code&gt;onchange&lt;/code&gt; event listener enables you to dynamically adapt to changes in
network quality. If you deferred uploads or downloads because of poor network
conditions, you can rely on the event listener to restart the transfer when it
detects better network conditions. You can also use it to notify users when the
network quality changes. For example, if they lost their Wi-Fi signal and were
dropped to a cellular network this can prevent accidental data transfers (and
charges 💸).&lt;/p&gt;
&lt;p&gt;Use the &lt;code&gt;onchange&lt;/code&gt; event listener as you would any other event listener:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&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;&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;change&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; doSomethingOnChange&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; Network information API is &lt;a href=&quot;https://caniuse.com/#feat=netinfo&quot;&gt;supported in Chromium-based browsers&lt;/a&gt; since version 62. &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/adaptive-serving-based-on-network-quality/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The potential benefits of the Network Information API are big, especially for
users on slow networks and applications that require a lot of bandwidth. Best of
all, it can be used as a progressive enhancement technique.&lt;/p&gt;
</content>
    <author>
      <name>Milica Mihajlija</name>
    </author>
  </entry>
  
  <entry>
    <title>Incorporate performance budgets into your build process</title>
    <link href="https://web.dev/incorporate-performance-budgets-into-your-build-tools/"/>
    <updated>2019-02-01T00:00:00Z</updated>
    <id>https://web.dev/incorporate-performance-budgets-into-your-build-tools/</id>
    <content type="html" mode="escaped">&lt;p&gt;Once you&#39;ve defined a performance budget, it&#39;s time to set up the build process
to keep track of it. There are a number of tools that let you define thresholds
for chosen performance metrics and warn you if you go over budget. Find out
how to choose one that best fits your needs and current setup. 🕵️‍♀️&lt;/p&gt;
&lt;h2 id=&quot;lighthouse-performance-budgets&quot;&gt;Lighthouse performance budgets &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/incorporate-performance-budgets-into-your-build-tools/#lighthouse-performance-budgets&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt; is an auditing tool that tests sites in a few key areas—performance, accessibility, best practices and how well your site performs as a progressive web application.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/#cli&quot; rel=&quot;noopener&quot;&gt;command line version&lt;/a&gt; of Lighthouse (v5+) supports setting &lt;a href=&quot;https://web.dev/use-lighthouse-for-performance-budgets/&quot;&gt;performance budgets&lt;/a&gt; based on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;resource size&lt;/li&gt;
&lt;li&gt;resource count&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can set budgets for any of the following resource types:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;document&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;image&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;media&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;other&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;script&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stylesheet&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;third-party&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;total&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Budgets are set in a JSON file and after defining them the new &amp;quot;Over Budget&amp;quot; column tells you whether you&#39;re exceeding any limits.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Budgets section in Lighthouse report&quot; decoding=&quot;async&quot; height=&quot;199&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/fsRNXJXQ5JESUuwsIlLn.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/fsRNXJXQ5JESUuwsIlLn.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/fsRNXJXQ5JESUuwsIlLn.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/fsRNXJXQ5JESUuwsIlLn.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/fsRNXJXQ5JESUuwsIlLn.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/fsRNXJXQ5JESUuwsIlLn.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/fsRNXJXQ5JESUuwsIlLn.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/fsRNXJXQ5JESUuwsIlLn.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/fsRNXJXQ5JESUuwsIlLn.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/fsRNXJXQ5JESUuwsIlLn.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/fsRNXJXQ5JESUuwsIlLn.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/fsRNXJXQ5JESUuwsIlLn.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/fsRNXJXQ5JESUuwsIlLn.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/fsRNXJXQ5JESUuwsIlLn.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/fsRNXJXQ5JESUuwsIlLn.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/fsRNXJXQ5JESUuwsIlLn.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/fsRNXJXQ5JESUuwsIlLn.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/fsRNXJXQ5JESUuwsIlLn.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    &quot;Budgets&quot; section in Lighthouse report
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;webpack-performance-hints&quot;&gt;Webpack performance hints &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/incorporate-performance-budgets-into-your-build-tools/#webpack-performance-hints&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/webpack/&quot;&gt;Webpack&lt;/a&gt; is a powerful build tool for optimizing how your code is delivered to the users. It also supports setting performance budgets based on &lt;strong&gt;asset size&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Turn on &lt;a href=&quot;https://webpack.js.org/configuration/performance/&quot; rel=&quot;noopener&quot;&gt;performance hints&lt;/a&gt; in &lt;code&gt;webpack.config.js&lt;/code&gt; to get command line warnings or errors when your bundle size grows over the limit. It&#39;s a great way to stay mindful about asset sizes throughout the development.&lt;/p&gt;
&lt;p&gt;After the build step, webpack outputs a color-coded list of assets and their sizes. Anything over budget is highlighted in yellow.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Webpack output highlighting bundle.js&quot; decoding=&quot;async&quot; height=&quot;86&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 617px) 617px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/G96Z2pZVL6aowVRa49rA.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/G96Z2pZVL6aowVRa49rA.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/G96Z2pZVL6aowVRa49rA.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/G96Z2pZVL6aowVRa49rA.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/G96Z2pZVL6aowVRa49rA.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/G96Z2pZVL6aowVRa49rA.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/G96Z2pZVL6aowVRa49rA.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/G96Z2pZVL6aowVRa49rA.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/G96Z2pZVL6aowVRa49rA.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/G96Z2pZVL6aowVRa49rA.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/G96Z2pZVL6aowVRa49rA.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/G96Z2pZVL6aowVRa49rA.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/G96Z2pZVL6aowVRa49rA.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/G96Z2pZVL6aowVRa49rA.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/G96Z2pZVL6aowVRa49rA.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/G96Z2pZVL6aowVRa49rA.png?auto=format&amp;w=1234 1234w&quot; width=&quot;617&quot; /&gt;
  &lt;figcaption&gt;
    The highlighted bundle.js is bigger than your budget
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The default limit for both assets and entry-points is &lt;strong&gt;250 KB&lt;/strong&gt;. You can set your own targets in the configuration file.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Webpack bundle size warning&quot; decoding=&quot;async&quot; height=&quot;104&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 566px) 566px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/StfxwSScNVzmeM70gwp4.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/StfxwSScNVzmeM70gwp4.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/StfxwSScNVzmeM70gwp4.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/StfxwSScNVzmeM70gwp4.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/StfxwSScNVzmeM70gwp4.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/StfxwSScNVzmeM70gwp4.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/StfxwSScNVzmeM70gwp4.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/StfxwSScNVzmeM70gwp4.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/StfxwSScNVzmeM70gwp4.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/StfxwSScNVzmeM70gwp4.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/StfxwSScNVzmeM70gwp4.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/StfxwSScNVzmeM70gwp4.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/StfxwSScNVzmeM70gwp4.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/StfxwSScNVzmeM70gwp4.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/StfxwSScNVzmeM70gwp4.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/StfxwSScNVzmeM70gwp4.jpg?auto=format&amp;w=1132 1132w&quot; width=&quot;566&quot; /&gt;
  &lt;figcaption&gt;
    Webpack bundle size warning ⚠️
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The budgets are compared against &lt;strong&gt;uncompressed asset sizes&lt;/strong&gt;. Uncompressed &lt;a href=&quot;https://v8.dev/blog/cost-of-javascript-2019&quot; rel=&quot;noopener&quot;&gt;JavaScript size is related to the execution time&lt;/a&gt; and big files can take a long time to execute, especially on mobile devices.&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; Compressed asset sizes affect the transfer time, which is very important on slow networks. &lt;/div&gt;&lt;/aside&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Webpack performance optimization recommendation&quot; decoding=&quot;async&quot; height=&quot;84&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 556px) 556px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/ogH1cMiF96iyhj8CiENK.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/ogH1cMiF96iyhj8CiENK.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/ogH1cMiF96iyhj8CiENK.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/ogH1cMiF96iyhj8CiENK.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/ogH1cMiF96iyhj8CiENK.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/ogH1cMiF96iyhj8CiENK.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/ogH1cMiF96iyhj8CiENK.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/ogH1cMiF96iyhj8CiENK.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/ogH1cMiF96iyhj8CiENK.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/ogH1cMiF96iyhj8CiENK.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/ogH1cMiF96iyhj8CiENK.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/ogH1cMiF96iyhj8CiENK.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/ogH1cMiF96iyhj8CiENK.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/ogH1cMiF96iyhj8CiENK.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/ogH1cMiF96iyhj8CiENK.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/ogH1cMiF96iyhj8CiENK.jpg?auto=format&amp;w=1112 1112w&quot; width=&quot;556&quot; /&gt;
  &lt;figcaption&gt;
    Bonus feature: webpack won’t only warn you, it will give you a recommendation on how to downsize your bundles. 💁
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;bundlesize&quot;&gt;Bundlesize &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/incorporate-performance-budgets-into-your-build-tools/#bundlesize&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/siddharthkp/bundlesize&quot; rel=&quot;noopener&quot;&gt;Bundlesize&lt;/a&gt; is a simple npm package that tests asset size against a threshold you&#39;ve set. It can run locally and integrate with your CI.&lt;/p&gt;
&lt;h3 id=&quot;bundlesize-cli&quot;&gt;Bundlesize CLI &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/incorporate-performance-budgets-into-your-build-tools/#bundlesize-cli&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Run &lt;a href=&quot;https://github.com/siddharthkp/bundlesize#cli&quot; rel=&quot;noopener&quot;&gt;bundlesize CLI&lt;/a&gt; by specifying a threshold and the file that you want to test.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;bundlesize -f &lt;span class=&quot;token string&quot;&gt;&quot;dist/bundle.js&quot;&lt;/span&gt; -s 170kB&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Bundlesize outputs color-coded test results in one line.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Failing bundlesize CLI test&quot; decoding=&quot;async&quot; height=&quot;36&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 473px) 473px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/6YsTnhpUlzbTB1ja23yP.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/6YsTnhpUlzbTB1ja23yP.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/6YsTnhpUlzbTB1ja23yP.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/6YsTnhpUlzbTB1ja23yP.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/6YsTnhpUlzbTB1ja23yP.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/6YsTnhpUlzbTB1ja23yP.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/6YsTnhpUlzbTB1ja23yP.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/6YsTnhpUlzbTB1ja23yP.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/6YsTnhpUlzbTB1ja23yP.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/6YsTnhpUlzbTB1ja23yP.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/6YsTnhpUlzbTB1ja23yP.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/6YsTnhpUlzbTB1ja23yP.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/6YsTnhpUlzbTB1ja23yP.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/6YsTnhpUlzbTB1ja23yP.png?auto=format&amp;w=946 946w&quot; width=&quot;473&quot; /&gt;
  &lt;figcaption&gt;
    Failing bundlesize CLI test ❌
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Passing bundlesize CLI test&quot; decoding=&quot;async&quot; height=&quot;36&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 460px) 460px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/TjynyaIzZlRa6xE2uVcD.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/TjynyaIzZlRa6xE2uVcD.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/TjynyaIzZlRa6xE2uVcD.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/TjynyaIzZlRa6xE2uVcD.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/TjynyaIzZlRa6xE2uVcD.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/TjynyaIzZlRa6xE2uVcD.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/TjynyaIzZlRa6xE2uVcD.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/TjynyaIzZlRa6xE2uVcD.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/TjynyaIzZlRa6xE2uVcD.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/TjynyaIzZlRa6xE2uVcD.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/TjynyaIzZlRa6xE2uVcD.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/TjynyaIzZlRa6xE2uVcD.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/TjynyaIzZlRa6xE2uVcD.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/TjynyaIzZlRa6xE2uVcD.png?auto=format&amp;w=920 920w&quot; width=&quot;460&quot; /&gt;
  &lt;figcaption&gt;
    Passing bundlesize CLI test ✔️
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;bundlesize-for-ci&quot;&gt;Bundlesize for CI &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/incorporate-performance-budgets-into-your-build-tools/#bundlesize-for-ci&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You&#39;ll get the most value out of bundlesize if you integrate it with a CI to automatically enforce size limits on pull requests. &lt;strong&gt;If bundlesize test fails, that pull request is not merged.&lt;/strong&gt; It works for pull requests on GitHub with &lt;a href=&quot;https://travis-ci.org/&quot; rel=&quot;noopener&quot;&gt;Travis CI&lt;/a&gt;, &lt;a href=&quot;https://circleci.com/&quot; rel=&quot;noopener&quot;&gt;CircleCI&lt;/a&gt;, &lt;a href=&quot;http://www.wercker.com/&quot; rel=&quot;noopener&quot;&gt;Wercker&lt;/a&gt;, and &lt;a href=&quot;http://readme.drone.io/&quot; rel=&quot;noopener&quot;&gt;Drone&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Bundlesize check status on GitHub&quot; class=&quot;screenshot&quot; decoding=&quot;async&quot; height=&quot;326&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/RKpNwfcXRoKZ34qDoxcO.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/RKpNwfcXRoKZ34qDoxcO.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/RKpNwfcXRoKZ34qDoxcO.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/RKpNwfcXRoKZ34qDoxcO.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/RKpNwfcXRoKZ34qDoxcO.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/RKpNwfcXRoKZ34qDoxcO.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/RKpNwfcXRoKZ34qDoxcO.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/RKpNwfcXRoKZ34qDoxcO.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/RKpNwfcXRoKZ34qDoxcO.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/RKpNwfcXRoKZ34qDoxcO.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/RKpNwfcXRoKZ34qDoxcO.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/RKpNwfcXRoKZ34qDoxcO.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/RKpNwfcXRoKZ34qDoxcO.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/RKpNwfcXRoKZ34qDoxcO.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/RKpNwfcXRoKZ34qDoxcO.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/RKpNwfcXRoKZ34qDoxcO.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/RKpNwfcXRoKZ34qDoxcO.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/RKpNwfcXRoKZ34qDoxcO.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Bundlesize check status on GitHub
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You may have a fast app today, but adding new code can often change this. Checking pull requests with bundlesize will help you avoid performance regressions. Bootstrap, Tinder, Trivago and many others use it to keep their budgets in check.&lt;/p&gt;
&lt;p&gt;With bundlesize, it&#39;s possible to set thresholds for each file separately. This is especially useful if you are splitting a bundle in your application.&lt;/p&gt;
&lt;p&gt;By default, &lt;strong&gt;it tests gzipped asset sizes&lt;/strong&gt;. You can use the compression option to switch to &lt;a href=&quot;https://css-tricks.com/brotli-static-compression/&quot; rel=&quot;noopener&quot;&gt;brotli compression&lt;/a&gt; or turn it off completely.&lt;/p&gt;
&lt;h2 id=&quot;lighthouse-bot&quot;&gt;Lighthouse Bot &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/incorporate-performance-budgets-into-your-build-tools/#lighthouse-bot&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Lighthouse Bot&quot; decoding=&quot;async&quot; height=&quot;267&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/5zLlIEUm06wEbu3J1WjG.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/5zLlIEUm06wEbu3J1WjG.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/5zLlIEUm06wEbu3J1WjG.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/5zLlIEUm06wEbu3J1WjG.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/5zLlIEUm06wEbu3J1WjG.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/5zLlIEUm06wEbu3J1WjG.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/5zLlIEUm06wEbu3J1WjG.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/5zLlIEUm06wEbu3J1WjG.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/5zLlIEUm06wEbu3J1WjG.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/5zLlIEUm06wEbu3J1WjG.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/5zLlIEUm06wEbu3J1WjG.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/5zLlIEUm06wEbu3J1WjG.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/5zLlIEUm06wEbu3J1WjG.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/5zLlIEUm06wEbu3J1WjG.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/5zLlIEUm06wEbu3J1WjG.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/5zLlIEUm06wEbu3J1WjG.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/5zLlIEUm06wEbu3J1WjG.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/5zLlIEUm06wEbu3J1WjG.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/ebidel/lighthouse-ci&quot; rel=&quot;noopener&quot;&gt;Lighthouse Bot&lt;/a&gt; integrates with Travis CI and enforces budgets based on any of the five Lighthouse audit categories. For example, a budget of 100 for your Lighthouse performance score. It&#39;s sometimes simpler to keep an eye on a single number than individual asset budgets and Lighthouse scores take many things into account.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Lighthouse scores 💯&quot; class=&quot;screenshot&quot; decoding=&quot;async&quot; height=&quot;211&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/6yVLSYcr0eCytJixZ1bK.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/6yVLSYcr0eCytJixZ1bK.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/6yVLSYcr0eCytJixZ1bK.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/6yVLSYcr0eCytJixZ1bK.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/6yVLSYcr0eCytJixZ1bK.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/6yVLSYcr0eCytJixZ1bK.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/6yVLSYcr0eCytJixZ1bK.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/6yVLSYcr0eCytJixZ1bK.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/6yVLSYcr0eCytJixZ1bK.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/6yVLSYcr0eCytJixZ1bK.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/6yVLSYcr0eCytJixZ1bK.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/6yVLSYcr0eCytJixZ1bK.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/6yVLSYcr0eCytJixZ1bK.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/6yVLSYcr0eCytJixZ1bK.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/6yVLSYcr0eCytJixZ1bK.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/6yVLSYcr0eCytJixZ1bK.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/6yVLSYcr0eCytJixZ1bK.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/6yVLSYcr0eCytJixZ1bK.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Lighthouse scores 💯
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Lighthouse Bot runs an audit after you deploy a site to staging server. In &lt;code&gt;.travis.yml&lt;/code&gt; set budgets for particular Lighthouse categories with &lt;code&gt;--perf&lt;/code&gt;, &lt;code&gt;--a11y&lt;/code&gt;, &lt;code&gt;--bp&lt;/code&gt;, &lt;code&gt;--seo&lt;/code&gt; or &lt;code&gt;--pwa&lt;/code&gt; options. Aim to stay in the green zone with scores of at least 90.&lt;/p&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;
after_success:
  - ./deploy.sh # Deploy the PR changes to staging server
  - npm run lh -- --perf=96 https://staging.example.com # Run Lighthouse test
&lt;/pre&gt;
&lt;p&gt;If the scores for a pull request on GitHub fall below the threshold you&#39;ve set, &lt;strong&gt;Lighthouse Bot can prevent pull request from being merged&lt;/strong&gt;. ⛔&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Lighthouse Bot check status on GitHub&quot; class=&quot;screenshot&quot; decoding=&quot;async&quot; height=&quot;175&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/nVDqJAA1DZDFqpOaQA9J.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/nVDqJAA1DZDFqpOaQA9J.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/nVDqJAA1DZDFqpOaQA9J.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/nVDqJAA1DZDFqpOaQA9J.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/nVDqJAA1DZDFqpOaQA9J.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/nVDqJAA1DZDFqpOaQA9J.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/nVDqJAA1DZDFqpOaQA9J.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/nVDqJAA1DZDFqpOaQA9J.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/nVDqJAA1DZDFqpOaQA9J.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/nVDqJAA1DZDFqpOaQA9J.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/nVDqJAA1DZDFqpOaQA9J.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/nVDqJAA1DZDFqpOaQA9J.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/nVDqJAA1DZDFqpOaQA9J.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/nVDqJAA1DZDFqpOaQA9J.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/nVDqJAA1DZDFqpOaQA9J.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/nVDqJAA1DZDFqpOaQA9J.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/nVDqJAA1DZDFqpOaQA9J.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/nVDqJAA1DZDFqpOaQA9J.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Lighthouse Bot check status on GitHub
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Lighthouse Bot&lt;/strong&gt; then comments on your pull request with updated scores. This is a neat feature which encourages conversation about performance as code changes are happening.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Lighthouse reporting scores on pull request&quot; decoding=&quot;async&quot; height=&quot;383&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/q1BUp838y2oSs70Suxyb.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/q1BUp838y2oSs70Suxyb.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/q1BUp838y2oSs70Suxyb.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/q1BUp838y2oSs70Suxyb.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/q1BUp838y2oSs70Suxyb.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/q1BUp838y2oSs70Suxyb.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/q1BUp838y2oSs70Suxyb.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/q1BUp838y2oSs70Suxyb.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/q1BUp838y2oSs70Suxyb.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/q1BUp838y2oSs70Suxyb.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/q1BUp838y2oSs70Suxyb.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/q1BUp838y2oSs70Suxyb.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/q1BUp838y2oSs70Suxyb.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/q1BUp838y2oSs70Suxyb.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/q1BUp838y2oSs70Suxyb.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/q1BUp838y2oSs70Suxyb.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/q1BUp838y2oSs70Suxyb.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/q1BUp838y2oSs70Suxyb.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Lighthouse reporting scores on pull request 💬
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If you find your pull request blocked by a poor Lighthouse score, run an audit with &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/#cli&quot; rel=&quot;noopener&quot;&gt;Lighthouse CLI&lt;/a&gt; or in &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/#devtools&quot; rel=&quot;noopener&quot;&gt;Dev Tools&lt;/a&gt;. It generates a report with details about bottlenecks and hints for simple optimizations.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/incorporate-performance-budgets-into-your-build-tools/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;tr&gt;
      &lt;th&gt;Tool&lt;/th&gt;
      &lt;th&gt;CLI&lt;/th&gt;
      &lt;th&gt;CI&lt;/th&gt;
      &lt;th&gt;Summary&lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Lighthouse&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;
        &lt;ul&gt;
          &lt;li&gt;Budgets for different types of resources based on their size or count.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;webpack&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;
        &lt;ul&gt;
          &lt;li&gt;Budgets based on sizes of assets generated by webpack.&lt;/li&gt;
          &lt;li&gt;Checks uncompressed sizes.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;bundlesize&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;
        &lt;ul&gt;
          &lt;li&gt;Budgets based on sizes of specific resources.&lt;/li&gt;
          &lt;li&gt;Checks compressed or uncompressed sizes.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Lighthouse Bot&lt;/td&gt;
      &lt;td&gt;❌&lt;/td&gt;
      &lt;td&gt;✔️&lt;/td&gt;
      &lt;td&gt;
        &lt;ul&gt;
          &lt;li&gt;Budgets based on Lighthouse scores.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/table&gt;
&lt;/div&gt;
</content>
    <author>
      <name>Milica Mihajlija</name>
    </author>
  </entry>
  
  <entry>
    <title>Using bundlesize with Travis CI</title>
    <link href="https://web.dev/using-bundlesize-with-travis-ci/"/>
    <updated>2019-02-01T00:00:00Z</updated>
    <id>https://web.dev/using-bundlesize-with-travis-ci/</id>
    <content type="html" mode="escaped">&lt;p&gt;Using &lt;a href=&quot;https://github.com/siddharthkp/bundlesize&quot; rel=&quot;noopener&quot;&gt;bundlesize&lt;/a&gt; with &lt;a href=&quot;https://travis-ci.org/&quot; rel=&quot;noopener&quot;&gt;Travis
CI&lt;/a&gt; lets you define performance budgets with minimal
setup and enforce them as part of your development workflow. Travis CI is a
service that runs tests for your app in the cloud every time you push code to
GitHub. You can &lt;a href=&quot;https://help.github.com/articles/about-required-status-checks/&quot; rel=&quot;noopener&quot;&gt;configure your
repository&lt;/a&gt; so
that it won&#39;t allow merging pull-requests unless the bundlesize tests have
passed.&lt;/p&gt;
&lt;p&gt;Bundlesize&#39;s GitHub checks include a size comparison to the main branch and
a warning in case of a big jump in size.&lt;/p&gt;
&lt;img alt=&quot;Bundlesize check on GitHub&quot; decoding=&quot;async&quot; height=&quot;316&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 769px) 769px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/8Mm1WPga9dbeIzGrv2fQ.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/8Mm1WPga9dbeIzGrv2fQ.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/8Mm1WPga9dbeIzGrv2fQ.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/8Mm1WPga9dbeIzGrv2fQ.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/8Mm1WPga9dbeIzGrv2fQ.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/8Mm1WPga9dbeIzGrv2fQ.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/8Mm1WPga9dbeIzGrv2fQ.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/8Mm1WPga9dbeIzGrv2fQ.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/8Mm1WPga9dbeIzGrv2fQ.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/8Mm1WPga9dbeIzGrv2fQ.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/8Mm1WPga9dbeIzGrv2fQ.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/8Mm1WPga9dbeIzGrv2fQ.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/8Mm1WPga9dbeIzGrv2fQ.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/8Mm1WPga9dbeIzGrv2fQ.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/8Mm1WPga9dbeIzGrv2fQ.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/8Mm1WPga9dbeIzGrv2fQ.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/8Mm1WPga9dbeIzGrv2fQ.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/8Mm1WPga9dbeIzGrv2fQ.jpg?auto=format&amp;w=1538 1538w&quot; width=&quot;769&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; You can also use bundlesize with &lt;a href=&quot;https://circleci.com/&quot;&gt;Circle CI&lt;/a&gt;, &lt;a href=&quot;https://app.wercker.com/&quot;&gt;Wrecker&lt;/a&gt; and &lt;a href=&quot;https://readme.drone.io/&quot;&gt;Drone&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;To see it in action, here&#39;s an app bundled with
&lt;a href=&quot;https://webpack.js.org/&quot; rel=&quot;noopener&quot;&gt;webpack&lt;/a&gt; that lets you &lt;a href=&quot;https://glitch.com/edit/#!/scarce-pixie&quot; rel=&quot;noopener&quot;&gt;vote for your favorite
kitty&lt;/a&gt;.&lt;/p&gt;
&lt;a href=&quot;https://glitch.com/edit/#!/scarce-pixie&quot;&gt;
  &lt;img alt=&quot;Cat voting app&quot; decoding=&quot;async&quot; height=&quot;567&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/DGSSFfpAMIaFqX8MwWss.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/DGSSFfpAMIaFqX8MwWss.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/DGSSFfpAMIaFqX8MwWss.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/DGSSFfpAMIaFqX8MwWss.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/DGSSFfpAMIaFqX8MwWss.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/DGSSFfpAMIaFqX8MwWss.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/DGSSFfpAMIaFqX8MwWss.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/DGSSFfpAMIaFqX8MwWss.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/DGSSFfpAMIaFqX8MwWss.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/DGSSFfpAMIaFqX8MwWss.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/DGSSFfpAMIaFqX8MwWss.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/DGSSFfpAMIaFqX8MwWss.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/DGSSFfpAMIaFqX8MwWss.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/DGSSFfpAMIaFqX8MwWss.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/DGSSFfpAMIaFqX8MwWss.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/DGSSFfpAMIaFqX8MwWss.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/DGSSFfpAMIaFqX8MwWss.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/DGSSFfpAMIaFqX8MwWss.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/a&gt;
&lt;h2 id=&quot;set-the-performance-budget&quot;&gt;Set the performance budget &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-bundlesize-with-travis-ci/#set-the-performance-budget&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://glitch.com/edit/#!/scarce-pixie&quot; rel=&quot;noopener&quot;&gt;This Glitch&lt;/a&gt; already contains
bundlesize.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Remix to Edit&lt;/strong&gt; to make the project editable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The main bundle of this app is in the public folder. To test its size, add the
following section to the &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 property&quot;&gt;&quot;bundlesize&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 punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;path&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./public/*.bundle.js&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;maxSize&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;170 kB&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;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; You can also set &lt;a href=&quot;https://github.com/siddharthkp/bundlesize#1-add-the-path-and-maxsize-in-your-packagejson&quot;&gt;different thresholds for different files&lt;/a&gt;. This is especially useful if you are &lt;a href=&quot;https://web.dev/reduce-javascript-payloads-with-code-splitting&quot;&gt;splitting a bundle&lt;/a&gt; in your application. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;To keep the compressed JavaScript bundle size &lt;a href=&quot;https://web.dev/your-first-performance-budget#budget-for-quantity-based-metrics&quot;&gt;under the recommended
limit&lt;/a&gt;,
set the performance budget to 170KB in the &lt;code&gt;maxSize&lt;/code&gt; field.&lt;/p&gt;
&lt;p&gt;Bundlesize supports &lt;a href=&quot;https://github.com/isaacs/node-glob&quot; rel=&quot;noopener&quot;&gt;glob patterns&lt;/a&gt; and the *
wildcard character in the file path will match all bundle names in the public
folder.&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; By default, bundlesize tests gzipped sizes. You can use the &lt;a href=&quot;https://github.com/siddharthkp/bundlesize#1-add-the-path-and-maxsize-in-your-packagejson&quot;&gt;compression option&lt;/a&gt; to switch to &lt;a href=&quot;https://en.wikipedia.org/wiki/Brotli&quot;&gt;brotli&lt;/a&gt; compression or turn it off completely. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;create-a-test-script&quot;&gt;Create a test script &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-bundlesize-with-travis-ci/#create-a-test-script&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Since Travis needs a test to run, add a test script to &lt;code&gt;package.json&lt;/code&gt;:&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 property&quot;&gt;&quot;scripts&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 property&quot;&gt;&quot;start&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 &amp;amp;&amp;amp; http-server -c-1&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;test&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bundlesize&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;set-up-continuous-integration&quot;&gt;Set up continuous integration &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-bundlesize-with-travis-ci/#set-up-continuous-integration&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;integrate-github-and-travis-ci&quot;&gt;Integrate GitHub and Travis CI &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-bundlesize-with-travis-ci/#integrate-github-and-travis-ci&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;First, create a new repository for this project on your GitHub account and
initialize it with a &lt;code&gt;README.md&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You&#39;ll need to &lt;a href=&quot;https://docs.travis-ci.com/user/tutorial&quot; rel=&quot;noopener&quot;&gt;register an account on
Travis&lt;/a&gt; and activate GitHub Apps
integration under the Settings section of your profile.&lt;/p&gt;
&lt;img alt=&quot;GitHub Apps integration on Travis CI&quot; decoding=&quot;async&quot; height=&quot;508&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/kMgVmB5rzRJN3DlqP08R.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/kMgVmB5rzRJN3DlqP08R.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/kMgVmB5rzRJN3DlqP08R.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/kMgVmB5rzRJN3DlqP08R.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/kMgVmB5rzRJN3DlqP08R.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/kMgVmB5rzRJN3DlqP08R.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/kMgVmB5rzRJN3DlqP08R.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/kMgVmB5rzRJN3DlqP08R.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/kMgVmB5rzRJN3DlqP08R.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/kMgVmB5rzRJN3DlqP08R.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/kMgVmB5rzRJN3DlqP08R.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/kMgVmB5rzRJN3DlqP08R.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/kMgVmB5rzRJN3DlqP08R.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/kMgVmB5rzRJN3DlqP08R.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/kMgVmB5rzRJN3DlqP08R.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/kMgVmB5rzRJN3DlqP08R.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/kMgVmB5rzRJN3DlqP08R.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/kMgVmB5rzRJN3DlqP08R.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Once you have an account, go to &lt;strong&gt;Settings&lt;/strong&gt; under your profile, click the &lt;strong&gt;Sync
account&lt;/strong&gt; button, and make sure your new repo is listed on Travis.&lt;/p&gt;
&lt;img alt=&quot;Travis CI Sync button&quot; decoding=&quot;async&quot; height=&quot;54&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 160px) 160px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/Zi9Oo5SCfM5P7IxejYd3.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/Zi9Oo5SCfM5P7IxejYd3.png?auto=format&amp;w=160 160w, https://web-dev.imgix.net/image/admin/Zi9Oo5SCfM5P7IxejYd3.png?auto=format&amp;w=182 182w, https://web-dev.imgix.net/image/admin/Zi9Oo5SCfM5P7IxejYd3.png?auto=format&amp;w=208 208w, https://web-dev.imgix.net/image/admin/Zi9Oo5SCfM5P7IxejYd3.png?auto=format&amp;w=237 237w, https://web-dev.imgix.net/image/admin/Zi9Oo5SCfM5P7IxejYd3.png?auto=format&amp;w=270 270w, https://web-dev.imgix.net/image/admin/Zi9Oo5SCfM5P7IxejYd3.png?auto=format&amp;w=308 308w, https://web-dev.imgix.net/image/admin/Zi9Oo5SCfM5P7IxejYd3.png?auto=format&amp;w=320 320w&quot; width=&quot;160&quot; /&gt;
&lt;h3 id=&quot;authorize-bundlesize-to-post-on-pull-requests&quot;&gt;Authorize bundlesize to post on pull requests &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-bundlesize-with-travis-ci/#authorize-bundlesize-to-post-on-pull-requests&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Bundlesize needs authorization to be able to post on pull requests, so &lt;a href=&quot;https://github.com/login/oauth/authorize?scope=repo%3Astatus&amp;amp;client_id=6756cb03a8d6528aca5a&quot; rel=&quot;noopener&quot;&gt;visit
this link to get the bundlesize
token&lt;/a&gt;
that will be stored in the Travis configuration.&lt;/p&gt;
&lt;img alt=&quot;bundlesize token&quot; decoding=&quot;async&quot; height=&quot;330&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 619px) 619px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/GkEMv2VCb25oC9lDSARQ.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/GkEMv2VCb25oC9lDSARQ.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/GkEMv2VCb25oC9lDSARQ.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/GkEMv2VCb25oC9lDSARQ.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/GkEMv2VCb25oC9lDSARQ.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/GkEMv2VCb25oC9lDSARQ.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/GkEMv2VCb25oC9lDSARQ.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/GkEMv2VCb25oC9lDSARQ.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/GkEMv2VCb25oC9lDSARQ.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/GkEMv2VCb25oC9lDSARQ.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/GkEMv2VCb25oC9lDSARQ.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/GkEMv2VCb25oC9lDSARQ.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/GkEMv2VCb25oC9lDSARQ.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/GkEMv2VCb25oC9lDSARQ.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/GkEMv2VCb25oC9lDSARQ.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/GkEMv2VCb25oC9lDSARQ.jpg?auto=format&amp;w=1238 1238w&quot; width=&quot;619&quot; /&gt;
&lt;p&gt;In your project&#39;s Travis dashboard, go to &lt;strong&gt;More options&lt;/strong&gt; &amp;gt; &lt;strong&gt;Settings&lt;/strong&gt; &amp;gt; &lt;strong&gt;Environment
variables&lt;/strong&gt;.&lt;/p&gt;
&lt;img alt=&quot;Adding environment variables on Travis CI&quot; decoding=&quot;async&quot; height=&quot;233&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 789px) 789px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/gol14FsIsYPyPWwIeYfI.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/gol14FsIsYPyPWwIeYfI.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/gol14FsIsYPyPWwIeYfI.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/gol14FsIsYPyPWwIeYfI.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/gol14FsIsYPyPWwIeYfI.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/gol14FsIsYPyPWwIeYfI.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/gol14FsIsYPyPWwIeYfI.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/gol14FsIsYPyPWwIeYfI.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/gol14FsIsYPyPWwIeYfI.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/gol14FsIsYPyPWwIeYfI.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/gol14FsIsYPyPWwIeYfI.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/gol14FsIsYPyPWwIeYfI.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/gol14FsIsYPyPWwIeYfI.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/gol14FsIsYPyPWwIeYfI.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/gol14FsIsYPyPWwIeYfI.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/gol14FsIsYPyPWwIeYfI.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/gol14FsIsYPyPWwIeYfI.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/gol14FsIsYPyPWwIeYfI.png?auto=format&amp;w=1578 1578w&quot; width=&quot;789&quot; /&gt;
&lt;p&gt;Add a new environment variable with the token as the value field and
BUNDLESIZE_GITHUB_TOKEN as the name.&lt;/p&gt;
&lt;p&gt;The last thing you need to kick-off continuous integration is a &lt;code&gt;.travis.yml&lt;/code&gt;
file, which tells Travis CI what to do. To speed things up, it is already
included in the project and it specifies that the app is using NodeJS.&lt;/p&gt;
&lt;p&gt;With this step, you&#39;re all set up and bundlesize will warn you if your
JavaScript ever goes over the budget. Even when you start off great, over time,
as you add new features, kilobytes can pile up. With automated performance
budget monitoring, you can rest easy knowing that it won&#39;t go unnoticed.&lt;/p&gt;
&lt;h2 id=&quot;try-it-out&quot;&gt;Try it out &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-bundlesize-with-travis-ci/#try-it-out&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;trigger-your-first-bundlesize-test&quot;&gt;Trigger your first bundlesize test &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-bundlesize-with-travis-ci/#trigger-your-first-bundlesize-test&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To see how the app stacks up against the performance budget, add the code to the
GitHub repo that you created in step 3.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;On Glitch, click &lt;strong&gt;Tools&lt;/strong&gt; &amp;gt; &lt;strong&gt;Git, Import, and Export&lt;/strong&gt; &amp;gt; &lt;strong&gt;Export to GitHub&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the pop-up, enter your GitHub username and the name of the repo as
&lt;code&gt;username/repo&lt;/code&gt;. Glitch will export your app to a new branch named &amp;quot;glitch&amp;quot;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a new pull request by clicking the &lt;strong&gt;New pull request&lt;/strong&gt; button on
the homepage of the repository.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You&#39;ll now see status checks in progress on the pull request page.&lt;/p&gt;
&lt;img alt=&quot;GitHub checks in progress&quot; decoding=&quot;async&quot; height=&quot;351&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 774px) 774px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/SrdHGr9z5QY1vEfBwNIY.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/SrdHGr9z5QY1vEfBwNIY.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/SrdHGr9z5QY1vEfBwNIY.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/SrdHGr9z5QY1vEfBwNIY.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/SrdHGr9z5QY1vEfBwNIY.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/SrdHGr9z5QY1vEfBwNIY.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/SrdHGr9z5QY1vEfBwNIY.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/SrdHGr9z5QY1vEfBwNIY.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/SrdHGr9z5QY1vEfBwNIY.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/SrdHGr9z5QY1vEfBwNIY.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/SrdHGr9z5QY1vEfBwNIY.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/SrdHGr9z5QY1vEfBwNIY.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/SrdHGr9z5QY1vEfBwNIY.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/SrdHGr9z5QY1vEfBwNIY.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/SrdHGr9z5QY1vEfBwNIY.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/SrdHGr9z5QY1vEfBwNIY.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/SrdHGr9z5QY1vEfBwNIY.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/SrdHGr9z5QY1vEfBwNIY.png?auto=format&amp;w=1548 1548w&quot; width=&quot;774&quot; /&gt;
&lt;p&gt;It won&#39;t take long until all checks are done. Unfortunately, the cat voting app
is a bit bloated and does not pass the performance budget check. The main bundle
is 266 KB and the budget is 170 KB.&lt;/p&gt;
&lt;img alt=&quot;Failed bundlesize check&quot; decoding=&quot;async&quot; height=&quot;347&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 774px) 774px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/Dt31nFkMvJjn1me6cir3.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/Dt31nFkMvJjn1me6cir3.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/Dt31nFkMvJjn1me6cir3.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/Dt31nFkMvJjn1me6cir3.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/Dt31nFkMvJjn1me6cir3.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/Dt31nFkMvJjn1me6cir3.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/Dt31nFkMvJjn1me6cir3.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/Dt31nFkMvJjn1me6cir3.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/Dt31nFkMvJjn1me6cir3.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/Dt31nFkMvJjn1me6cir3.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/Dt31nFkMvJjn1me6cir3.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/Dt31nFkMvJjn1me6cir3.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/Dt31nFkMvJjn1me6cir3.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/Dt31nFkMvJjn1me6cir3.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/Dt31nFkMvJjn1me6cir3.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/Dt31nFkMvJjn1me6cir3.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/Dt31nFkMvJjn1me6cir3.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/Dt31nFkMvJjn1me6cir3.png?auto=format&amp;w=1548 1548w&quot; width=&quot;774&quot; /&gt;
&lt;h3 id=&quot;optimize&quot;&gt;Optimize &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-bundlesize-with-travis-ci/#optimize&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Luckily, there are some easy performance wins you can make by
&lt;a href=&quot;https://web.dev/remove-unused-code&quot;&gt;removing unused code&lt;/a&gt;. There are two main imports in
&lt;code&gt;src/index.js&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; firebase &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;firebase&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;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; moment &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;moment&#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;The app is using &lt;a href=&quot;https://firebase.google.com/products/realtime-database/&quot; rel=&quot;noopener&quot;&gt;Firebase Realtime
Database&lt;/a&gt; to store the
data, but it&#39;s importing the entire firebase package which consists of a lot
more than just a database (auth, storage, messaging etc.).&lt;/p&gt;
&lt;p&gt;Fix this by importing only the package that the app needs in the &lt;code&gt;src/index.js&lt;/code&gt;
file:&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; firebase &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;firebase&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; firebase &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;firebase/app&#39;&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 string&quot;&gt;&#39;firebase/database&#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;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The &lt;code&gt;firebase/app&lt;/code&gt; import, which sets up the API surface for each of the different services, is always required. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;re-run-test&quot;&gt;Re-run test &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-bundlesize-with-travis-ci/#re-run-test&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Since the source file has been updated, you need to run webpack to build the new
bundle file.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Click the &lt;strong&gt;Tools&lt;/strong&gt; button.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then click the &lt;strong&gt;Console&lt;/strong&gt; button. This will open the console in another tab.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the console, type &lt;code&gt;webpack&lt;/code&gt; and wait for it to finish the build.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Export the code to GitHub from &lt;strong&gt;Tools&lt;/strong&gt; &amp;gt; &lt;strong&gt;Git, Import, and Export&lt;/strong&gt; &amp;gt; &lt;strong&gt;Export to GitHub&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go to the pull request page on GitHub and wait for all checks to finish.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;img alt=&quot;Passed bundlesize check&quot; decoding=&quot;async&quot; height=&quot;355&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 778px) 778px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/3aKOqNvvavQ32gl15PmU.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/3aKOqNvvavQ32gl15PmU.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/3aKOqNvvavQ32gl15PmU.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/3aKOqNvvavQ32gl15PmU.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/3aKOqNvvavQ32gl15PmU.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/3aKOqNvvavQ32gl15PmU.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/3aKOqNvvavQ32gl15PmU.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/3aKOqNvvavQ32gl15PmU.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/3aKOqNvvavQ32gl15PmU.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/3aKOqNvvavQ32gl15PmU.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/3aKOqNvvavQ32gl15PmU.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/3aKOqNvvavQ32gl15PmU.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/3aKOqNvvavQ32gl15PmU.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/3aKOqNvvavQ32gl15PmU.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/3aKOqNvvavQ32gl15PmU.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/3aKOqNvvavQ32gl15PmU.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/3aKOqNvvavQ32gl15PmU.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/3aKOqNvvavQ32gl15PmU.png?auto=format&amp;w=1556 1556w&quot; width=&quot;778&quot; /&gt;
&lt;p&gt;Success! The new size of the bundle is 125.5 KB and all the checks have passed.
🎉&lt;/p&gt;
&lt;p&gt;Unlike Firebase, importing parts of the moment library cannot be done as easily,
but it&#39;s worth a shot. Check out how you can further optimize the app in the
&lt;a href=&quot;https://web.dev/codelab-remove-unused-code&quot;&gt;Remove unused code codelab&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;monitor&quot;&gt;Monitor &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-bundlesize-with-travis-ci/#monitor&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The app is now under the budget and all is well. Travis CI and bundlesize will
keep monitoring the performance budget for you, making sure your app stays fast.&lt;/p&gt;
</content>
    <author>
      <name>Milica Mihajlija</name>
    </author>
  </entry>
  
  <entry>
    <title>Setting performance budgets with webpack</title>
    <link href="https://web.dev/codelab-setting-performance-budgets-with-webpack/"/>
    <updated>2019-01-31T00:00:00Z</updated>
    <id>https://web.dev/codelab-setting-performance-budgets-with-webpack/</id>
    <content type="html" mode="escaped">&lt;p&gt;&lt;a href=&quot;https://web.dev/webpack/&quot;&gt;Webpack&lt;/a&gt;
combines all your imported files and packages them into one or more output
files known as bundles. Bundling is neat, but as your app grows your bundles
will grow too. You need to monitor bundle sizes to ensure that they don&#39;t grow
too large and affect how long your application takes to load. Webpack supports
setting &lt;a href=&quot;https://web.dev/performance-budgets-101&quot;&gt;performance budgets&lt;/a&gt;
based on &lt;strong&gt;asset size&lt;/strong&gt; and it can keep an eye on bundle sizes for you.&lt;/p&gt;
&lt;p&gt;To see it in action, here&#39;s an app that counts the days left until New Year&#39;s.
It&#39;s built with &lt;a href=&quot;https://reactjs.org/&quot; rel=&quot;noopener&quot;&gt;React&lt;/a&gt; and &lt;a href=&quot;https://momentjs.com/&quot; rel=&quot;noopener&quot;&gt;moment.js&lt;/a&gt;.
(Just like real-world apps that increasingly rely on frameworks and libraries. 😉)&lt;/p&gt;
&lt;img alt=&quot;An app that counts the days left until New Year&amp;#x27;s day&quot; decoding=&quot;async&quot; height=&quot;459&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/xG1cKnEmDUJk6Dd4892x.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/xG1cKnEmDUJk6Dd4892x.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/xG1cKnEmDUJk6Dd4892x.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/xG1cKnEmDUJk6Dd4892x.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/xG1cKnEmDUJk6Dd4892x.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/xG1cKnEmDUJk6Dd4892x.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/xG1cKnEmDUJk6Dd4892x.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/xG1cKnEmDUJk6Dd4892x.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/xG1cKnEmDUJk6Dd4892x.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/xG1cKnEmDUJk6Dd4892x.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/xG1cKnEmDUJk6Dd4892x.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/xG1cKnEmDUJk6Dd4892x.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/xG1cKnEmDUJk6Dd4892x.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/xG1cKnEmDUJk6Dd4892x.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/xG1cKnEmDUJk6Dd4892x.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/xG1cKnEmDUJk6Dd4892x.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/xG1cKnEmDUJk6Dd4892x.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/xG1cKnEmDUJk6Dd4892x.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h2 id=&quot;measure&quot;&gt;Measure &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-setting-performance-budgets-with-webpack/#measure&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This codelab already contains the app bundled with webpack.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Remix to Edit&lt;/strong&gt; to make the project editable.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Terminal&lt;/strong&gt; (note: if the Terminal button does not show you may need to use the Fullscreen option).&lt;/li&gt;
&lt;li&gt;To get a color-coded list of assets and their sizes, type &lt;code&gt;webpack&lt;/code&gt; in the console.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;webpack&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The main bundle is highlighted in yellow because it&#39;s larger than 244 KiB (250
KB).&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; Webpack uses the unit &amp;quot;KiB&amp;quot;, which you might not have seen before. 1 &amp;quot;KiB&amp;quot; and 1 &amp;quot;KB&amp;quot; are fairly close in size: 1 KiB is 1024 bytes, while 1 KB is 1000 bytes. &lt;/div&gt;&lt;/aside&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Webpack output showing bundle size of 323 KiB&quot; decoding=&quot;async&quot; height=&quot;59&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 640px) 640px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/MpeaVZE4nJHOAAnlQ8Sz.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/MpeaVZE4nJHOAAnlQ8Sz.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/MpeaVZE4nJHOAAnlQ8Sz.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/MpeaVZE4nJHOAAnlQ8Sz.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/MpeaVZE4nJHOAAnlQ8Sz.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/MpeaVZE4nJHOAAnlQ8Sz.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/MpeaVZE4nJHOAAnlQ8Sz.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/MpeaVZE4nJHOAAnlQ8Sz.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/MpeaVZE4nJHOAAnlQ8Sz.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/MpeaVZE4nJHOAAnlQ8Sz.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/MpeaVZE4nJHOAAnlQ8Sz.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/MpeaVZE4nJHOAAnlQ8Sz.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/MpeaVZE4nJHOAAnlQ8Sz.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/MpeaVZE4nJHOAAnlQ8Sz.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/MpeaVZE4nJHOAAnlQ8Sz.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/MpeaVZE4nJHOAAnlQ8Sz.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/MpeaVZE4nJHOAAnlQ8Sz.png?auto=format&amp;w=1280 1280w&quot; width=&quot;640&quot; /&gt;
  &lt;figcaption&gt;
    Webpack warning you about bulky JS bundle ⚠️
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;These warnings are enabled by default in &lt;a href=&quot;https://webpack.js.org/concepts/mode/&quot; rel=&quot;noopener&quot;&gt;production mode&lt;/a&gt;
and the default threshold is &lt;strong&gt;244 KiB uncompressed&lt;/strong&gt;, for both assets and
&lt;a href=&quot;https://webpack.js.org/concepts/entry-points/&quot; rel=&quot;noopener&quot;&gt;entry points&lt;/a&gt;
(the combination of all assets used during the initial load of a page).&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Webpack warning that the asset exceeds the recommended size limit&quot; decoding=&quot;async&quot; height=&quot;108&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 642px) 642px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/tXgrsOqdJAzf6LTelr0v.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/tXgrsOqdJAzf6LTelr0v.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/tXgrsOqdJAzf6LTelr0v.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/tXgrsOqdJAzf6LTelr0v.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/tXgrsOqdJAzf6LTelr0v.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/tXgrsOqdJAzf6LTelr0v.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/tXgrsOqdJAzf6LTelr0v.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/tXgrsOqdJAzf6LTelr0v.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/tXgrsOqdJAzf6LTelr0v.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/tXgrsOqdJAzf6LTelr0v.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/tXgrsOqdJAzf6LTelr0v.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/tXgrsOqdJAzf6LTelr0v.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/tXgrsOqdJAzf6LTelr0v.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/tXgrsOqdJAzf6LTelr0v.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/tXgrsOqdJAzf6LTelr0v.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/tXgrsOqdJAzf6LTelr0v.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/tXgrsOqdJAzf6LTelr0v.png?auto=format&amp;w=1284 1284w&quot; width=&quot;642&quot; /&gt;
  &lt;figcaption&gt;
    Webpack warning you about bulky JS bundle ⚠️
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Webpack will not only warn you, but it will also give you a recommendation on
how to downsize your bundles. You can learn more about the recommended techniques on
&lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/webpack/use-long-term-caching#lazy-loading&quot; rel=&quot;noopener&quot;&gt;Web Fundamentals&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Webpack performance optimization recommendation&quot; decoding=&quot;async&quot; height=&quot;96&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 641px) 641px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/pygtt0At7nmByNKm38hr.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/pygtt0At7nmByNKm38hr.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/pygtt0At7nmByNKm38hr.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/pygtt0At7nmByNKm38hr.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/pygtt0At7nmByNKm38hr.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/pygtt0At7nmByNKm38hr.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/pygtt0At7nmByNKm38hr.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/pygtt0At7nmByNKm38hr.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/pygtt0At7nmByNKm38hr.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/pygtt0At7nmByNKm38hr.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/pygtt0At7nmByNKm38hr.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/pygtt0At7nmByNKm38hr.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/pygtt0At7nmByNKm38hr.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/pygtt0At7nmByNKm38hr.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/pygtt0At7nmByNKm38hr.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/pygtt0At7nmByNKm38hr.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/pygtt0At7nmByNKm38hr.png?auto=format&amp;w=1282 1282w&quot; width=&quot;641&quot; /&gt;
  &lt;figcaption&gt;Webpack performance optimization recommendation 💁&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;set-a-custom-performance-budget&quot;&gt;Set a custom performance budget &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-setting-performance-budgets-with-webpack/#set-a-custom-performance-budget&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An appropriate performance budget will depend on the nature of your project.
It&#39;s always best to &lt;a href=&quot;https://web.dev/your-first-performance-budget&quot;&gt;do your own research&lt;/a&gt;.
A &lt;a href=&quot;https://web.dev/your-first-performance-budget#budget-for-quantity-based-metrics&quot;&gt;good rule&lt;/a&gt;
of thumb is to deliver under 170 KB of compressed/minified
&lt;a href=&quot;https://web.dev/critical-rendering-path/&quot;&gt;critical-path&lt;/a&gt;
resources.&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; Critical-path resources are the minimal set of resources required for the browser to display the first screen&#39;s worth of content. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;For this simple demo, try being even more conservative and set the budget to
100 KB (97.7 KiB). In &lt;code&gt;webpack.config.js&lt;/code&gt;, add the following:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &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;//...&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;performance&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 literal-property property&quot;&gt;maxAssetSize&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100000&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;maxEntrypointSize&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100000&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;hints&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;warning&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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The new performance budget is set in &lt;strong&gt;bytes&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;100000 bytes for individual assets (maxAssetSize)&lt;/li&gt;
&lt;li&gt;100000 bytes for the entry-point (maxEntrypointSize)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this case, there&#39;s only one bundle which also acts as the entry point.&lt;/p&gt;
&lt;p&gt;Possible values for &lt;strong&gt;hints&lt;/strong&gt; are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;warning&lt;/code&gt;&lt;/strong&gt; (default): Displays a yellow warning message, but the build
passes. It&#39;s best to use this in development environments.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;error&lt;/code&gt;&lt;/strong&gt;: Displays a red error message, but the build still passes. This
setting is recommended for production builds.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;false&lt;/code&gt;&lt;/strong&gt;: No warnings or errors are shown.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Webpack performance error in red font&quot; decoding=&quot;async&quot; height=&quot;107&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 606px) 606px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/ZFP0SZfO4zzD0lyD29Vk.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/ZFP0SZfO4zzD0lyD29Vk.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/ZFP0SZfO4zzD0lyD29Vk.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/ZFP0SZfO4zzD0lyD29Vk.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/ZFP0SZfO4zzD0lyD29Vk.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/ZFP0SZfO4zzD0lyD29Vk.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/ZFP0SZfO4zzD0lyD29Vk.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/ZFP0SZfO4zzD0lyD29Vk.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/ZFP0SZfO4zzD0lyD29Vk.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/ZFP0SZfO4zzD0lyD29Vk.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/ZFP0SZfO4zzD0lyD29Vk.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/ZFP0SZfO4zzD0lyD29Vk.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/ZFP0SZfO4zzD0lyD29Vk.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/ZFP0SZfO4zzD0lyD29Vk.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/ZFP0SZfO4zzD0lyD29Vk.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/ZFP0SZfO4zzD0lyD29Vk.png?auto=format&amp;w=1212 1212w&quot; width=&quot;606&quot; /&gt;
  &lt;figcaption&gt;Webpack performance hint &quot;error&quot; 🚨&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;optimize&quot;&gt;Optimize &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-setting-performance-budgets-with-webpack/#optimize&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The purpose of a performance budget is to warn you about performance issues
before they become too difficult to fix. There is always more than one way to
build an app and some techniques will make for faster load times. (A lot of
them are documented right here in &lt;a href=&quot;https://web.dev/fast#optimize-your-javascript&quot;&gt;Optimizing your JavaScript&lt;/a&gt;. 🤓)&lt;/p&gt;
&lt;p&gt;Frameworks and libraries make developers&#39; lives easier, but end users don&#39;t
really care how apps are built, only that they&#39;re functional and fast. If you
find yourself going over the performance budget, it&#39;s time to think about
possible optimizations.&lt;/p&gt;
&lt;p&gt;In the real world, large client-side frameworks are usually hard to swap out,
so it&#39;s important to use them wisely. With a bit of research, you can often
find smaller alternatives to popular libraries that do the job just as well
(&lt;a href=&quot;https://date-fns.org/&quot; rel=&quot;noopener&quot;&gt;date-fns&lt;/a&gt; is a good alternative for &lt;a href=&quot;https://momentjs.com/&quot; rel=&quot;noopener&quot;&gt;moment.js&lt;/a&gt;).
Sometimes it makes more sense to not use a framework or library at all if it
has a significant performance impact.&lt;/p&gt;
&lt;p&gt;Removing unused code a good way to optimize apps that include large third-party
libraries. The &lt;a href=&quot;https://web.dev/remove-unused-code&quot;&gt;Remove unused code guide&lt;/a&gt; explains this
process in detail and here&#39;s a quick way to rewrite the countdown code without using moment.js.&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;app/components/Countdown.jsx&lt;/strong&gt; remove:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;del class=&quot;highlight-line highlight-line-remove&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; today &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;moment&lt;/span&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;/del&gt;&lt;br /&gt;&lt;del class=&quot;highlight-line highlight-line-remove&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; yearEnd &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;moment&lt;/span&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;endOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;year&#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;/del&gt;&lt;br /&gt;&lt;del class=&quot;highlight-line highlight-line-remove&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; daysLeft &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; yearEnd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;diff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;today&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;days&#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;/del&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;And delete this line:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;del class=&quot;highlight-line highlight-line-remove&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; moment &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;moment&#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;/del&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;It takes a bit of math, but you can implement the same countdown with vanilla
JavaScript:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; today &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;Date&lt;/span&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;const&lt;/span&gt; year &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; today&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getFullYear&lt;/span&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;const&lt;/span&gt; yearEnd &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;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;year&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;31&lt;/span&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 comment&quot;&gt;//months are zero indexed in JS&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; timeDiff &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;abs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;yearEnd&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTime&lt;/span&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; today&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTime&lt;/span&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;const&lt;/span&gt; daysLeft &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;ceil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;timeDiff &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 number&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3600&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;24&lt;/span&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;Now remove &lt;code&gt;moment.js&lt;/code&gt; from &lt;code&gt;package.json&lt;/code&gt; and run webpack in the console
again to build the optimized bundle.&lt;/p&gt;
&lt;p&gt;Ta-da! You have shaved off 223 KiB (230KB) and the app is under budget.🎉&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Webpack bundle size output after optimization is 97.7 KiB&quot; decoding=&quot;async&quot; height=&quot;58&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 473px) 473px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/A6TIDToLthjRrHvxluND.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/A6TIDToLthjRrHvxluND.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/A6TIDToLthjRrHvxluND.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/A6TIDToLthjRrHvxluND.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/A6TIDToLthjRrHvxluND.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/A6TIDToLthjRrHvxluND.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/A6TIDToLthjRrHvxluND.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/A6TIDToLthjRrHvxluND.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/A6TIDToLthjRrHvxluND.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/A6TIDToLthjRrHvxluND.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/A6TIDToLthjRrHvxluND.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/A6TIDToLthjRrHvxluND.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/A6TIDToLthjRrHvxluND.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/A6TIDToLthjRrHvxluND.png?auto=format&amp;w=946 946w&quot; width=&quot;473&quot; /&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;monitor&quot;&gt;Monitor &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-setting-performance-budgets-with-webpack/#monitor&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Setting up a performance budget in webpack takes just a couple of lines of code
and it will warn you if you ever (accidentally) add a big dependency. The
saying goes &amp;quot;out of sight, out of mind&amp;quot; but webpack can make sure that you are
aware of performance implications at all times.&lt;/p&gt;
</content>
    <author>
      <name>Milica Mihajlija</name>
    </author>
  </entry>
  
  <entry>
    <title>Using Lighthouse Bot to set a performance budget</title>
    <link href="https://web.dev/using-lighthouse-bot-to-set-a-performance-budget/"/>
    <updated>2019-01-28T00:00:00Z</updated>
    <id>https://web.dev/using-lighthouse-bot-to-set-a-performance-budget/</id>
    <content type="html" mode="escaped">&lt;p&gt;You&#39;ve done hard work to get fast, now make sure you stay fast by automating
performance testing with &lt;a href=&quot;https://github.com/ebidel/lighthousebot&quot; rel=&quot;noopener&quot;&gt;Lighthouse Bot&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt; grades your app
in 5 categories, and one of those is performance. While you could try to remember to
monitor performance changes with &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/#devtools&quot; rel=&quot;noopener&quot;&gt;DevTools&lt;/a&gt;
or &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/#cli&quot; rel=&quot;noopener&quot;&gt;Lighthouse CLI&lt;/a&gt; every
time you edit your code, you don&#39;t have to do that. Tools can do the tedious
stuff for you. &lt;a href=&quot;https://travis-ci.com/&quot; rel=&quot;noopener&quot;&gt;Travis CI&lt;/a&gt; is a great service that
automatically runs tests for your app in the cloud every time you push new code.&lt;/p&gt;
&lt;p&gt;Lighthouse Bot integrates with Travis CI, and its performance budget feature ensures
that you won&#39;t accidentally downgrade performance without noticing. You can
&lt;a href=&quot;https://help.github.com/articles/about-required-status-checks/&quot; rel=&quot;noopener&quot;&gt;configure your repository&lt;/a&gt;
so that it won&#39;t allow merging pull-requests if the Lighthouse scores fall below
the threshold you&#39;ve set (e.g. &amp;lt; 96/100).&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Failing Lighthouse Bot checks on GitHub&quot; decoding=&quot;async&quot; height=&quot;360&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/LIEdWOuIGubFE0JgBM5Y.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/LIEdWOuIGubFE0JgBM5Y.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/LIEdWOuIGubFE0JgBM5Y.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/LIEdWOuIGubFE0JgBM5Y.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/LIEdWOuIGubFE0JgBM5Y.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/LIEdWOuIGubFE0JgBM5Y.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/LIEdWOuIGubFE0JgBM5Y.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/LIEdWOuIGubFE0JgBM5Y.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/LIEdWOuIGubFE0JgBM5Y.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/LIEdWOuIGubFE0JgBM5Y.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/LIEdWOuIGubFE0JgBM5Y.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/LIEdWOuIGubFE0JgBM5Y.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/LIEdWOuIGubFE0JgBM5Y.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/LIEdWOuIGubFE0JgBM5Y.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/LIEdWOuIGubFE0JgBM5Y.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/LIEdWOuIGubFE0JgBM5Y.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/LIEdWOuIGubFE0JgBM5Y.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/LIEdWOuIGubFE0JgBM5Y.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Lighthouse Bot checks on GitHub.&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; Lighthouse Bot used to be called Lighthouse CI. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Although you can test performance on localhost, your site will often perform
differently on live servers. To get a more realistic picture, it&#39;s best to
deploy your site to a staging server. You can use any hosting service; this
guide will take &lt;a href=&quot;https://firebase.google.com/docs/hosting/&quot; rel=&quot;noopener&quot;&gt;Firebase hosting&lt;/a&gt;
for a spin.&lt;/p&gt;
&lt;h2 id=&quot;1-setup&quot;&gt;1. Setup &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-lighthouse-bot-to-set-a-performance-budget/#1-setup&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This simple app helps you sort three numbers.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/GoogleChromeLabs/lighthouse-bot-starter&quot; rel=&quot;noopener&quot;&gt;Clone the example from GitHub&lt;/a&gt;,
and make sure to add it as a repository on your GitHub account.&lt;/p&gt;
&lt;h2 id=&quot;2-deploy-to-firebase&quot;&gt;2. Deploy to Firebase &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-lighthouse-bot-to-set-a-performance-budget/#2-deploy-to-firebase&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To get started, you&#39;ll need a Firebase account. Once you&#39;ve taken care of that,
&lt;a href=&quot;https://console.firebase.google.com/&quot; rel=&quot;noopener&quot;&gt;create a new project in the Firebase console&lt;/a&gt;
by clicking &amp;quot;Add project&amp;quot;:&lt;/p&gt;
&lt;img alt=&quot;&quot; decoding=&quot;async&quot; height=&quot;552&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 517px) 517px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/SYTTK9CHxnTt1n7vONKU.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/SYTTK9CHxnTt1n7vONKU.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/SYTTK9CHxnTt1n7vONKU.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/SYTTK9CHxnTt1n7vONKU.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/SYTTK9CHxnTt1n7vONKU.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/SYTTK9CHxnTt1n7vONKU.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/SYTTK9CHxnTt1n7vONKU.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/SYTTK9CHxnTt1n7vONKU.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/SYTTK9CHxnTt1n7vONKU.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/SYTTK9CHxnTt1n7vONKU.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/SYTTK9CHxnTt1n7vONKU.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/SYTTK9CHxnTt1n7vONKU.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/SYTTK9CHxnTt1n7vONKU.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/SYTTK9CHxnTt1n7vONKU.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/SYTTK9CHxnTt1n7vONKU.png?auto=format&amp;w=1034 1034w&quot; width=&quot;517&quot; /&gt;
&lt;h3 id=&quot;deploying-to-firebase&quot;&gt;Deploying to Firebase &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-lighthouse-bot-to-set-a-performance-budget/#deploying-to-firebase&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You&#39;ll need &lt;a href=&quot;https://firebase.google.com/docs/cli/&quot; rel=&quot;noopener&quot;&gt;Firebase CLI&lt;/a&gt; to deploy the
app. Even if you already have it installed, it&#39;s good practice to frequently
update the CLI to the latest version with this command:&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 function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; -g firebase-tools&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;To authorize the Firebase CLI, run:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;firebase login&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Now initialize the project:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;firebase init&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The console will ask you a series of questions during setup:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When prompted to select features, choose &amp;quot;Hosting.&amp;quot;&lt;/li&gt;
&lt;li&gt;For the default Firebase project, select the project that you&#39;ve created in
the Firebase console.&lt;/li&gt;
&lt;li&gt;Type in &amp;quot;public&amp;quot; as your public directory.&lt;/li&gt;
&lt;li&gt;Type &amp;quot;N&amp;quot; (no) to configuring as a single-page app.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This process creates a &lt;code&gt;firebase.json&lt;/code&gt; configuration file in the root of your
project directory.&lt;/p&gt;
&lt;p&gt;Congrats, you&#39;re ready to deploy! Run:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;firebase deploy&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In a split second, you&#39;ll have a live app.&lt;/p&gt;
&lt;h2 id=&quot;3-setting-up-travis&quot;&gt;3. Setting up Travis &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-lighthouse-bot-to-set-a-performance-budget/#3-setting-up-travis&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You&#39;ll need to &lt;a href=&quot;https://travis-ci.com/&quot; rel=&quot;noopener&quot;&gt;register an account&lt;/a&gt; on Travis and then
activate GitHub Apps integration under the Settings section of your profile.&lt;/p&gt;
&lt;img alt=&quot;GitHub Apps integration on Travis CI&quot; decoding=&quot;async&quot; height=&quot;508&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/fNGAjcYM5Rw3e43PExoD.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/fNGAjcYM5Rw3e43PExoD.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/fNGAjcYM5Rw3e43PExoD.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/fNGAjcYM5Rw3e43PExoD.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/fNGAjcYM5Rw3e43PExoD.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/fNGAjcYM5Rw3e43PExoD.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/fNGAjcYM5Rw3e43PExoD.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/fNGAjcYM5Rw3e43PExoD.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/fNGAjcYM5Rw3e43PExoD.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/fNGAjcYM5Rw3e43PExoD.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/fNGAjcYM5Rw3e43PExoD.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/fNGAjcYM5Rw3e43PExoD.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/fNGAjcYM5Rw3e43PExoD.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/fNGAjcYM5Rw3e43PExoD.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/fNGAjcYM5Rw3e43PExoD.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/fNGAjcYM5Rw3e43PExoD.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/fNGAjcYM5Rw3e43PExoD.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/fNGAjcYM5Rw3e43PExoD.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h3 id=&quot;once-you-have-an-account&quot;&gt;Once you have an account &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-lighthouse-bot-to-set-a-performance-budget/#once-you-have-an-account&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Go to Settings under your profile, hit the Sync account button, and make sure
your project repo is listed on Travis.&lt;/p&gt;
&lt;img alt=&quot;&quot; decoding=&quot;async&quot; height=&quot;54&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 160px) 160px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/dnXFBpBw4WvWbXQL1DQf.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/dnXFBpBw4WvWbXQL1DQf.png?auto=format&amp;w=160 160w, https://web-dev.imgix.net/image/admin/dnXFBpBw4WvWbXQL1DQf.png?auto=format&amp;w=182 182w, https://web-dev.imgix.net/image/admin/dnXFBpBw4WvWbXQL1DQf.png?auto=format&amp;w=208 208w, https://web-dev.imgix.net/image/admin/dnXFBpBw4WvWbXQL1DQf.png?auto=format&amp;w=237 237w, https://web-dev.imgix.net/image/admin/dnXFBpBw4WvWbXQL1DQf.png?auto=format&amp;w=270 270w, https://web-dev.imgix.net/image/admin/dnXFBpBw4WvWbXQL1DQf.png?auto=format&amp;w=308 308w, https://web-dev.imgix.net/image/admin/dnXFBpBw4WvWbXQL1DQf.png?auto=format&amp;w=320 320w&quot; width=&quot;160&quot; /&gt;
&lt;p&gt;To kick-off continuous integration, you need two things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;To have a &lt;code&gt;.travis.yml&lt;/code&gt; file in the root directory&lt;/li&gt;
&lt;li&gt;To trigger a build by doing a regular old git push&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The &lt;code&gt;lighthouse-bot-starter&lt;/code&gt; repo already has a &lt;code&gt;.travis.yml&lt;/code&gt; YAML file:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;language&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; node_js&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;node_js&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 string&quot;&gt;&quot;8.1.3&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;install&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; npm install&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;before_script&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; npm install &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;g firebase&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;tools&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;script&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; webpack&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The YAML file tells Travis to install all the dependencies and build your app.
Now it&#39;s your turn to &lt;strong&gt;push the example app to your own GitHub repository&lt;/strong&gt;.
If you haven&#39;t already, run the following command:&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 function&quot;&gt;git&lt;/span&gt; push origin main&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Click on your repo under Settings in Travis to see your project&#39;s Travis
dashboard. If everything is cool, you&#39;ll see your build go from yellow to green
in a couple of minutes. 🎉&lt;/p&gt;
&lt;h2 id=&quot;4-automate-firebase-deployment-with-travis&quot;&gt;4. Automate Firebase deployment with Travis &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-lighthouse-bot-to-set-a-performance-budget/#4-automate-firebase-deployment-with-travis&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In Step 2, you logged into your Firebase account and deployed the app from the
command line with &lt;code&gt;firebase deploy&lt;/code&gt;. In order for Travis to deploy your app to
Firebase, you have to authorize it. How do you do that? With a Firebase token.
🗝️🔥&lt;/p&gt;
&lt;h3 id=&quot;authorize-firebase&quot;&gt;Authorize Firebase &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-lighthouse-bot-to-set-a-performance-budget/#authorize-firebase&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To generate the token run this command:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;firebase login:ci&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;It will open a new tab in a browser window so that Firebase can verify you.
After that, look back at the console, and you&#39;ll see your freshly minted token.
Copy it and go back to Travis.&lt;/p&gt;
&lt;p&gt;In your project&#39;s Travis dashboard, go to &lt;strong&gt;More options&lt;/strong&gt; &amp;gt; &lt;strong&gt;Settings&lt;/strong&gt; &amp;gt; &lt;strong&gt;Environment variables&lt;/strong&gt;.&lt;/p&gt;
&lt;img alt=&quot;&quot; decoding=&quot;async&quot; height=&quot;233&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 789px) 789px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/uU7MBc5NdBDZch3ZE3Zd.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/uU7MBc5NdBDZch3ZE3Zd.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/uU7MBc5NdBDZch3ZE3Zd.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/uU7MBc5NdBDZch3ZE3Zd.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/uU7MBc5NdBDZch3ZE3Zd.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/uU7MBc5NdBDZch3ZE3Zd.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/uU7MBc5NdBDZch3ZE3Zd.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/uU7MBc5NdBDZch3ZE3Zd.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/uU7MBc5NdBDZch3ZE3Zd.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/uU7MBc5NdBDZch3ZE3Zd.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/uU7MBc5NdBDZch3ZE3Zd.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/uU7MBc5NdBDZch3ZE3Zd.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/uU7MBc5NdBDZch3ZE3Zd.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/uU7MBc5NdBDZch3ZE3Zd.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/uU7MBc5NdBDZch3ZE3Zd.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/uU7MBc5NdBDZch3ZE3Zd.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/uU7MBc5NdBDZch3ZE3Zd.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/uU7MBc5NdBDZch3ZE3Zd.png?auto=format&amp;w=1578 1578w&quot; width=&quot;789&quot; /&gt;
&lt;p&gt;Paste the token in the value field, name the variable &lt;code&gt;FIREBASE_TOKEN&lt;/code&gt;, and add it.&lt;/p&gt;
&lt;h3 id=&quot;add-deployment-to-your-travis-setup&quot;&gt;Add deployment to your Travis setup &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-lighthouse-bot-to-set-a-performance-budget/#add-deployment-to-your-travis-setup&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You need the following lines to tell Travis to deploy the app after every successful build.
Add them to the end of the &lt;code&gt;.travis.yml&lt;/code&gt; file. 🔚&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;after_success&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; firebase deploy &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;token $FIREBASE_TOKEN &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;non&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;interactive&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Push this change to GitHub and wait for your first automated deployment.
If you take a look at your Travis log, it should soon say ✔️ Deploy complete!&lt;/p&gt;
&lt;p&gt;Now whenever you make changes to your app, they will be automatically deployed to Firebase.&lt;/p&gt;
&lt;h2 id=&quot;5-setting-up-lighthouse-bot&quot;&gt;5. Setting up Lighthouse Bot &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-lighthouse-bot-to-set-a-performance-budget/#5-setting-up-lighthouse-bot&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Friendly Lighthouse Bot updates you on your app&#39;s Lighthouse scores.
It just needs an invitation to your repo.&lt;/p&gt;
&lt;p&gt;On GitHub, go to your project&#39;s settings and &lt;strong&gt;add &amp;quot;lighthousebot&amp;quot; as a collaborator&lt;/strong&gt; (Settings&amp;gt;Collaborators):&lt;/p&gt;
&lt;img alt=&quot;Lighthouse bot collaborator status&quot; decoding=&quot;async&quot; height=&quot;298&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/H2aLCOr36UDwm5Yk1k9r.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/H2aLCOr36UDwm5Yk1k9r.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/H2aLCOr36UDwm5Yk1k9r.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/H2aLCOr36UDwm5Yk1k9r.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/H2aLCOr36UDwm5Yk1k9r.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/H2aLCOr36UDwm5Yk1k9r.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/H2aLCOr36UDwm5Yk1k9r.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/H2aLCOr36UDwm5Yk1k9r.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/H2aLCOr36UDwm5Yk1k9r.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/H2aLCOr36UDwm5Yk1k9r.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/H2aLCOr36UDwm5Yk1k9r.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/H2aLCOr36UDwm5Yk1k9r.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/H2aLCOr36UDwm5Yk1k9r.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/H2aLCOr36UDwm5Yk1k9r.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/H2aLCOr36UDwm5Yk1k9r.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/H2aLCOr36UDwm5Yk1k9r.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/H2aLCOr36UDwm5Yk1k9r.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/H2aLCOr36UDwm5Yk1k9r.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Approving these requests is a manual process so they don&#39;t always happen
instantly. Before you start testing, make sure lighthousebot has approved its
collaborator status. In the meantime, you also need to add another key to your
project&#39;s environment variables on Travis. &lt;a href=&quot;https://docs.google.com/forms/d/e/1FAIpQLSdIc3QNIMn7bBMgl2cfxmmo6wGBlUpdLGxjB_ml464t9eCg_A/viewform&quot; rel=&quot;noopener&quot;&gt;Leave your email
here&lt;/a&gt;,
and you&#39;ll get a Lighthouse Bot key in your inbox. 📬&lt;/p&gt;
&lt;p&gt;On Travis, add this key as an environment variable and name it &lt;code&gt;LIGHTHOUSE_API_KEY&lt;/code&gt;:&lt;/p&gt;
&lt;img alt=&quot;&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/0XCrRSbUg1Sdca8k9xK9.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/0XCrRSbUg1Sdca8k9xK9.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/0XCrRSbUg1Sdca8k9xK9.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/0XCrRSbUg1Sdca8k9xK9.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/0XCrRSbUg1Sdca8k9xK9.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/0XCrRSbUg1Sdca8k9xK9.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/0XCrRSbUg1Sdca8k9xK9.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/0XCrRSbUg1Sdca8k9xK9.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/0XCrRSbUg1Sdca8k9xK9.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/0XCrRSbUg1Sdca8k9xK9.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/0XCrRSbUg1Sdca8k9xK9.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/0XCrRSbUg1Sdca8k9xK9.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/0XCrRSbUg1Sdca8k9xK9.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/0XCrRSbUg1Sdca8k9xK9.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/0XCrRSbUg1Sdca8k9xK9.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/0XCrRSbUg1Sdca8k9xK9.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/0XCrRSbUg1Sdca8k9xK9.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/0XCrRSbUg1Sdca8k9xK9.jpg?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; You can reuse this same key for other projects. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;add-lighthouse-bot-to-your-project&quot;&gt;Add Lighthouse Bot to your project &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-lighthouse-bot-to-set-a-performance-budget/#add-lighthouse-bot-to-your-project&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Next, add Lighthouse Bot to your project by running:&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 function&quot;&gt;npm&lt;/span&gt; i --save-dev https://github.com/ebidel/lighthousebot&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;And add this bit to the &lt;code&gt;package.json&lt;/code&gt;:&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 property&quot;&gt;&quot;scripts&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 property&quot;&gt;&quot;lh&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;lighthousebot&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;add-lighthouse-bot-to-your-travis-configuration&quot;&gt;Add Lighthouse Bot to your Travis configuration &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-lighthouse-bot-to-set-a-performance-budget/#add-lighthouse-bot-to-your-travis-configuration&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For the final trick, test the performance of the app after every pull
request!&lt;/p&gt;
&lt;p&gt;In &lt;code&gt;.travis.yml&lt;/code&gt; add another step in after_success:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;after_success&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; firebase deploy &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;token $FIREBASE_TOKEN &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;non&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;interactive&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; npm run lh &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//staging.example.com&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;It will run a Lighthouse audit on the given URL, so replace
&lt;code&gt;https://staging.example.com&lt;/code&gt; with the URL of your app
(that&#39;s your-app-123.firebaseapp.com).&lt;/p&gt;
&lt;p&gt;Set your standards high and tweak the setup so you don&#39;t accept any changes to
the app that bring the performance score below 95:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; npm run lh &lt;span class=&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;perf=95 https&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//staging.example.com&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;make-a-pull-request-to-trigger-lighthouse-bot-test-on-travis&quot;&gt;Make a pull request to trigger Lighthouse Bot test on Travis &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-lighthouse-bot-to-set-a-performance-budget/#make-a-pull-request-to-trigger-lighthouse-bot-test-on-travis&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Lighthouse Bot will only test pull requests, so if you push to the main branch
now, you&#39;ll just get &amp;quot;This script can only be run on Travis PR requests&amp;quot; in your
Travis log.&lt;/p&gt;
&lt;p&gt;To trigger the Lighthouse Bot test:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Checkout a new branch&lt;/li&gt;
&lt;li&gt;Push it to GitHub&lt;/li&gt;
&lt;li&gt;Make a pull request&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Hang tight on that pull request page and wait for Lighthouse Bot to sing! 🎤&lt;/p&gt;
&lt;img alt=&quot;Passing Lighthouse scores&quot; decoding=&quot;async&quot; height=&quot;329&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 586px) 586px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/SmWHb70YqVfagXI3f03D.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/SmWHb70YqVfagXI3f03D.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/SmWHb70YqVfagXI3f03D.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/SmWHb70YqVfagXI3f03D.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/SmWHb70YqVfagXI3f03D.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/SmWHb70YqVfagXI3f03D.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/SmWHb70YqVfagXI3f03D.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/SmWHb70YqVfagXI3f03D.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/SmWHb70YqVfagXI3f03D.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/SmWHb70YqVfagXI3f03D.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/SmWHb70YqVfagXI3f03D.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/SmWHb70YqVfagXI3f03D.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/SmWHb70YqVfagXI3f03D.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/SmWHb70YqVfagXI3f03D.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/SmWHb70YqVfagXI3f03D.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/SmWHb70YqVfagXI3f03D.png?auto=format&amp;w=1172 1172w&quot; width=&quot;586&quot; /&gt;
&lt;img alt=&quot;Passing GitHub checks&quot; decoding=&quot;async&quot; height=&quot;189&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 462px) 462px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/ZrPGH5OGEY5Y4e9ntUBK.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/ZrPGH5OGEY5Y4e9ntUBK.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/ZrPGH5OGEY5Y4e9ntUBK.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/ZrPGH5OGEY5Y4e9ntUBK.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/ZrPGH5OGEY5Y4e9ntUBK.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/ZrPGH5OGEY5Y4e9ntUBK.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/ZrPGH5OGEY5Y4e9ntUBK.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/ZrPGH5OGEY5Y4e9ntUBK.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/ZrPGH5OGEY5Y4e9ntUBK.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/ZrPGH5OGEY5Y4e9ntUBK.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/ZrPGH5OGEY5Y4e9ntUBK.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/ZrPGH5OGEY5Y4e9ntUBK.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/ZrPGH5OGEY5Y4e9ntUBK.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/ZrPGH5OGEY5Y4e9ntUBK.png?auto=format&amp;w=924 924w&quot; width=&quot;462&quot; /&gt;
&lt;p&gt;The performance score is great, the app is under budget, and the check has passed!&lt;/p&gt;
&lt;h3 id=&quot;more-lighthouse-options&quot;&gt;More Lighthouse options &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/using-lighthouse-bot-to-set-a-performance-budget/#more-lighthouse-options&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Remember how Lighthouse tests 5 different categories? You can enforce scores for
any of those with Lighthouse Bot flags:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;--perf  &lt;span class=&quot;token comment&quot;&gt;# performance&lt;/span&gt;&lt;br /&gt;--pwa   &lt;span class=&quot;token comment&quot;&gt;# progressive web app score&lt;/span&gt;&lt;br /&gt;--a11y  &lt;span class=&quot;token comment&quot;&gt;# accessibility score&lt;/span&gt;&lt;br /&gt;--bp    &lt;span class=&quot;token comment&quot;&gt;# best practices score&lt;/span&gt;&lt;br /&gt;--seo   &lt;span class=&quot;token comment&quot;&gt;# SEO score&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Example:&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 function&quot;&gt;npm&lt;/span&gt; run lh -- --perf&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;93&lt;/span&gt; --seo&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt; https://staging.example.com&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This will fail the PR if the performance score drops below 93 &lt;strong&gt;or&lt;/strong&gt; the SEO
score drops below 100.&lt;/p&gt;
&lt;p&gt;You can also choose not to get Lighthouse Bot&#39;s comments with the &lt;code&gt;--no-comment&lt;/code&gt;
option.&lt;/p&gt;
</content>
    <author>
      <name>Milica Mihajlija</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>Performance budgets 101</title>
    <link href="https://web.dev/performance-budgets-101/"/>
    <updated>2018-11-05T00:00:00Z</updated>
    <id>https://web.dev/performance-budgets-101/</id>
    <content type="html" mode="escaped">&lt;p&gt;Performance is an important part of the user experience and it &lt;a href=&quot;https://wpostats.com/&quot; rel=&quot;noopener&quot;&gt;affects business metrics&lt;/a&gt;. It&#39;s tempting to think that if you are a good developer you&#39;ll end up with a performant site, but the truth is that good performance is rarely a side effect. As with most other things—to reach a goal you have to define it clearly. Start the journey by setting a &lt;strong&gt;performance budget&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;definition&quot;&gt;Definition &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-budgets-101/#definition&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A performance budget is a set of limits imposed on metrics that affect site performance. This could be the total size of a page, the time it takes to load on a mobile network, or even the number of HTTP requests that are sent. Defining a budget helps get the web performance conversation started. It serves as a point of reference for making decisions about design, technology, and adding features.&lt;/p&gt;
&lt;p&gt;Having a budget enables designers to think about the effects of high-resolution images and the number of web fonts they pick. It also helps developers compare different approaches to a problem and evaluate frameworks and libraries based on their size and &lt;a href=&quot;https://medium.com/@addyosmani/the-cost-of-javascript-in-2018-7d8950fbb5d4&quot; rel=&quot;noopener&quot;&gt;parsing cost&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;choose-metrics&quot;&gt;Choose metrics &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-budgets-101/#choose-metrics&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;quantity-based-metrics-⚖️&quot;&gt;Quantity-based metrics ⚖️ &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-budgets-101/#quantity-based-metrics-%E2%9A%96%EF%B8%8F&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;These metrics are useful in the early stages of development because they highlight the impact of including heavy images and scripts. They are also easy to communicate to both designers and developers.&lt;/p&gt;
&lt;p&gt;We&#39;ve already mentioned a few things you can include in a performance budget such as page weight and the number of HTTP requests, but you can split these up into more granular limits like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Maximum size of images&lt;/li&gt;
&lt;li&gt;Maximum number of web fonts&lt;/li&gt;
&lt;li&gt;Maximum size of scripts, including frameworks&lt;/li&gt;
&lt;li&gt;Total number of external resources, such as third-party scripts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, these numbers don&#39;t tell you much about the user experience. Two pages with the same number of requests or same weight can render differently depending on the order in which resources get requested. If a &lt;a href=&quot;https://web.dev/critical-rendering-path/&quot;&gt;critical resource&lt;/a&gt; like a hero image or a stylesheet on one of the pages is loaded late in the process, the users will wait longer to see something useful and perceive the page as slower. If on the other page the most important parts load quickly, they may not even notice if the rest of the page doesn&#39;t.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Image of progressive page rendering based on the critical-path&quot; decoding=&quot;async&quot; height=&quot;300&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 611px) 611px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/U0QhA82KFyED4r1y3tAq.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/U0QhA82KFyED4r1y3tAq.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/U0QhA82KFyED4r1y3tAq.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/U0QhA82KFyED4r1y3tAq.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/U0QhA82KFyED4r1y3tAq.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/U0QhA82KFyED4r1y3tAq.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/U0QhA82KFyED4r1y3tAq.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/U0QhA82KFyED4r1y3tAq.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/U0QhA82KFyED4r1y3tAq.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/U0QhA82KFyED4r1y3tAq.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/U0QhA82KFyED4r1y3tAq.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/U0QhA82KFyED4r1y3tAq.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/U0QhA82KFyED4r1y3tAq.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/U0QhA82KFyED4r1y3tAq.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/U0QhA82KFyED4r1y3tAq.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/U0QhA82KFyED4r1y3tAq.png?auto=format&amp;w=1222 1222w&quot; width=&quot;611&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;This is why it&#39;s important to keep track of another type of metric.&lt;/p&gt;
&lt;h3 id=&quot;milestone-timings-⏱️&quot;&gt;Milestone timings ⏱️ &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-budgets-101/#milestone-timings-%E2%8F%B1%EF%B8%8F&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Milestone timings mark events that happen during page load, such as &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Events/DOMContentLoaded&quot; rel=&quot;noopener&quot;&gt;DOMContentLoaded&lt;/a&gt; or &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Events/load&quot; rel=&quot;noopener&quot;&gt;load&lt;/a&gt; event. The most useful timings are &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/&quot;&gt;user-centric performance metrics&lt;/a&gt; that tell you something about the experience of loading a page. These metrics are available through &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/#in-the-field&quot;&gt;browser APIs&lt;/a&gt; and as part of &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt; reports.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/fcp/&quot;&gt;First Contentful Paint (FCP)&lt;/a&gt; measures when the browser displays the first bit of content from the DOM, like text or images.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/tti/&quot;&gt;Time to Interactive (TTI)&lt;/a&gt; measures how long it takes for a page to become fully interactive and reliably respond to user input. It&#39;s a very important metric to track if you expect any kind of user interaction on the page like clicking links, buttons, typing or using form elements.&lt;/p&gt;
&lt;h3 id=&quot;rule-based-metrics-💯&quot;&gt;Rule-based metrics 💯 &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-budgets-101/#rule-based-metrics-%F0%9F%92%AF&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt; and &lt;a href=&quot;https://www.webpagetest.org/&quot; rel=&quot;noopener&quot;&gt;WebPageTest&lt;/a&gt; calculate &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/performance-scoring/#perf-scoring&quot; rel=&quot;noopener&quot;&gt;performance scores&lt;/a&gt; based on general best practice rules, that you can use as guidelines. As a bonus, Lighthouse also offers you hints for simple optimizations.&lt;/p&gt;
&lt;p&gt;You&#39;ll get the best results if you keep track of a combination of quantity-based and user-centric performance metrics. Focus on asset sizes in the early phases of a project and start tracking FCP and TTI as soon as possible.&lt;/p&gt;
&lt;h2 id=&quot;establish-a-baseline&quot;&gt;Establish a baseline &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-budgets-101/#establish-a-baseline&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The only way to really know what works best for your site is to try it—research and then test your findings. Analyze the competition to see how you stack up. 🕵️&lt;/p&gt;
&lt;p&gt;If you don&#39;t have time for that, here are good default numbers to get you started:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Under &lt;strong&gt;5 s&lt;/strong&gt; Time to Interactive&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;170 KB&lt;/strong&gt; of &lt;a href=&quot;https://web.dev/critical-rendering-path/&quot;&gt;critical-path&lt;/a&gt; resources (compressed/minified)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These &lt;a href=&quot;https://infrequently.org/2017/10/can-you-afford-it-real-world-web-performance-budgets/&quot; rel=&quot;noopener&quot;&gt;numbers&lt;/a&gt; are calculated based on real-world baseline devices and &lt;strong&gt;3G network speed&lt;/strong&gt;. &lt;a href=&quot;https://www.statista.com/statistics/277125/share-of-website-traffic-coming-from-mobile-devices/&quot; rel=&quot;noopener&quot;&gt;Over half of the internet traffic&lt;/a&gt; today happens on mobile networks, so you should use 3G network speed as a starting point.&lt;/p&gt;
&lt;h3 id=&quot;examples-of-budgets&quot;&gt;Examples of budgets &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-budgets-101/#examples-of-budgets&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You should have a budget in place for different types of pages on your site since the content will vary. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Our product page must ship less than 170 KB of JavaScript on mobile&lt;/li&gt;
&lt;li&gt;Our search page must include less than 2 MB of images on desktop&lt;/li&gt;
&lt;li&gt;Our home page must load and get interactive in &amp;lt; 5 s on slow 3G on a Moto G4 phone&lt;/li&gt;
&lt;li&gt;Our blog must score &amp;gt; 80 on Lighthouse performance audits&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;add-performance-budgets-to-your-build-process&quot;&gt;Add performance budgets to your build process &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-budgets-101/#add-performance-budgets-to-your-build-process&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;img alt=&quot;Webpack, bundlesize and Lighthouse logos&quot; decoding=&quot;async&quot; height=&quot;267&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/YKJcgI9Yd8qEZM0nzPuv.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/YKJcgI9Yd8qEZM0nzPuv.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/YKJcgI9Yd8qEZM0nzPuv.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/YKJcgI9Yd8qEZM0nzPuv.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/YKJcgI9Yd8qEZM0nzPuv.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/YKJcgI9Yd8qEZM0nzPuv.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/YKJcgI9Yd8qEZM0nzPuv.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/YKJcgI9Yd8qEZM0nzPuv.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/YKJcgI9Yd8qEZM0nzPuv.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/YKJcgI9Yd8qEZM0nzPuv.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/YKJcgI9Yd8qEZM0nzPuv.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/YKJcgI9Yd8qEZM0nzPuv.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/YKJcgI9Yd8qEZM0nzPuv.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/YKJcgI9Yd8qEZM0nzPuv.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/YKJcgI9Yd8qEZM0nzPuv.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/YKJcgI9Yd8qEZM0nzPuv.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/YKJcgI9Yd8qEZM0nzPuv.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/YKJcgI9Yd8qEZM0nzPuv.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Choosing a tool for this will depend a lot on the scale of your project and resources that you can dedicate to the task. There are a few open-source tools that can help you add budgeting to your build process:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://webpack.js.org/configuration/performance/&quot; rel=&quot;noopener&quot;&gt;Webpack performance features&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/siddharthkp/bundlesize&quot; rel=&quot;noopener&quot;&gt;bundlesize&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/GoogleChrome/lighthouse-ci&quot; rel=&quot;noopener&quot;&gt;Lighthouse CI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If something goes over a defined threshold, you can either:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Optimize an existing feature or asset 🛠️&lt;/li&gt;
&lt;li&gt;Remove an existing feature or asset 🗑️&lt;/li&gt;
&lt;li&gt;Not add the new feature or asset ✋⛔&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;track-performance&quot;&gt;Track performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-budgets-101/#track-performance&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Making sure your site is fast enough means you have to keep measuring after the initial launch. Monitoring these metrics over time and &lt;a href=&quot;https://web.dev/navigation-and-resource-timing/&quot;&gt;getting data from real users&lt;/a&gt; will show you how changes in performance impact key business metrics.&lt;/p&gt;
&lt;h2 id=&quot;wrap-up&quot;&gt;Wrap up &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/performance-budgets-101/#wrap-up&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The purpose of a performance budget is to make sure you focus on performance throughout a project and setting it early will help prevent backtracking later. It should be the point of reference for helping you figure out what to include on your website. The main idea is to set goals so that you can better balance performance without harming functionality or user experience.🎯&lt;/p&gt;
&lt;p&gt;The next guide will walk you through defining your first performance budget in a few simple steps.&lt;/p&gt;
</content>
    <author>
      <name>Milica Mihajlija</name>
    </author>
  </entry>
  
  <entry>
    <title>Your first performance budget</title>
    <link href="https://web.dev/your-first-performance-budget/"/>
    <updated>2018-11-05T00:00:00Z</updated>
    <id>https://web.dev/your-first-performance-budget/</id>
    <content type="html" mode="escaped">&lt;p&gt;When you set a personal, business or family budget, you are setting a limit to your spending and making sure you stay within it. &lt;a href=&quot;https://web.dev/performance-budgets-101&quot;&gt;Performance budgets&lt;/a&gt; work in the same way, but for metrics that affect website performance.&lt;/p&gt;
&lt;p&gt;With a performance budget established and enforced you can be sure that your site will render as quickly as possible. This will provide a better experience for your visitors and positively impact business metrics.&lt;/p&gt;
&lt;p&gt;Here&#39;s how to define your first performance budget in a few simple steps.&lt;/p&gt;
&lt;h2 id=&quot;preliminary-analysis&quot;&gt;Preliminary analysis &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/your-first-performance-budget/#preliminary-analysis&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you are trying to improve the performance of an existing site, start by identifying the most important pages. For example, these could be pages that have the highest amount of user traffic or a product landing page.&lt;/p&gt;
&lt;p&gt;After you identify your key pages, it&#39;s time to analyze them. First, we&#39;ll focus on the timing milestones that best measure the user experience.&lt;/p&gt;
&lt;p&gt;Under the Audits panel in Chrome DevTools, you&#39;ll find &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt;. Run audits on each page in a &lt;a href=&quot;https://support.google.com/chrome/answer/6130773?co=GENIE.Platform%3DDesktop&amp;amp;hl=en&quot; rel=&quot;noopener&quot;&gt;Guest window&lt;/a&gt; to record these two times:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/fcp/&quot;&gt;First Contentful Paint (FCP)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/tti/&quot;&gt;Time to Interactive (TTI)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Using a Guest window gives you a clean testing environment without any Chrome extensions that could interfere with the audit. &lt;/div&gt;&lt;/aside&gt;
&lt;img alt=&quot;Lighthouse panel in Chrome DevTools&quot; decoding=&quot;async&quot; height=&quot;637&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/VUtkCadH9vjnKSzGzd0S.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/VUtkCadH9vjnKSzGzd0S.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/VUtkCadH9vjnKSzGzd0S.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/VUtkCadH9vjnKSzGzd0S.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/VUtkCadH9vjnKSzGzd0S.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/VUtkCadH9vjnKSzGzd0S.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/VUtkCadH9vjnKSzGzd0S.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/VUtkCadH9vjnKSzGzd0S.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/VUtkCadH9vjnKSzGzd0S.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/VUtkCadH9vjnKSzGzd0S.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/VUtkCadH9vjnKSzGzd0S.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/VUtkCadH9vjnKSzGzd0S.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/VUtkCadH9vjnKSzGzd0S.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/VUtkCadH9vjnKSzGzd0S.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/VUtkCadH9vjnKSzGzd0S.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/VUtkCadH9vjnKSzGzd0S.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/VUtkCadH9vjnKSzGzd0S.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/VUtkCadH9vjnKSzGzd0S.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Let&#39;s take a highly specialized search engine, Doggos.com, as an example. Doggos.com aims to index all dog-related things on the internet, and its most important pages are the home and results pages. Here are the FCP and TTI numbers measured for the site on desktop and mobile.&lt;/p&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Desktop&lt;/th&gt;
        &lt;th&gt;FCP&lt;/th&gt;
        &lt;th&gt;TTI&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;Homepage&lt;/td&gt;
        &lt;td&gt;1,680 ms&lt;/td&gt;
        &lt;td&gt;5,550 ms&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Results page&lt;/td&gt;
        &lt;td&gt;2,060 ms&lt;/td&gt;
        &lt;td&gt;6,690 ms&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
    &lt;caption&gt;Desktop analysis of Doggos.com&lt;/caption&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Mobile&lt;/th&gt;
        &lt;th&gt;FCP&lt;/th&gt;
        &lt;th&gt;TTI&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;Homepage&lt;/td&gt;
        &lt;td&gt;1,800 ms&lt;/td&gt;
        &lt;td&gt;6,150 ms&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Results page&lt;/td&gt;
        &lt;td&gt;1,100 ms&lt;/td&gt;
        &lt;td&gt;7,870 ms&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
    &lt;caption&gt;Mobile analysis of Doggos.com&lt;/caption&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;h2 id=&quot;competitive-analysis&quot;&gt;Competitive analysis &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/your-first-performance-budget/#competitive-analysis&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once you&#39;ve analyzed your own site, it&#39;s time to analyze your competitors&#39; sites. Comparing results from websites similar to yours is a great way to figure out a performance budget. Whether you are working on an established project or starting from scratch, this is an important step. You get competitive advantage when you are faster than your competitors.&lt;/p&gt;
&lt;p&gt;If you are not sure which sites to look at, here are a few tools to try:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Google search&#39;s &amp;quot;related:&amp;quot; keyword&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.alexa.com/find-similar-sites&quot; rel=&quot;noopener&quot;&gt;Alexa&#39;s similar sites&lt;/a&gt; feature&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.similarweb.com/&quot; rel=&quot;noopener&quot;&gt;SimilarWeb&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;img alt=&quot;Screenshot of Google search with the related keyword&quot; decoding=&quot;async&quot; height=&quot;336&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 775px) 775px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/EzpGvSgVJYC2y3rsnHRk.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/EzpGvSgVJYC2y3rsnHRk.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/EzpGvSgVJYC2y3rsnHRk.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/EzpGvSgVJYC2y3rsnHRk.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/EzpGvSgVJYC2y3rsnHRk.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/EzpGvSgVJYC2y3rsnHRk.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/EzpGvSgVJYC2y3rsnHRk.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/EzpGvSgVJYC2y3rsnHRk.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/EzpGvSgVJYC2y3rsnHRk.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/EzpGvSgVJYC2y3rsnHRk.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/EzpGvSgVJYC2y3rsnHRk.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/EzpGvSgVJYC2y3rsnHRk.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/EzpGvSgVJYC2y3rsnHRk.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/EzpGvSgVJYC2y3rsnHRk.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/EzpGvSgVJYC2y3rsnHRk.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/EzpGvSgVJYC2y3rsnHRk.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/EzpGvSgVJYC2y3rsnHRk.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/EzpGvSgVJYC2y3rsnHRk.png?auto=format&amp;w=1550 1550w&quot; width=&quot;775&quot; /&gt;
&lt;p&gt;For a realistic picture, try to &lt;strong&gt;find 10 or so competitors&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;budget-for-timing-milestones&quot;&gt;Budget for timing milestones &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/your-first-performance-budget/#budget-for-timing-milestones&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Our niche search engine in this example has a handful of competitors and we&#39;ll focus on optimizing the homepage for mobile devices. Over &lt;a href=&quot;https://www.statista.com/statistics/277125/share-of-website-traffic-coming-from-mobile-devices/&quot; rel=&quot;noopener&quot;&gt;half of the internet traffic&lt;/a&gt; today happens on mobile networks and using mobile numbers as default will benefit not only your mobile users, but your desktop users as well.&lt;/p&gt;
&lt;p&gt;Create a chart with FCP and TTI times for all the similar websites and highlight the fastest in the bunch. A chart like this one gives you a clearer picture of how your website is doing compared to the competition.&lt;/p&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Site/Homepage&lt;/th&gt;
        &lt;th&gt;FCP&lt;/th&gt;
        &lt;th&gt;TTI&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;goggles.com&lt;/td&gt;
        &lt;td bgcolor=&quot;yellow&quot;&gt;&lt;strong&gt;880 ms&lt;/strong&gt;&lt;/td&gt;
        &lt;td bgcolor=&quot;yellow&quot;&gt;&lt;strong&gt;3,150 ms&lt;strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Doggos.com&lt;/td&gt;
        &lt;td&gt;1,800 ms&lt;/td&gt;
        &lt;td&gt;6,500 ms&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;quackquackgo.com&lt;/td&gt;
        &lt;td&gt;2,680 ms&lt;/td&gt;
        &lt;td&gt;4,740 ms&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;ding.xyz&lt;/td&gt;
        &lt;td&gt;2,420 ms&lt;/td&gt;
        &lt;td&gt;7,040 ms&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
    &lt;caption&gt;Competitive analysis of Doggos.com on 3G network&lt;/caption&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Doggo at a computer&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/Mfzr0dmMxHij9KrJraHD.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/Mfzr0dmMxHij9KrJraHD.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/Mfzr0dmMxHij9KrJraHD.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/Mfzr0dmMxHij9KrJraHD.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/Mfzr0dmMxHij9KrJraHD.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/Mfzr0dmMxHij9KrJraHD.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/Mfzr0dmMxHij9KrJraHD.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/Mfzr0dmMxHij9KrJraHD.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/Mfzr0dmMxHij9KrJraHD.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/Mfzr0dmMxHij9KrJraHD.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/Mfzr0dmMxHij9KrJraHD.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/Mfzr0dmMxHij9KrJraHD.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/Mfzr0dmMxHij9KrJraHD.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/Mfzr0dmMxHij9KrJraHD.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/Mfzr0dmMxHij9KrJraHD.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/Mfzr0dmMxHij9KrJraHD.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/Mfzr0dmMxHij9KrJraHD.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/Mfzr0dmMxHij9KrJraHD.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Doggos.com seems to be doing okay on the FCP metric but seriously lagging behind in TTI
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;There&#39;s room for improvement and a good guideline for that is the &lt;a href=&quot;https://www.smashingmagazine.com/2015/09/why-performance-matters-the-perception-of-time/#the-need-for-performance-optimization-the-20-rule&quot; rel=&quot;noopener&quot;&gt;20% rule&lt;/a&gt;. Research states that users recognize a difference in response times when it&#39;s greater than 20%. This means that if you want to be noticeably better than the best comparable site, you have to &lt;strong&gt;be at least 20% faster&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Measure&lt;/th&gt;
        &lt;th&gt;Current time&lt;/th&gt;
        &lt;th&gt;Budget (20% faster than competition)&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;FCP&lt;/td&gt;
        &lt;td&gt;1,800 ms&lt;/td&gt;
        &lt;td&gt;704 ms&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;TTI&lt;/td&gt;
        &lt;td&gt;6,500 ms&lt;/td&gt;
        &lt;td&gt;2,520 ms&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
    &lt;caption&gt;Performance budget that would get Doggos.com ahead of the competition&lt;/caption&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;If you are trying to optimize an existing site that goal may seem impossible to reach. This is not a sign for you to give up. Start with small steps and set a budget at 20% faster than your current speed. Keep optimizing from there.&lt;/p&gt;
&lt;p&gt;For Doggos.com, a revised budget could look like this.&lt;/p&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Measure&lt;/th&gt;
        &lt;th&gt;Current time&lt;/th&gt;
        &lt;th&gt;Initial budget
    (20% faster than the current time)&lt;/th&gt;
        &lt;th&gt;Long-term goal
    (20% faster than competition)&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;FCP&lt;/td&gt;
        &lt;td&gt;1,800 ms&lt;/td&gt;
        &lt;td&gt;1,440 ms&lt;/td&gt;
        &lt;td&gt;704 ms&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;TTI&lt;/td&gt;
        &lt;td&gt;6,500 ms&lt;/td&gt;
        &lt;td&gt;5,200 ms&lt;/td&gt;
        &lt;td&gt;2,520 ms&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
    &lt;caption&gt;Revised Doggos.com performance budget&lt;/caption&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;h2 id=&quot;combine-different-metrics&quot;&gt;Combine different metrics &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/your-first-performance-budget/#combine-different-metrics&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A solid performance budget combines different types of metrics. We&#39;ve already defined the budget for milestone timings and now we&#39;ll add two more to the mix:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;quantity-based metrics&lt;/li&gt;
&lt;li&gt;rule-based metrics&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;budget-for-quantity-based-metrics&quot;&gt;Budget for quantity-based metrics &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/your-first-performance-budget/#budget-for-quantity-based-metrics&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Whatever total page weight number you come up with, try to &lt;strong&gt;deliver&lt;/strong&gt; under 170 KB of &lt;strong&gt;&lt;a href=&quot;https://web.dev/critical-rendering-path/&quot;&gt;critical-path&lt;/a&gt; resources&lt;/strong&gt; (compressed/minified). This guarantees your website will be fast even on &lt;a href=&quot;https://infrequently.org/2017/10/can-you-afford-it-real-world-web-performance-budgets/&quot; rel=&quot;noopener&quot;&gt;inexpensive devices and slow 3G&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can have a bigger budget for the desktop experience, but don&#39;t go wild. The median page weight on both desktop and mobile is over 1MB according to the &lt;a href=&quot;https://httparchive.org/reports/page-weight&quot; rel=&quot;noopener&quot;&gt;HTTP Archive&lt;/a&gt; data for the last year. To get a performant website you have to aim well below these median numbers.&lt;/p&gt;
&lt;p&gt;Here are a few examples based on TTI budgets:&lt;/p&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Network&lt;/th&gt;
        &lt;th&gt;Device&lt;/th&gt;
        &lt;th&gt;JS&lt;/th&gt;
        &lt;th&gt;Images&lt;/th&gt;
        &lt;th&gt;CSS&lt;/th&gt;
        &lt;th&gt;HTML&lt;/th&gt;
        &lt;th&gt;Fonts&lt;/th&gt;
        &lt;th&gt;Total&lt;/th&gt;
        &lt;th&gt;Time to Interactive budget&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;Slow 3G&lt;/td&gt;
        &lt;td&gt;Moto G4&lt;/td&gt;
        &lt;td&gt;100&lt;/td&gt;
        &lt;td&gt;30&lt;/td&gt;
        &lt;td&gt;10&lt;/td&gt;
        &lt;td&gt;10&lt;/td&gt;
        &lt;td&gt;20&lt;/td&gt;
        &lt;td&gt;~170 KB&lt;/td&gt;
        &lt;td&gt;5s&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Slow 4G&lt;/td&gt;
        &lt;td&gt;Moto G4&lt;/td&gt;
        &lt;td&gt;200&lt;/td&gt;
        &lt;td&gt;50&lt;/td&gt;
        &lt;td&gt;35&lt;/td&gt;
        &lt;td&gt;30&lt;/td&gt;
        &lt;td&gt;30&lt;/td&gt;
        &lt;td&gt;~345 KB&lt;/td&gt;
        &lt;td&gt;3s&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;WiFi&lt;/td&gt;
        &lt;td&gt;Desktop&lt;/td&gt;
        &lt;td&gt;300&lt;/td&gt;
        &lt;td&gt;250&lt;/td&gt;
        &lt;td&gt;50&lt;/td&gt;
        &lt;td&gt;50&lt;/td&gt;
        &lt;td&gt;100&lt;/td&gt;
        &lt;td&gt;~750 KB&lt;/td&gt;
        &lt;td&gt;2s&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The recommended sizes are for the critical-path resources. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Defining a budget based on quantity metrics is a tricky business. An e-commerce website with loads of product photos is very different from a news portal which is mostly text. If you have ads or analytics on your site, that increases the amount of JavaScript you&#39;re shipping.&lt;/p&gt;
&lt;p&gt;Use the table above as a starting point and adjust based on the type of content you are working with. Define what your pages will include, review your research and take an educated guess for individual asset sizes. For example, if you are building a website with a lot of images, put stricter limits to JS size.&lt;/p&gt;
&lt;p&gt;Once you have a working website, check how you are doing on user-centric performance metrics and adjust your budget.&lt;/p&gt;
&lt;h3 id=&quot;budget-for-rule-based-metrics&quot;&gt;Budget for rule-based metrics &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/your-first-performance-budget/#budget-for-rule-based-metrics&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Very effective rule-based metrics are &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt; scores. Lighthouse grades your app in 5 categories and one of those is performance. Performance scores are calculated based on &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/performance-scoring/#perf-audits&quot; rel=&quot;noopener&quot;&gt;5 different metrics&lt;/a&gt;, including First Contentful Paint and Time to Interactive.&lt;/p&gt;
&lt;p&gt;When you try to build a great site, &lt;strong&gt;set Lighthouse performance score budget to at least 85 (out of 100)&lt;/strong&gt;. Use &lt;a href=&quot;https://github.com/ebidel/lighthouse-ci&quot; rel=&quot;noopener&quot;&gt;Lighthouse CI&lt;/a&gt; to enforce it on pull-requests.&lt;/p&gt;
&lt;h2 id=&quot;prioritize&quot;&gt;Prioritize &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/your-first-performance-budget/#prioritize&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Ask yourself what level of interaction you expect on your site. If it&#39;s a news website, users&#39; primary goal is to read content so you should focus on rendering quickly and keeping FCP low. Doggos.com visitors want to click on relevant links as soon as possible, so the top priority is low TTI.&lt;/p&gt;
&lt;p&gt;Find out exactly what part of your audience browses on desktop vs. on mobile devices and prioritize accordingly. One way to figure this out is to check what your audience is doing on competitors&#39; websites, through the &lt;a href=&quot;https://developer.chrome.com/blog/chrome-ux-report-looker-studio-dashboard/&quot; rel=&quot;noopener&quot;&gt;Chrome User Experience report&lt;/a&gt; dashboard.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Device distribution data from Chrome User Experience report&quot; decoding=&quot;async&quot; height=&quot;530&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/ycZwOrFNzjdjquriM9rJ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/ycZwOrFNzjdjquriM9rJ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/ycZwOrFNzjdjquriM9rJ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/ycZwOrFNzjdjquriM9rJ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/ycZwOrFNzjdjquriM9rJ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/ycZwOrFNzjdjquriM9rJ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/ycZwOrFNzjdjquriM9rJ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/ycZwOrFNzjdjquriM9rJ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/ycZwOrFNzjdjquriM9rJ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/ycZwOrFNzjdjquriM9rJ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/ycZwOrFNzjdjquriM9rJ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/ycZwOrFNzjdjquriM9rJ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/ycZwOrFNzjdjquriM9rJ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/ycZwOrFNzjdjquriM9rJ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/ycZwOrFNzjdjquriM9rJ.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/ycZwOrFNzjdjquriM9rJ.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/ycZwOrFNzjdjquriM9rJ.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/ycZwOrFNzjdjquriM9rJ.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Device distribution data from Chrome User Experience report
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next steps &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/your-first-performance-budget/#next-steps&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Make sure your performance budget is enforced throughout the project and incorporate it into your build process.&lt;/p&gt;
</content>
    <author>
      <name>Milica Mihajlija</name>
    </author>
  </entry>
</feed>
