<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://web.dev/</id>
  <title>Katie Hempenius on web.dev</title>
  <updated>2026-04-15T23:21:06Z</updated>
  <author>
    <name>Katie Hempenius</name>
  </author>
  <link href="https://web.dev/authors/katiehempenius/feed.xml" rel="self"/>
  <link href="https://web.dev/"/>
  <icon>https://web-dev.imgix.net/image/admin/fZo7BJGec2MNRt6cWpeh.jpg?auto=format</icon>
  <logo>https://web.dev/images/shared/rss-banner.png</logo>
  <subtitle>Our latest news, updates, and stories by Katie Hempenius.</subtitle>
  
  
  <entry>
    <title>Best practices for using third-party embeds</title>
    <link href="https://web.dev/embed-best-practices/"/>
    <updated>2021-10-05T00:00:00Z</updated>
    <id>https://web.dev/embed-best-practices/</id>
    <content type="html" mode="escaped">&lt;p&gt;Many sites use third-party embeds to create an engaging user experience by delegating some sections of a web page to another content provider. The most common examples of third-party content embeds are video players, social-media feeds, maps, and advertisements.&lt;/p&gt;
&lt;p&gt;Third-party content can impact the performance of a page in many ways. It can be render-blocking, contend with other critical resources for network and bandwidth, or affect the Core Web Vitals metrics. Third-party embeds may also cause layout shifts as they load. This article discusses performance best practices that you can use when loading third-party embeds, efficient loading techniques, and the Layout Shift Terminator tool that helps reduce layout shifts for popular embeds.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; It&#39;s best to use the techniques described in this post to load only offscreen or non-primary page content. This ensures that all the critical content gets indexed by &lt;a href=&quot;https://developers.google.com/search/docs/advanced/javascript/lazy-loading&quot;&gt;search engines&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;what-is-an-embed&quot;&gt;What is an embed &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embed-best-practices/#what-is-an-embed&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A third-party embed is any content displayed on your site that is:&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;img alt=&quot;Multiple offscreen embeds are shown, which could be lazy-loaded&quot; decoding=&quot;async&quot; height=&quot;450&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/QfPvNJo9yN6IcxvWqYWI.jpeg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/QfPvNJo9yN6IcxvWqYWI.jpeg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/QfPvNJo9yN6IcxvWqYWI.jpeg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/QfPvNJo9yN6IcxvWqYWI.jpeg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/QfPvNJo9yN6IcxvWqYWI.jpeg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/QfPvNJo9yN6IcxvWqYWI.jpeg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/QfPvNJo9yN6IcxvWqYWI.jpeg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/QfPvNJo9yN6IcxvWqYWI.jpeg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/QfPvNJo9yN6IcxvWqYWI.jpeg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/QfPvNJo9yN6IcxvWqYWI.jpeg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/QfPvNJo9yN6IcxvWqYWI.jpeg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/QfPvNJo9yN6IcxvWqYWI.jpeg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/QfPvNJo9yN6IcxvWqYWI.jpeg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/QfPvNJo9yN6IcxvWqYWI.jpeg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/QfPvNJo9yN6IcxvWqYWI.jpeg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/QfPvNJo9yN6IcxvWqYWI.jpeg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/QfPvNJo9yN6IcxvWqYWI.jpeg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/QfPvNJo9yN6IcxvWqYWI.jpeg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Embeds are frequently used in the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Websites related to sports, news, entertainment, and fashion use videos to augment textual content.&lt;/li&gt;
&lt;li&gt;Organizations with active Twitter or social media accounts embed feeds from these accounts to their web pages to engage and reach out to more people.&lt;/li&gt;
&lt;li&gt;Restaurant, park, and event venue pages often embed maps.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Third-party embeds are typically loaded in &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/iframe&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;&lt;/a&gt; elements on the page. Third-party providers offer HTML snippets often consisting of an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; that pulls in a page composed of markup, scripts, and stylesheets. Some providers also use a script snippet that dynamically injects an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; to pull other content in. This can make the third-party embeds heavy and affect the performance of the page by delaying its first-party content.&lt;/p&gt;
&lt;h2 id=&quot;performance-impact-of-third-party-embeds&quot;&gt;Performance impact of third-party embeds &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embed-best-practices/#performance-impact-of-third-party-embeds&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Many popular embeds include over 100 KB of JavaScript, sometimes even going up to 2 MB. They take more time to load and keep the main thread busy when executing. Performance monitoring tools such as &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; help to &lt;a href=&quot;https://web.dev/identify-slow-third-party-javascript/&quot;&gt;measure the impact of third-party embeds on performance&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/third-party-summary/&quot; rel=&quot;noopener&quot;&gt;Reduce the impact of third-party code&lt;/a&gt; Lighthouse audit shows the list of third-party providers a page uses, with size and main-thread blocking time. The audit is available through Chrome DevTools under the Lighthouse tab.&lt;/p&gt;
&lt;p&gt;It is a good practice to periodically audit the performance impact of your embeds and third-party code because embed source code may change. You can use this opportunity to remove any redundant code.&lt;/p&gt;
&lt;img alt=&quot;Reduce the impact of third-party code&quot; decoding=&quot;async&quot; height=&quot;738&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/WektrXQsgQPMWy2hxQ4E.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/WektrXQsgQPMWy2hxQ4E.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/WektrXQsgQPMWy2hxQ4E.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/WektrXQsgQPMWy2hxQ4E.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/WektrXQsgQPMWy2hxQ4E.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/WektrXQsgQPMWy2hxQ4E.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/WektrXQsgQPMWy2hxQ4E.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/WektrXQsgQPMWy2hxQ4E.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/WektrXQsgQPMWy2hxQ4E.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/WektrXQsgQPMWy2hxQ4E.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/WektrXQsgQPMWy2hxQ4E.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/WektrXQsgQPMWy2hxQ4E.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/WektrXQsgQPMWy2hxQ4E.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/WektrXQsgQPMWy2hxQ4E.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/WektrXQsgQPMWy2hxQ4E.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/WektrXQsgQPMWy2hxQ4E.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/WektrXQsgQPMWy2hxQ4E.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/WektrXQsgQPMWy2hxQ4E.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h2 id=&quot;loading-best-practices&quot;&gt;Loading best practices &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embed-best-practices/#loading-best-practices&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Third-party embeds can negatively impact performance, but they also offer important functionalities. To efficiently use third-party embeds and reduce their performance impact, follow the guidelines below.&lt;/p&gt;
&lt;h3 id=&quot;script-ordering&quot;&gt;Script ordering &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embed-best-practices/#script-ordering&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In a well-designed page, the key first-party content will be the focus of the page, while the third-party embeds will occupy side-bars or appear after the first-party content.&lt;/p&gt;
&lt;p&gt;For the best user experience, the main content should load quickly and before any other supporting content. For example, the news text on a news page should load before embeds for a Twitter feed or advertisements.&lt;/p&gt;
&lt;p&gt;Requests for third-party embeds can get in the way of loading first-party content, so the position of a third-party script tag is important. Scripts can affect the loading sequence because the DOM construction pauses while scripts are executed. Place third-party script tags after the key first-party tags and &lt;a href=&quot;https://web.dev/efficiently-load-third-party-javascript/#use-async-or-defer&quot;&gt;use &lt;code&gt;async&lt;/code&gt; or &lt;code&gt;defer&lt;/code&gt;&lt;/a&gt; attributes to load them asynchronously.&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;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Order of Things&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;title&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;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;screen&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;/assets/application.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;   &lt;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;index.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;   &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;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://example.com/3p-library.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;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;h3 id=&quot;lazy-loading&quot;&gt;Lazy-loading &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embed-best-practices/#lazy-loading&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Since third-party content usually comes after the primary content, it may not be visible in the viewport when the page loads. In that case, downloading third-party resources may be deferred until the user scrolls down to that part of the page. This not only helps optimize the initial page load but also reduces the download costs for users on fixed data plans and slow network connections.&lt;/p&gt;
&lt;p&gt;Delaying the download of content until it is actually needed is called &lt;a href=&quot;https://web.dev/lazy-loading-best-practices/&quot;&gt;lazy-loading&lt;/a&gt;. Depending on the requirements and the type of embed, you can use different lazy-loading techniques explained below.&lt;/p&gt;
&lt;h4 id=&quot;native-lazy-loading-for-lessiframegreater&quot;&gt;Native lazy-loading for &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embed-best-practices/#native-lazy-loading-for-lessiframegreater&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;For third-party embeds loaded through &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; elements, you can use browser-level lazy-loading to defer loading offscreen iframes until users scroll near them. The &lt;a href=&quot;https://web.dev/iframe-lazy-loading/&quot;&gt;loading attribute for &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; is available in Chrome 77&lt;/a&gt; and above and has &lt;a href=&quot;https://caniuse.com/loading-lazy-attr&quot; rel=&quot;noopener&quot;&gt;also been introduced&lt;/a&gt; to other Chromium-based browsers.&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;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://example.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;       &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;       &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;600&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;       &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;400&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;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;The loading attribute supports the following values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;lazy&lt;/code&gt;: Indicates that the browser should defer loading the iframe. The browser will load the iframe when it is nearing the viewport. Use if the iframe is a good candidate for lazy-loading.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;eager&lt;/code&gt;: Loads the iframe immediately. Use if the iframe is not a good candidate for lazy-loading. If the &lt;code&gt;loading&lt;/code&gt; attribute has not been specified, this is the default behavior—except in &lt;a href=&quot;https://support.google.com/chrome/answer/2392284?hl=en&amp;amp;co=GENIE.Platform%3DAndroid&quot; rel=&quot;noopener&quot;&gt;Lite mode&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;auto&lt;/code&gt;: The browser determines whether to lazy-load this frame.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Browsers that don’t support the &lt;code&gt;loading&lt;/code&gt; attribute ignore it, so you can apply native lazy-loading as a progressive enhancement. Browsers that support the attribute may have different implementations for the &lt;a href=&quot;https://web.dev/browser-level-image-lazy-loading/#distance-from-viewport-thresholds&quot;&gt;distance-from-viewport&lt;/a&gt; threshold (the distance at which the iframe starts loading).&lt;/p&gt;
&lt;p&gt;Following are some ways in which you can lazy load iframes for different types of embeds.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;YouTube videos: To lazy-load a YouTube video player iframe,  include the &lt;code&gt;loading&lt;/code&gt; attribute to the embed code provided by YouTube. Lazy loading the YouTube embed can save approximately 500 KB on the initial page load.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token 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;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/aKydtOXW8mI&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;   &lt;span class=&quot;token attr-name&quot;&gt;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;br /&gt;   &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;   &lt;span class=&quot;token attr-name&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;YouTube video player&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;   &lt;span class=&quot;token attr-name&quot;&gt;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;br /&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; clipboard-write;&lt;br /&gt;            encrypted-media; gyroscope; picture-in-picture&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;   &lt;span class=&quot;token attr-name&quot;&gt;allowfullscreen&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;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;ul&gt;
&lt;li&gt;Google Maps: To lazy-load a Google Map iframe, include the &lt;code&gt;loading&lt;/code&gt; attribute in the code for the iframe embed generated by the &lt;a href=&quot;https://developers.google.com/maps/documentation/embed/get-started&quot; rel=&quot;noopener&quot;&gt;Google Maps Embed API&lt;/a&gt;. Following is an example of the code with a placeholder for the Google Cloud API key.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token 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;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.google.com/maps/embed/v1/place?key=API_KEY&amp;amp;q=PLACE_ID&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;   &lt;span class=&quot;token attr-name&quot;&gt;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;600&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;450&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;   &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;   &lt;span class=&quot;token attr-name&quot;&gt;allowfullscreen&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;   &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;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;h4 id=&quot;lazysizes-library&quot;&gt;lazysizes library &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embed-best-practices/#lazysizes-library&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Because browsers use an embed’s distance-from-viewport, in addition to signals like &lt;a href=&quot;https://googlechrome.github.io/samples/network-information/&quot; rel=&quot;noopener&quot;&gt;effective connection type&lt;/a&gt; and Lite-mode, to decide when an iframe should be loaded, native lazy-loading can be inconsistent. If you need better control on the distance thresholds or you want to provide a consistent lazy-loading experience across browsers, you can use the &lt;a href=&quot;https://github.com/aFarkas/lazysizes&quot; rel=&quot;noopener&quot;&gt;lazysizes&lt;/a&gt; library.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/aFarkas/lazysizes&quot; rel=&quot;noopener&quot;&gt;lazysizes&lt;/a&gt; is a fast, SEO-friendly lazy loader for both images and iframes. Once you have downloaded the component, it can be used with an iframe for a YouTube embed as follows.&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;lazysizes.min.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;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;iframe&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/aKydtOXW8mI&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;   &lt;span class=&quot;token attr-name&quot;&gt;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;br /&gt;   &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazyload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;   &lt;span class=&quot;token attr-name&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;YouTube video player&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;   &lt;span class=&quot;token attr-name&quot;&gt;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;br /&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; clipboard-write;&lt;br /&gt;        encrypted-media; gyroscope; picture-in-picture&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;   &lt;span class=&quot;token attr-name&quot;&gt;allowfullscreen&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;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;Similarly, lazysizes may be used with iframes for other third-party embeds.&lt;/p&gt;
&lt;p&gt;Note that lazysizes 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 when an element becomes visible.&lt;/p&gt;
&lt;h4 id=&quot;using-data-lazy-in-facebook&quot;&gt;Using data-lazy in Facebook &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embed-best-practices/#using-data-lazy-in-facebook&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Facebook provides different types of &lt;a href=&quot;https://developers.facebook.com/docs/plugins&quot; rel=&quot;noopener&quot;&gt;social plugins&lt;/a&gt; that can be embedded. This includes posts, comments, videos, and the most popular &lt;em&gt;Like&lt;/em&gt; button. All plugins include a setting for &lt;code&gt;data-lazy&lt;/code&gt;. Setting it to &lt;code&gt;true&lt;/code&gt; ensures that the plugin will use the browser&#39;s lazy-loading mechanism by setting the &lt;code&gt;loading=&amp;quot;lazy&amp;quot;&lt;/code&gt; iframe attribute.&lt;/p&gt;
&lt;h4 id=&quot;lazy-loading-instagram-feeds&quot;&gt;Lazy-loading Instagram feeds &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embed-best-practices/#lazy-loading-instagram-feeds&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Instagram provides a block of markup and a script as part of the embed. The script injects an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; into the page. Lazy-loading this &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; can improve performance as the embed can be over 100 KB gzipped in size. Many Instagram plugins for WordPress sites like &lt;a href=&quot;https://wordpress.org/plugins/instagram-widget-by-wpzoom/&quot; rel=&quot;noopener&quot;&gt;WPZoom&lt;/a&gt; and &lt;a href=&quot;https://www.mapledesign.co.uk/tech-blog/elfsight-instagram-feed-performance/&quot; rel=&quot;noopener&quot;&gt;Elfsight&lt;/a&gt; provide the lazy-loading option.&lt;/p&gt;
&lt;h3 id=&quot;replace-embeds-with-facades&quot;&gt;Replace embeds with facades &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embed-best-practices/#replace-embeds-with-facades&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;While interactive embeds add value to the page, many users may not interact with them. For example, not every user browsing a restaurant page will click, expand, scroll, and navigate the map embed. Similarly, not every user to a telecom service providers page will interact with the chatbot. In these cases, you can avoid loading or lazy-loading the embed altogether by displaying a facade in its place.&lt;/p&gt;
&lt;div class=&quot;switcher&quot;&gt;
  &lt;figure&gt;
    &lt;figcaption&gt;
      A map embed with a zoom in and out feature.
    &lt;/figcaption&gt;
    &lt;img alt=&quot;A map embed&quot; decoding=&quot;async&quot; height=&quot;725&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/Cn0x7aeqCw7M0X5b4L1P.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/Cn0x7aeqCw7M0X5b4L1P.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/Cn0x7aeqCw7M0X5b4L1P.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/Cn0x7aeqCw7M0X5b4L1P.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/Cn0x7aeqCw7M0X5b4L1P.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/Cn0x7aeqCw7M0X5b4L1P.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/Cn0x7aeqCw7M0X5b4L1P.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/Cn0x7aeqCw7M0X5b4L1P.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/Cn0x7aeqCw7M0X5b4L1P.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/Cn0x7aeqCw7M0X5b4L1P.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/Cn0x7aeqCw7M0X5b4L1P.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/Cn0x7aeqCw7M0X5b4L1P.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/Cn0x7aeqCw7M0X5b4L1P.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/Cn0x7aeqCw7M0X5b4L1P.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/Cn0x7aeqCw7M0X5b4L1P.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/Cn0x7aeqCw7M0X5b4L1P.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/Cn0x7aeqCw7M0X5b4L1P.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/Cn0x7aeqCw7M0X5b4L1P.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;/figure&gt;
  &lt;figure&gt;
    &lt;figcaption&gt;
      A map facade that is an image.
    &lt;/figcaption&gt;
    &lt;img alt=&quot;A map facade&quot; decoding=&quot;async&quot; height=&quot;541&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/f8z9MfvgIFiBkCLA1Qud.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/f8z9MfvgIFiBkCLA1Qud.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/f8z9MfvgIFiBkCLA1Qud.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/f8z9MfvgIFiBkCLA1Qud.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/f8z9MfvgIFiBkCLA1Qud.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/f8z9MfvgIFiBkCLA1Qud.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/f8z9MfvgIFiBkCLA1Qud.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/f8z9MfvgIFiBkCLA1Qud.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/f8z9MfvgIFiBkCLA1Qud.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/f8z9MfvgIFiBkCLA1Qud.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/f8z9MfvgIFiBkCLA1Qud.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/f8z9MfvgIFiBkCLA1Qud.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/f8z9MfvgIFiBkCLA1Qud.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/f8z9MfvgIFiBkCLA1Qud.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/f8z9MfvgIFiBkCLA1Qud.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/f8z9MfvgIFiBkCLA1Qud.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/f8z9MfvgIFiBkCLA1Qud.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/f8z9MfvgIFiBkCLA1Qud.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;A &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/third-party-facades/&quot; rel=&quot;noopener&quot;&gt;facade&lt;/a&gt; is a static element that looks similar to the actual embedded third-party but is not functional and, therefore, much less taxing on the page load. Following are a few strategies to load such embeds optimally while still providing some value to the user.&lt;/p&gt;
&lt;h4 id=&quot;use-static-images-as-facades&quot;&gt;Use static images as facades &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embed-best-practices/#use-static-images-as-facades&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Static images can be used instead of map embeds where you might not need to make the map interactive. You can zoom in on the area of interest on the map, capture an image, and use this instead of the interactive map embed. You can also use DevTools &lt;strong&gt;Capture node screenshot&lt;/strong&gt; functionality to capture a screenshot of the embedded &lt;code&gt;iframe&lt;/code&gt; element, as shown below.&lt;/p&gt;
&lt;img alt=&quot;Capture node screenshot&quot; decoding=&quot;async&quot; height=&quot;500&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 400px) 400px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EJvMAEUmF3QNUDGBgfNR.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EJvMAEUmF3QNUDGBgfNR.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EJvMAEUmF3QNUDGBgfNR.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EJvMAEUmF3QNUDGBgfNR.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EJvMAEUmF3QNUDGBgfNR.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EJvMAEUmF3QNUDGBgfNR.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EJvMAEUmF3QNUDGBgfNR.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EJvMAEUmF3QNUDGBgfNR.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EJvMAEUmF3QNUDGBgfNR.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EJvMAEUmF3QNUDGBgfNR.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EJvMAEUmF3QNUDGBgfNR.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EJvMAEUmF3QNUDGBgfNR.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EJvMAEUmF3QNUDGBgfNR.png?auto=format&amp;w=800 800w&quot; width=&quot;400&quot; /&gt;
&lt;p&gt;DevTools captures the image as a  &lt;code&gt;png&lt;/code&gt;, but you can also consider converting it to &lt;code&gt;&lt;a href=&quot;https://web.dev/serve-images-webp/&quot;&gt;WebP format for better performance&lt;/a&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&quot;use-dynamic-images-as-facades&quot;&gt;Use dynamic images as facades &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embed-best-practices/#use-dynamic-images-as-facades&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;This technique allows you to generate images corresponding to an interactive embed at run time. Following are some of the tools that allow you to generate static versions of embeds on your pages.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Maps Static API&lt;/strong&gt;: The Google &lt;a href=&quot;https://developers.google.com/maps/documentation/maps-static/overview&quot; rel=&quot;noopener&quot;&gt;Maps Static API&lt;/a&gt; service generates a map based on the URL parameters included in a standard HTTP request and returns the map as an image you can display on your web page. The URL needs to include the Google Maps API key and must be placed in the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag on the page as the &lt;code&gt;src&lt;/code&gt; attribute.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://staticmapmaker.com/google/&quot; rel=&quot;noopener&quot;&gt;Static map maker&lt;/a&gt; tool helps to configure the parameters required for the URL and gives you the code for the image element in real-time.&lt;/p&gt;
&lt;p&gt;The following snippet shows code for an image with the source set to a Maps Static API URL. It has been included in a link tag that ensures that the actual map can be accessed by clicking on the image. (Note: API key attribute is not included in the 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;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://www.google.com/maps/place/Albany,+NY/&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://maps.googleapis.com/maps/api/staticmap?center=Albany,+NY&amp;amp;zoom=13&amp;amp;scale=1&amp;amp;size=600x300&amp;amp;maptype=roadmap&amp;amp;format=png&amp;amp;visual_refresh=true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Google Map of Albany, NY&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;a&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;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Twitter screenshots&lt;/strong&gt;: Similar to map screenshots, this concept allows you to dynamically embed a Twitter screenshot instead of the live feed. &lt;a href=&quot;https://tweetpik.com/&quot; rel=&quot;noopener&quot;&gt;Tweetpik&lt;/a&gt; is one of the tools that can be used to take screenshots of tweets. Tweetpik API accepts the URL of the tweet and returns an image with its contents. The API also accepts parameters to customize the background, colors, borders, and dimensions of the image.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;use-click-to-load-to-enhance-facades&quot;&gt;Use click-to-load to enhance facades &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embed-best-practices/#use-click-to-load-to-enhance-facades&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The click-to-load concept combines lazy-loading and facades. The page initially loads with the facade. When the user interacts with the static placeholder by clicking on it, the third-party embed is loaded. This is also known as the &lt;a href=&quot;https://addyosmani.com/blog/import-on-interaction/&quot; rel=&quot;noopener&quot;&gt;import on interaction&lt;/a&gt; pattern and can be implemented using the following steps.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;On page load: Facade or static element is included on the page.&lt;/li&gt;
&lt;li&gt;On mouseover: Facade preconnects to the third-party embed provider.&lt;/li&gt;
&lt;li&gt;On click: The facade is replaced by the third-party product.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Facades may be used with third-party embeds for video players, chat widgets, authentication services, and social media widgets. YouTube video embeds that are just images with a play button are facades that we come across frequently. The actual video loads only when you click on the image.&lt;/p&gt;
&lt;p&gt;You can build a custom click-to-load facade using the &lt;em&gt;import on interaction&lt;/em&gt; pattern or use one of the following open source facades available for different types of embeds.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;YouTube facade&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/paulirish/lite-youtube-embed&quot; rel=&quot;noopener&quot;&gt;Lite-youtube-embed&lt;/a&gt; is a recommended facade for the YouTube player, which looks like the real player but is 224 times faster. It can be used by downloading the script and stylesheet and then using the &lt;code&gt;&amp;lt;lite-youtube&amp;gt;&lt;/code&gt; tag in HTML or JavaScript. Custom player parameters supported by YouTube may be included through the &lt;code&gt;params&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;lite-youtube&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;videoid&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ogfYd705cRs&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;playlabel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Play: Keynote (Google I/O &lt;span class=&quot;token punctuation&quot;&gt;&#39;&lt;/span&gt;18)&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;lite-youtube&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;Following is a comparison between the lite-youtube-embed and the actual embed.&lt;/p&gt;
&lt;div class=&quot;switcher&quot;&gt;
  &lt;figure&gt;
    &lt;img alt=&quot;Lite YouTube embed&quot; decoding=&quot;async&quot; height=&quot;521&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EcTxjLs9SUb1ofALN8rA.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EcTxjLs9SUb1ofALN8rA.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EcTxjLs9SUb1ofALN8rA.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EcTxjLs9SUb1ofALN8rA.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EcTxjLs9SUb1ofALN8rA.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EcTxjLs9SUb1ofALN8rA.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EcTxjLs9SUb1ofALN8rA.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EcTxjLs9SUb1ofALN8rA.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EcTxjLs9SUb1ofALN8rA.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EcTxjLs9SUb1ofALN8rA.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EcTxjLs9SUb1ofALN8rA.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EcTxjLs9SUb1ofALN8rA.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EcTxjLs9SUb1ofALN8rA.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EcTxjLs9SUb1ofALN8rA.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EcTxjLs9SUb1ofALN8rA.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EcTxjLs9SUb1ofALN8rA.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EcTxjLs9SUb1ofALN8rA.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/EcTxjLs9SUb1ofALN8rA.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
    &lt;figcaption&gt;
      A lite-YouTube embed
    &lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figure&gt;
    &lt;img alt=&quot;Actual YouTube embed&quot; decoding=&quot;async&quot; height=&quot;502&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/cYG1NJqM8ZoLkYOi6xFJ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/cYG1NJqM8ZoLkYOi6xFJ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/cYG1NJqM8ZoLkYOi6xFJ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/cYG1NJqM8ZoLkYOi6xFJ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/cYG1NJqM8ZoLkYOi6xFJ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/cYG1NJqM8ZoLkYOi6xFJ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/cYG1NJqM8ZoLkYOi6xFJ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/cYG1NJqM8ZoLkYOi6xFJ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/cYG1NJqM8ZoLkYOi6xFJ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/cYG1NJqM8ZoLkYOi6xFJ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/cYG1NJqM8ZoLkYOi6xFJ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/cYG1NJqM8ZoLkYOi6xFJ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/cYG1NJqM8ZoLkYOi6xFJ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/cYG1NJqM8ZoLkYOi6xFJ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/cYG1NJqM8ZoLkYOi6xFJ.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/cYG1NJqM8ZoLkYOi6xFJ.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/cYG1NJqM8ZoLkYOi6xFJ.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/cYG1NJqM8ZoLkYOi6xFJ.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
    &lt;figcaption&gt;
      A YouTube embed
    &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;Other similar facades available for YouTube and Vimeo players are &lt;a href=&quot;https://github.com/justinribeiro/lite-youtube&quot; rel=&quot;noopener&quot;&gt;lite-youtube&lt;/a&gt;, &lt;a href=&quot;https://github.com/luwes/lite-vimeo-embed&quot; rel=&quot;noopener&quot;&gt;lite-vimeo-embed&lt;/a&gt;, and &lt;a href=&quot;https://github.com/slightlyoff/lite-vimeo&quot; rel=&quot;noopener&quot;&gt;lite-vimeo&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Chat widget facade&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/calibreapp/react-live-chat-loader&quot; rel=&quot;noopener&quot;&gt;React-live-chat-loader&lt;/a&gt; loads a button that looks like a chat embed instead of the embed itself. It can be used with various chat provider platforms such as Intercom, Help Scout, Messenger, and so on. The look-alike widget is much lighter than the chat-widget and loads faster. It can be replaced by the actual chat widget when the user hovers or clicks on the button or if the page has been idle for a long time. The &lt;a href=&quot;https://wildbit.com/blog/2020/09/30/getting-postmark-lighthouse-performance-score-to-100&quot; rel=&quot;noopener&quot;&gt;Postmark case study&lt;/a&gt; explains how they implemented react-live-chat-loader and performance improvements they achieved.&lt;/p&gt;
 &lt;img alt=&quot;Postmark chat widget&quot; decoding=&quot;async&quot; height=&quot;389&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/XyJON43TV8h1qWNZV1Ev.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/XyJON43TV8h1qWNZV1Ev.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/XyJON43TV8h1qWNZV1Ev.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/XyJON43TV8h1qWNZV1Ev.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/XyJON43TV8h1qWNZV1Ev.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/XyJON43TV8h1qWNZV1Ev.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/XyJON43TV8h1qWNZV1Ev.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/XyJON43TV8h1qWNZV1Ev.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/XyJON43TV8h1qWNZV1Ev.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/XyJON43TV8h1qWNZV1Ev.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/XyJON43TV8h1qWNZV1Ev.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/XyJON43TV8h1qWNZV1Ev.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/XyJON43TV8h1qWNZV1Ev.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/XyJON43TV8h1qWNZV1Ev.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/XyJON43TV8h1qWNZV1Ev.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/XyJON43TV8h1qWNZV1Ev.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/XyJON43TV8h1qWNZV1Ev.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/XyJON43TV8h1qWNZV1Ev.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;remove-or-replace-embeds-with-links&quot;&gt;Remove or replace embeds with links &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embed-best-practices/#remove-or-replace-embeds-with-links&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you find that some third-party embeds result in poor loading performance and using any of the techniques above is not an option, the simplest thing that you can do is remove the embed entirely. If you still want your users to be able to access the content in the embed, you can provide a link to it with &lt;code&gt;target=&amp;quot;_blank&amp;quot;&lt;/code&gt; so that the user can click and view it in another tab.&lt;/p&gt;
&lt;h2 id=&quot;layout-stability&quot;&gt;Layout stability &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embed-best-practices/#layout-stability&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While dynamically loading embedded content can improve the loading performance of a page, it can sometimes cause unexpected movement of page content. This is known as layout shift.&lt;/p&gt;
&lt;p&gt;Since visual stability is important to guarantee a smooth user experience, &lt;a href=&quot;https://web.dev/cls/&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt; measures how often those shifts happen and how disruptive they are.&lt;/p&gt;
&lt;p&gt;Layout shifts can be avoided by reserving space during page load for elements that are going to be dynamically loaded later. The browser can determine the space to be reserved if it knows the width and height of the elements. You can ensure this by specifying the &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes of iframes or by setting a fixed size for static elements where the third-party embed will be loaded. For example, an iframe for a YouTube embed should have width and height specified as follows.&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;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/aKydtOXW8mI&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;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 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;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;Popular embeds like YouTube, Google Maps, and Facebook provide the embed code with size attributes specified. However, there may be providers who do not include this. For example, this code snippet does not indicate the dimensions of the resulting embed.&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;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;twitter-timeline&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://twitter.com/ChannelNewsAsia?ref_src=twsrc%5Etfw&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-tweet-limit&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1&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;Tweets by ChannelNewsAsia&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;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;https://platform.twitter.com/widgets.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;charset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;utf-8&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You can use DevTools to inspect the injected &lt;code&gt;iframe &lt;/code&gt;after this page is rendered. As seen in the following snippet, the height of the injected iframe is fixed while the width is specified in percentage.&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;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;twitter-widget-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;scrolling&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;no&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;allowtransparency&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&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 attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;twitter-timeline twitter-timeline-rendered&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; static&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;visibility&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; visible&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline-block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;max-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1000px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;min-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 180px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;margin-top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;margin-bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;min-height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 6238.31px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-widget-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;profile:ChannelNewsAsia&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Twitter Timeline&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;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;This information can be used to set the size of the containing element to ensure that the container does not expand on loading the feed and there is no layout shift. Following snippet may be used to fix the size of the embed included previously.&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;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;&lt;br /&gt;    &lt;span class=&quot;token selector&quot;&gt;.twitterfeed&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; table-cell&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token property&quot;&gt;vertical-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; top&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100vw&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token selector&quot;&gt;.twitter-timeline&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 400px &lt;span class=&quot;token important&quot;&gt;!important&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;twitterfeed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;       &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;twitter-timeline&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://twitter.com/ChannelNewsAsia?ref_src=twsrc%5Etfw&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-tweet-limit&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1&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;Tweets by ChannelNewsAsia&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;       &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;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;https://platform.twitter.com/widgets.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;charset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;utf-8&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;layout-shift-terminator&quot;&gt;Layout Shift Terminator &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embed-best-practices/#layout-shift-terminator&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Since third-party embeds often omit the dimensions (width, height) for the final content they render, they can cause significant layout shifts on a page. This problem can be tricky to address without manually inspecting the final sizes using DevTools at a variety of different viewport sizes.&lt;/p&gt;
&lt;p&gt;Now there’s an automated tool, &lt;a href=&quot;https://googlechromelabs.github.io/layout-shift-terminator/&quot; rel=&quot;noopener&quot;&gt;Layout Shift Terminator&lt;/a&gt;, that can help you reduce layout shifts from popular embeds, such as from Twitter, Facebook, and other providers.&lt;/p&gt;
&lt;p&gt;Layout Shift Terminator:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Loads the embed client-side in an iframe.&lt;/li&gt;
&lt;li&gt;Resizes the iframe to various popular viewport sizes.&lt;/li&gt;
&lt;li&gt;For each popular viewport, captures the dimensions of the embed to later generate media queries and container queries.&lt;/li&gt;
&lt;li&gt;Sizes a min-height wrapper around the embed markup using media queries (and container queries) until the embed initializes (after which the min-height styles are removed).&lt;/li&gt;
&lt;li&gt;Generates an optimized embed snippet that can be copy/pasted where you would otherwise be including the embed in your page.&lt;/li&gt;
&lt;/ul&gt;
 &lt;img alt=&quot;Layour shift Terminal&quot; decoding=&quot;async&quot; height=&quot;740&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/lJrW6vxuf1G80XUmvXBT.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/lJrW6vxuf1G80XUmvXBT.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/lJrW6vxuf1G80XUmvXBT.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/lJrW6vxuf1G80XUmvXBT.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/lJrW6vxuf1G80XUmvXBT.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/lJrW6vxuf1G80XUmvXBT.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/lJrW6vxuf1G80XUmvXBT.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/lJrW6vxuf1G80XUmvXBT.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/lJrW6vxuf1G80XUmvXBT.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/lJrW6vxuf1G80XUmvXBT.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/lJrW6vxuf1G80XUmvXBT.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/lJrW6vxuf1G80XUmvXBT.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/lJrW6vxuf1G80XUmvXBT.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/lJrW6vxuf1G80XUmvXBT.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/lJrW6vxuf1G80XUmvXBT.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/lJrW6vxuf1G80XUmvXBT.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/lJrW6vxuf1G80XUmvXBT.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/1L2RBhCLSnXjCnSlevaDjy3vba73/lJrW6vxuf1G80XUmvXBT.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Try out the Layout Shift Terminator, and feel free to leave any feedback on &lt;a href=&quot;https://github.com/GoogleChromeLabs/layout-shift-terminator&quot; rel=&quot;noopener&quot;&gt;GitHub&lt;/a&gt;. The tool is in a beta state and aims to improve over time with further refinements.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/embed-best-practices/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Third-party embeds can provide a lot of value to users, but as the number and size of embeds on a page increases, performance can suffer. That’s why it is necessary to measure, judge, and use appropriate loading strategies for embeds based on their position, relevance, and potential users&#39; needs.&lt;/p&gt;
</content>
    <author>
      <name>Leena Sohoni</name>
    </author><author>
      <name>Addy Osmani</name>
    </author><author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Best practices for tags and tag managers</title>
    <link href="https://web.dev/tag-best-practices/"/>
    <updated>2021-07-29T00:00:00Z</updated>
    <id>https://web.dev/tag-best-practices/</id>
    <content type="html" mode="escaped">&lt;p&gt;&lt;a href=&quot;https://support.google.com/tagmanager/answer/3281060?hl=en&quot; rel=&quot;noopener&quot;&gt;Tags&lt;/a&gt; are snippets
of third-party code that are inserted into a site, typically via a tag manager.
Tags are most commonly used for marketing and analytics.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Although tags and third-party scripts are often fundamentally the exact same thing, the separate terminology reflects the different ways that these scripts are used: &amp;quot;tags&amp;quot; are typically owned by marketing departments and added via tag managers; whereas &amp;quot;third-party scripts&amp;quot; more commonly refer to resources added through code changes made by engineering departments. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The performance impact of tags and tag managers varies wildly from site to site.
Tag managers can be compared to an envelope: the tag manager provides a
vessel—but what you fill it with and how you use it is mostly up to you.&lt;/p&gt;
&lt;p&gt;This article discusses techniques for optimizing tags and tag managers for
performance and Web Vitals. Although this article references Google Tag Manager,
many of the ideas discussed are also applicable to other tag managers.&lt;/p&gt;
&lt;h2 id=&quot;impact-on-core-web-vitals&quot;&gt;Impact on Core Web Vitals &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#impact-on-core-web-vitals&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Tag Managers can often impact your Core Web Vitals indirectly by using up resources needed to load your page quickly and keep it responsive. Bandwidth can be spent downloading the tag manager JavaScript for your sites, or the subsequent calls this makes. CPU time on the main thread can be spent evaluating and executing JavaScript contained within the tag manager and the tags.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint&lt;/a&gt; (LCP) is vulnerable to bandwidth contention during the critical page load time. Additionally, blocking the main thread can &lt;a href=&quot;https://web.dev/optimize-lcp/#2-eliminate-element-render-delay&quot;&gt;delay the LCP render time&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/cls/&quot;&gt;Cumulative Layout Shift&lt;/a&gt; (CLS) can be impacted, either by delaying loading critical resources before the first render, or by tag managers injecting content into the page.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/fid/&quot;&gt;First Input Delay&lt;/a&gt; (FID) is susceptible to CPU contention on the main thread. This can also impact the newer &lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint&lt;/a&gt; (INP) metric and we have seen a correlation between the size of tag managers, and poorer INP scores.&lt;/p&gt;
&lt;h2 id=&quot;tag-types&quot;&gt;Tag types &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#tag-types&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The impact of tags on performance varies by tag type. Generally speaking, image
tags (&amp;quot;pixels&amp;quot;) are the most performant, followed by custom templates, and
lastly, custom HTML tags. Vendor tags vary depending on the functionality they
allow.&lt;/p&gt;
&lt;p&gt;However, keep in mind that how you use a tag greatly influences its performance
impact. &amp;quot;Pixels&amp;quot; are highly performant largely because the nature of this tag
type imposes tight restrictions on how they can be used; custom HTML tags aren&#39;t
necessarily always bad for performance, but due to the level of freedom they
offer users, they can be easy to misuse in a way that is bad for performance.&lt;/p&gt;
&lt;p&gt;When thinking about tags, keep scale in mind: the performance impact of any
single tag may be negligible—but can become significant when tens or hundreds of
tags are used on the same page.&lt;/p&gt;
&lt;h3 id=&quot;not-all-scripts-should-be-loaded-using-a-tag-manager&quot;&gt;Not all scripts should be loaded using a tag manager &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#not-all-scripts-should-be-loaded-using-a-tag-manager&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tag managers are typically not a good mechanism for loading resources that
implement immediate visual or functional aspects of the user experience—for
example, cookie notices, hero images, or site features. Using a tag manager to
load these resources typically delays their delivery. This is bad for the user
experience and can also increase metrics like LCP and CLS.  In addition, keep in
mind that some users block tag managers. Using a tag manager to implement UX
features may result in a broken website for some of your users.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Resources requested via tag manager will typically load later than resources requested directly. This is not always a bad thing. If a tag manager is primarily used to deliver non-essential resources, this delay can be used constructively by providing a mechanism for delaying non-essential requests. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;be-careful-with-custom-html-tags&quot;&gt;Be careful with Custom HTML tags &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#be-careful-with-custom-html-tags&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://support.google.com/tagmanager/answer/6107167/custom-tags?hl=en#CustomHTML&quot; rel=&quot;noopener&quot;&gt;Custom HTML
tags&lt;/a&gt;
have been around for many years and are heavily used on most sites. Custom HTML
tags allow you to enter your own code with few restrictions as, despite the name,
the main use of this tag is to add custom &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; elements to a page.&lt;/p&gt;
&lt;p&gt;Custom HTML tags can be used in a wide variety of ways and their performance impact
varies significantly. When measuring the performance of your site, be aware that
most tools will attribute the performance impact of a Custom HTML tag to the tag
manager that injected it—rather than the tag itself.&lt;/p&gt;
&lt;img alt=&quot;Screenshot of creating a custom tag in Google Tag Manager&quot; class=&quot;screenshot&quot; decoding=&quot;async&quot; height=&quot;470&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/cjLFJStmZvkQkr4DYPlk.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Custom HTML tags can insert an element into the surrounding page. The act
of inserting elements into the page can be a source of performance issues, and
in some cases, also cause layout shifts.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In most situations, if an element is inserted into the page, the browser
must recalculate the size and position of each item on the page—this process
is known as
&lt;a href=&quot;https://developer.chrome.com/blog/inside-browser-part3/#layout&quot; rel=&quot;noopener&quot;&gt;layout&lt;/a&gt;.
The performance impact of a single layout is minimal, but when it occurs
excessively it can become a source of performance issues. The impact of this
phenomenon is larger on lower-end devices and pages with a high number of
DOM elements.&lt;/li&gt;
&lt;li&gt;If a visible page element is inserted into the DOM after the surrounding
area has already been rendered, it can cause a layout shift. This phenomenon
is not unique to tag managers—however, because tags typically load later
than other parts of the page, it&#39;s common for them to be inserted into the
DOM after the surrounding page has already been rendered.&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; If you&#39;re adding or changing visible content on a page, keep in mind that search engines will need to be able to fetch and process the JavaScript used. For Google, you can &lt;a href=&quot;https://developers.google.com/search/docs/crawling-indexing/javascript/dynamic-rendering#verify&quot;&gt;verify rendering of a page&lt;/a&gt; for Search; other search engines may have similar testing tools available. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;consider-using-custom-templates&quot;&gt;Consider using Custom Templates &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#consider-using-custom-templates&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developers.google.com/tag-platform/tag-manager/templates&quot; rel=&quot;noopener&quot;&gt;Custom templates&lt;/a&gt; support
some of the same operations as Custom HTML tags but are built upon a sandboxed
version of JavaScript that provides
&lt;a href=&quot;https://developers.google.com/tag-manager/templates/api&quot; rel=&quot;noopener&quot;&gt;APIs&lt;/a&gt; for common use
cases like script injection and pixel injection. As the name implies, they allow
a template to be created, by a power user who can build this with performance in
mind. Less technical users can then use the template. This is often safer
than providing full Custom HTML access.&lt;/p&gt;
&lt;p&gt;Due to the greater restrictions imposed on custom templates, these tags are much
less likely to exhibit performance or security issues; however, for these same
reasons, custom templates will not work for all use cases.&lt;/p&gt;
&lt;img alt=&quot;Screenshot of using a custom template in Google Tag Manager&quot; class=&quot;screenshot&quot; decoding=&quot;async&quot; height=&quot;388&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QRz6pn83YE6plVivynA7.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h3 id=&quot;inject-scripts-correctly&quot;&gt;Inject scripts correctly &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#inject-scripts-correctly&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Using a tag manager to inject a script is a very common use case. The
recommended way to do this is to use a Custom Template and the
&lt;a href=&quot;https://developers.google.com/tag-manager/templates/api#injectscript&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;injectScript&lt;/code&gt;&lt;/a&gt;
API.&lt;/p&gt;
&lt;p&gt;For information on using the injectScript API to convert an existing Custom HTML
tag, see &lt;a href=&quot;https://developers.google.com/tag-manager/templates/convert-existing-tag&quot; rel=&quot;noopener&quot;&gt;Convert an existing
tag&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you must use a Custom HTML tag, here are some things to keep in mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Libraries and large third-party scripts should be loaded via a script tag
that downloads an external file (for example, &lt;code&gt;&amp;lt;script src=&amp;quot;external-scripts.js&amp;quot;&amp;gt;&lt;/code&gt;), rather than directly copy-pasting the script&#39;s
contents into the tag. Although forgoing use of the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag
eliminates a separate round-trip to download the script&#39;s contents, this
practice increases container size and prevents the script from being cached
separately by the browser.&lt;/li&gt;
&lt;li&gt;Many vendors recommend placing their &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag at the top of the
&lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;. However, for scripts loaded via tag manager, this recommendation
is usually unnecessary: in most situations, the browser has already finished
parsing the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; by the time that the tag manager executes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;use-pixels&quot;&gt;Use pixels &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#use-pixels&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In some situations third-party scripts can be replaced with image or iframe
&amp;quot;pixels&amp;quot;. Compared to their script-based counterparts, pixels may support
less functionality, so are often seen as a less-preferred implementation because
of that. However, when used inside tag managers, pixels can be more dynamic
as they can fire on triggers and pass different variables. They are the most
performant and secure type of tag because there is no JavaScript execution after
it is fired. Pixels have a very small resource size (less than 1 KB) and do
not cause layout shifts.&lt;/p&gt;
&lt;p&gt;Check with your third-party provider for more information on their support for
pixels. Additionally, you can try inspecting their code for a &lt;code&gt;&amp;lt;noscript&amp;gt;&lt;/code&gt; tag.
If a vendor supports pixels, they will often include them within the
&lt;code&gt;&amp;lt;noscript&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;
&lt;img alt=&quot;Screenshot of custom image tag in Google Tag Manager&quot; decoding=&quot;async&quot; height=&quot;342&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/zJPC30RE3axvUDiWUdqW.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h4 id=&quot;alternatives-to-pixels&quot;&gt;Alternatives to pixels &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#alternatives-to-pixels&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Pixels became popular largely because at one time they were one of the cheapest
and most reliable ways to make a HTTP request in situations where the server
response is not relevant ( for example, when sending data to analytics
providers). The
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Navigator/sendBeacon&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;navigator.sendBeacon()&lt;/code&gt;&lt;/a&gt;
and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/WindowOrWorkerGlobalScope/fetch#parameters&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;fetch() keepalive&lt;/code&gt;&lt;/a&gt;
APIs are designed to address this same use case but are arguably more reliable
than pixels.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The &lt;code&gt;sendBeacon()&lt;/code&gt; and &lt;code&gt;fetch() keepalive&lt;/code&gt; APIs will both still work in situations where the browser is &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Window/unload_event&quot;&gt;unloading&lt;/a&gt; the page: for example, both of these APIs can be used to track outbound link clicks. By contrast, techniques like pixels and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/XMLHttpRequest&quot;&gt;&lt;code&gt;XMLHttpRequest&lt;/code&gt;&lt;/a&gt; would likely fail in those cases. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;There is nothing wrong with continuing to use pixels—they are well supported and
have minimal performance impact. However, if you are building your own beacons,
it is worth considering using one of these APIs.&lt;/p&gt;
&lt;h5 id=&quot;sendbeacon&quot;&gt;&lt;code&gt;sendBeacon()&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#sendbeacon&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;The
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Navigator/sendBeacon&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;navigator.sendBeacon()&lt;/code&gt;&lt;/a&gt;
API is designed for sending small amounts of data to web servers in situations
where the server response does not matter.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://example.com/analytics&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;checkout&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; performance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendBeacon&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;sendBeacon()&lt;/code&gt; has a limited API: it only supports making POST requests and does
not support setting custom headers. It is
&lt;a href=&quot;https://caniuse.com/beacon&quot; rel=&quot;noopener&quot;&gt;supported&lt;/a&gt; by all modern browsers.&lt;/p&gt;
&lt;h5 id=&quot;fetch-keepalive&quot;&gt;&lt;code&gt;fetch() keepalive&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#fetch-keepalive&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/WindowOrWorkerGlobalScope/fetch#parameters&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;keepalive&lt;/code&gt;&lt;/a&gt;
is a flag that allows the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Fetch_API/Using_Fetch&quot; rel=&quot;noopener&quot;&gt;Fetch
API&lt;/a&gt; to
be used to make non-blocking requests like event reporting and analytics. It is
used by including &lt;code&gt;keepalive: true&lt;/code&gt; in the parameters passed to &lt;code&gt;fetch()&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://example.com/analytics&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;checkout&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; performance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;POST&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;keepalive&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If &lt;code&gt;fetch() keepalive&lt;/code&gt; and &lt;code&gt;sendBeacon()&lt;/code&gt; seem very similar, it&#39;s because they
are. In fact, in Chromium browsers, &lt;code&gt;sendBeacon()&lt;/code&gt; is now built upon &lt;code&gt;fetch() keepalive&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When choosing between &lt;code&gt;fetch() keepalive&lt;/code&gt; and &lt;code&gt;sendBeacon()&lt;/code&gt;, it&#39;s important to
consider the features and browser support that you need. The fetch() API is
significantly more flexible; however, &lt;code&gt;keepalive&lt;/code&gt; has less browser
&lt;a href=&quot;https://caniuse.com/?search=keepalive&quot; rel=&quot;noopener&quot;&gt;support&lt;/a&gt; than &lt;code&gt;sendBeacon()&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;get-clarification&quot;&gt;Get clarification &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#get-clarification&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tags are often created by following guidance provided by a third-party vendor.
If it is unclear what a vendor&#39;s code does—consider asking someone who knows.
Getting a second opinion can help identify if a tag has the potential to create
performance or security issues.&lt;/p&gt;
&lt;p&gt;Labeling tags with an owner in the tag manager is also recommended. It is far
too easy to forget who owns a tag, and be afraid to remove it just in case!&lt;/p&gt;
&lt;h2 id=&quot;triggers&quot;&gt;Triggers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#triggers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At a high-level, optimizing &lt;a href=&quot;https://support.google.com/tagmanager/topic/7679108?hl=en&amp;amp;ref_topic=7679384&quot; rel=&quot;noopener&quot;&gt;tag
triggers&lt;/a&gt;
generally consists of making sure to not trigger tags more than necessary and
choosing a trigger that balances business needs with performance costs.&lt;/p&gt;
&lt;p&gt;Triggers themselves are JavaScript code that will increase the size—and execution
cost—of the tag manager. While most triggers are small, the cumulative effect can
add up. Having many click events for example, or timer triggers can dramatically
increase the workload of the tag manager.&lt;/p&gt;
&lt;h3 id=&quot;choose-an-appropriate-trigger-event&quot;&gt;Choose an appropriate trigger event &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#choose-an-appropriate-trigger-event&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The performance impact of a tag is not fixed: generally speaking, the earlier
that a tag fires, the greater its impact on performance. Resources are typically
constrained during the initial page load and therefore loading or executing a
particular resource (or tag) takes resources away from something else.&lt;/p&gt;
&lt;p&gt;Although it is important to choose appropriate triggers for all tags, it is
particularly important for tags that load large resources or execute long
scripts.&lt;/p&gt;
&lt;p&gt;Tags can be triggered on
&lt;a href=&quot;https://support.google.com/tagmanager/answer/7679319?hl=en&quot; rel=&quot;noopener&quot;&gt;Page Views&lt;/a&gt;
(typically &lt;code&gt;Page load&lt;/code&gt;, on &lt;code&gt;DOM Ready&lt;/code&gt;, on &lt;code&gt;Window Loaded&lt;/code&gt;), or based on a
custom event. To avoid impacting page load, it is recommended to fire
non-essential tags after &lt;code&gt;Window Loaded&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;use-custom-events&quot;&gt;Use custom events &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#use-custom-events&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://support.google.com/tagmanager/answer/7679219?hl=en&quot; rel=&quot;noopener&quot;&gt;Custom events&lt;/a&gt;
allow you to fire triggers in response to page events that aren&#39;t covered by
Google Tag Manager&#39;s built-in triggers. For example, many tags use &lt;a href=&quot;https://support.google.com/tagmanager/answer/7679319?hl=en&quot; rel=&quot;noopener&quot;&gt;page view
triggers&lt;/a&gt;; however,
the time period between &lt;code&gt;DOM Ready&lt;/code&gt; and &lt;code&gt;Window Loaded&lt;/code&gt; can be lengthy on many
pages and this can make it difficult to fine-tune when a tag fires. Custom
events provide a solution to this problem.&lt;/p&gt;
&lt;p&gt;To use custom events, first create a custom event trigger and update your tags
to use this trigger.&lt;/p&gt;
&lt;img alt=&quot;Screenshot of a Custom Event trigger in Google Tag Manager&quot; decoding=&quot;async&quot; height=&quot;348&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/FsoFKKxRishTMunJAl3W.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;To fire the trigger, push the corresponding event to the data layer.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Custom event trigger that fires after 2 seconds&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  dataLayer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token string-property property&quot;&gt;&#39;event&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;my-custom-event&#39;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;use-specific-trigger-conditions&quot;&gt;Use specific trigger conditions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#use-specific-trigger-conditions&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Using specific trigger conditions helps avoid firing a tag unnecessarily.
Although there are many ways to apply this concept, one of the simplest yet most
useful things you can do is ensure that a tag only fires on pages where it is
actually used.&lt;/p&gt;
&lt;img alt=&quot;Screenshot showing trigger conditions in Google Tag Manager&quot; decoding=&quot;async&quot; height=&quot;454&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/B05L2hNj1RUO9duvvdYX.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;&lt;a href=&quot;https://support.google.com/tagmanager/answer/7182738&quot; rel=&quot;noopener&quot;&gt;Built-in variables&lt;/a&gt; can
also be incorporated into trigger conditions to limit tag firing.&lt;/p&gt;
&lt;p&gt;However, be aware that having complex trigger conditions or exceptions takes
processing time in and off itself, so do not make them too complex.&lt;/p&gt;
&lt;h3 id=&quot;load-your-tag-manager-at-an-appropriate-time&quot;&gt;Load your tag manager at an appropriate time &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#load-your-tag-manager-at-an-appropriate-time&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Adjusting when you tag manager itself loads can have a significant impact on
performance. Triggers, regardless of how they are configured, can&#39;t fire until
after a tag manager loads. Although it is important to choose good triggers for
individual tags (as explained above), experimenting with when you load your tag
manager can often have an equal or greater impact given that this single
decision will impact all tags on a page.&lt;/p&gt;
&lt;p&gt;Loading the tag manager later also adds a layer of control and can avoid future
performance issues as it will prevent a tag manager user inadvertently loading
a tag too early, without realizing the impact this can have.&lt;/p&gt;
&lt;h2 id=&quot;variables&quot;&gt;Variables &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#variables&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Variables allow data to be read from the page. They are useful in triggers, and
in tags themselves.&lt;/p&gt;
&lt;p&gt;Like triggers, variables result in JavaScript code being added to the tag manager,
and so can cause performance issues. Variables can be relatively simple built-in
types that can, for example, read parts of the URL, cookies, data layer, or DOM.
Or they can be custom JavaScript that is basically unlimited in what it can do.&lt;/p&gt;
&lt;p&gt;Keep variables simple and to a minimum, since they will need to be evaluated
continually by the tag manager. Remove old variables that are no longer used
to reduce both the size of the tag manager script, and the processing time it
uses.&lt;/p&gt;
&lt;h2 id=&quot;tag-management&quot;&gt;Tag management &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#tag-management&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Using the tags efficiently will reduce the risk of performance issues.&lt;/p&gt;
&lt;h3 id=&quot;use-the-data-layer&quot;&gt;Use the data layer &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#use-the-data-layer&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://developers.google.com/tag-manager/devguide&quot; rel=&quot;noopener&quot;&gt;data layer&lt;/a&gt; &amp;quot;contains
all of the information that you want to pass to Google Tag Manager&amp;quot;. More
concretely, it is a JavaScript array of objects that contain information about
the page. It can also be used to trigger tags.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Contents of the data layer&lt;/span&gt;&lt;br /&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataLayer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token string-property property&quot;&gt;&#39;pageCategory&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;signup&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token string-property property&quot;&gt;&#39;visitorType&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;high-value&#39;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Pushing a variable to the data layer&lt;/span&gt;&lt;br /&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataLayer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string-property property&quot;&gt;&#39;variable_name&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;variable_value&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Pushing an event to the data layer&lt;/span&gt;&lt;br /&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataLayer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string-property property&quot;&gt;&#39;event&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;event_name&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Although Google Tag Manager can be used without the data layer, its use is
strongly recommended. The data layer provides a way to consolidate the data
being accessed by third-party scripts into a single place thereby providing
better visibility into its usage. Amongst other things, this can help reduce
redundant variable calculations and script execution. Using a data layer also
controls the data being accessed by the tags, rather than giving full JavaScript
variable or DOM access.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The performance benefits of the data layer might seem non-intuitive given that updating the data layer causes Google Tag Manager to reevaluate all container variables and potentially trigger tags—all of which entails JavaScript execution. Although it is possible to misuse the data layer, generally speaking, if working with the data layer is a source of performance issues, it probably indicates that the container itself has performance issues—the data layer is merely making these issues more apparent. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;remove-duplicate-and-unused-tags&quot;&gt;Remove duplicate and unused tags &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#remove-duplicate-and-unused-tags&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Duplicate tags can occur when a tag is included in a page&#39;s HTML markup in
addition to being injected through a tag manager.&lt;/p&gt;
&lt;p&gt;Unused tags should be paused or removed rather than blocked through use of a
&lt;a href=&quot;https://support.google.com/tagmanager/answer/7679318?hl=en&quot; rel=&quot;noopener&quot;&gt;trigger exception&lt;/a&gt;.
Pausing or removing a tag removes the code from the container; blocking does
not.&lt;/p&gt;
&lt;p&gt;When unused tags are removed, the triggers and variables should also be
reviewed to see if any of them can be removed if they were only used by those
tags.&lt;/p&gt;
&lt;h3 id=&quot;use-allow-and-deny-lists&quot;&gt;Use allow and deny lists &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#use-allow-and-deny-lists&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developers.google.com/tag-manager/web/restrict&quot; rel=&quot;noopener&quot;&gt;Allow and deny lists&lt;/a&gt;
allow you to configure highly granular restrictions on the tags, triggers, and
variables allowed on a page. This can be used to help enforce performance best
practices and other policies.&lt;/p&gt;
&lt;p&gt;Allow and deny lists are configured through the data layer.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataLayer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token string-property property&quot;&gt;&#39;gtm.allowlist&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;id&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;id&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token string-property property&quot;&gt;&#39;gtm.blocklist&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;customScripts&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;For example, it is possible to not allow any Custom HTML tags, JavaScript
variables, or direct DOM access. This means only pixels and pre-defined tags
can be used, with data from the data layer. While this is certainly restrictive,
it can result in a much more performant, and secure, tag manager implementation.&lt;/p&gt;
&lt;h3 id=&quot;consider-using-server-side-tagging&quot;&gt;Consider using server-side tagging &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#consider-using-server-side-tagging&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Switching to server-side tagging is not a trivial task, but it is worth
considering - particularly for larger sites that want more control over their
data. Server-side tagging removes vendor code from the client, and with it,
offloads processing from the client to the server.&lt;/p&gt;
&lt;p&gt;For example, when using client-side tagging, sending data to multiple analytics
accounts entails that the client initiates separate requests for each endpoint.
By contrast, with server-side tagging, a single request is made by the client to
the server-side container, and from there, this data is forwarded to different
analytics accounts.&lt;/p&gt;
&lt;p&gt;Keep in mind that server-side tagging only works with some tags; tag
compatibility varies depending on vendor.&lt;/p&gt;
&lt;p&gt;For more information, see &lt;a href=&quot;https://developers.google.com/tag-manager/serverside/intro&quot; rel=&quot;noopener&quot;&gt;An introduction to server-side
tagging&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;containers&quot;&gt;Containers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#containers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Tag managers typically allow multiple instances or &amp;quot;containers&amp;quot; within their
set up. This allows multiple containers to be controlled within the one tag
manager account.&lt;/p&gt;
&lt;h3 id=&quot;use-only-one-container-per-page&quot;&gt;Use only one container per page &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#use-only-one-container-per-page&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Using multiple
&lt;a href=&quot;https://developers.google.com/tag-manager/api/v1/reference/accounts/containers&quot; rel=&quot;noopener&quot;&gt;containers&lt;/a&gt;
on a single page can create significant performance issues as it introduces
additional overhead and script execution. At the very least it duplicates the
core tag code itself which, as it is delivered as part of the container&#39;s
JavaScript, cannot be reused between the containers.&lt;/p&gt;
&lt;p&gt;It is rare for multiple containers to be used effectively. However, there can be
instances when this can work—if controlled well—including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Having a lighter &amp;quot;early load&amp;quot; container, and a heavier &amp;quot;later load&amp;quot; container,
rather than one large container.&lt;/li&gt;
&lt;li&gt;Having a restricted container used by less technical users, with a less
restricted, but more tightly controlled, container for tags that cannot be used
in the restricted container.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you must use multiple containers per page, &lt;a href=&quot;https://developers.google.com/tag-manager/devguide#multiple-containers&quot; rel=&quot;noopener&quot;&gt;follow Google Tag manager
guidance for setting up multiple
containers&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;use-separate-containers-if-needed&quot;&gt;Use separate containers if needed &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#use-separate-containers-if-needed&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you use a tag manager for multiple properties (for example, a web app and a
mobile app)—the number of containers you use can help or hurt your workflow
productivity. It can also impact performance.&lt;/p&gt;
&lt;p&gt;Generally speaking, a single container can effectively be used across multiple
sites if the sites are similar in use and structure. For example, although a
brand&#39;s mobile and web apps might serve similar functions, it&#39;s likely that the
apps will be structured differently, and therefore more effectively managed
through separate containers.&lt;/p&gt;
&lt;p&gt;Trying to reuse a single container too broadly typically unnecessarily increases
the complexity and size of the container by forcing the adoption of complex logic
to manage tags and triggers.&lt;/p&gt;
&lt;h3 id=&quot;keep-an-eye-on-container-size&quot;&gt;Keep an eye on container size &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#keep-an-eye-on-container-size&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The size of a container is determined by its tags, triggers, and variables.
Although a small container may still negatively impact page performance, a large
container almost certainly will.&lt;/p&gt;
&lt;p&gt;Container size should not be your north-star metric when optimizing your tag
usage; however, a large container size is often a warning sign that a container
is not well maintained and possibly misused.&lt;/p&gt;
&lt;p&gt;Google Tag Manager
&lt;a href=&quot;https://support.google.com/tagmanager/answer/2772488?hl=en&quot; rel=&quot;noopener&quot;&gt;limits&lt;/a&gt; container
size to 200 KB and will warn about container size starting at 140 KB. However,
most sites should aim to keep their containers far smaller than this. For
perspective, the median site container is around 50 KB.&lt;/p&gt;
&lt;p&gt;To determine the size of your container, look at the size of the response
returned by &lt;code&gt;https://www.googletagmanager.com/gtag/js?id=YOUR_ID&lt;/code&gt;. This
response contains the Google Tag Manager library plus the contents of the
container. By itself, the Google Tag Manager library is around 33 KB
compressed.&lt;/p&gt;
&lt;h3 id=&quot;name-your-container-versions&quot;&gt;Name your container versions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#name-your-container-versions&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A &lt;a href=&quot;https://developers.google.com/tag-manager/api/v1/reference/accounts/containers/versions&quot; rel=&quot;noopener&quot;&gt;container
version&lt;/a&gt;
is a snapshot of a container&#39;s content at a particular point in time. Using a
meaningful name and along with including a short description of meaningful
changes within can go a long way in making it easier to debug future performance
issues.&lt;/p&gt;
&lt;h2 id=&quot;tagging-workflows&quot;&gt;Tagging workflows &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#tagging-workflows&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Managing changes to your tags is important to ensure they do not have a
negative impact on page performance.&lt;/p&gt;
&lt;h3 id=&quot;test-tags-before-deploying&quot;&gt;Test tags before deploying &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#test-tags-before-deploying&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Testing your tags before deployment can help catch issues (performance and
otherwise) before they ship.&lt;/p&gt;
&lt;p&gt;Things to consider when testing a tag include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is the tag working correctly?&lt;/li&gt;
&lt;li&gt;Does the tag cause any layout shifts?&lt;/li&gt;
&lt;li&gt;Does the tag load any resources? How large are these resources?&lt;/li&gt;
&lt;li&gt;Does the tag trigger a long-running script?&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;preview-mode&quot;&gt;Preview mode &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#preview-mode&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://support.google.com/tagmanager/answer/6107056&quot; rel=&quot;noopener&quot;&gt;Preview mode&lt;/a&gt; allows you
to test tag changes on your actual site without having to deploy them to the
public first. Preview mode includes a debugging console that provides
information about tags.&lt;/p&gt;
&lt;p&gt;The execution time of Google Tag Manager will be different (slightly slower)
when run in Preview mode due to the additional overhead required to expose
information in the debugging console. Thus, comparing Web Vitals measurements
collected in preview mode to those collected in production is not recommended.
However, this discrepancy should not affect the execution behavior of the tags
themselves.&lt;/p&gt;
&lt;h4 id=&quot;standalone-testing&quot;&gt;Standalone testing &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#standalone-testing&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;An alternative approach to testing tags is to set up an empty page containing a
container with a single tag—the tag you are testing. This testing setup is less
realistic and won&#39;t catch some issues (for example, whether a tag causes layout
shifts)—however it can make it easier to isolate and measure the impact of the
tag on things like script execution. Check out how &lt;a href=&quot;https://medium.com/the-telegraph-engineering/improving-third-party-web-performance-at-the-telegraph-a0a1000be5&quot; rel=&quot;noopener&quot;&gt;Telegraph uses this
isolation approach to improve
performance&lt;/a&gt;
of third-party code.&lt;/p&gt;
&lt;h3 id=&quot;monitor-tag-performance&quot;&gt;Monitor tag performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#monitor-tag-performance&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Google Tag Manager &lt;a href=&quot;https://developers.google.com/tag-manager/templates/monitoring&quot; rel=&quot;noopener&quot;&gt;Monitoring
API&lt;/a&gt; can be used
to gather information about the &lt;a href=&quot;https://developers.google.com/tag-manager/templates/monitoring#execution_times&quot; rel=&quot;noopener&quot;&gt;execution
time&lt;/a&gt;
of a particular tag. This information is reported to an endpoint of your
choosing.&lt;/p&gt;
&lt;p&gt;For more information, see &lt;a href=&quot;https://www.simoahava.com/analytics/google-tag-manager-monitor/&quot; rel=&quot;noopener&quot;&gt;How to build a Google Tag Manager
Monitor&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;require-approval-for-container-changes&quot;&gt;Require approval for container changes &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#require-approval-for-container-changes&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;First-party code typically goes through review and testing before deployment -
treat your tags the same. Adding &lt;a href=&quot;https://support.google.com/tagmanager/answer/4525539/tag-manager-and-2-step-verification?hl=en&quot; rel=&quot;noopener&quot;&gt;two-step
verification&lt;/a&gt;,
which requires administrator approval for container changes, is one way to do
this. Alternatively, if you don&#39;t want to require two-step verification but
would still like to keep an eye on changes, you can set up &lt;a href=&quot;https://support.google.com/tagmanager/answer/9713667?hl=en&quot; rel=&quot;noopener&quot;&gt;container
notifications&lt;/a&gt; to
receive email alerts about container events of your choosing.&lt;/p&gt;
&lt;h3 id=&quot;periodically-audit-tag-usage&quot;&gt;Periodically audit tag usage &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/tag-best-practices/#periodically-audit-tag-usage&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One of the challenges of working with tags is that they tend to accumulate over
time: tags get added but are rarely removed. Auditing tags periodically is one
way to reverse this trend. The ideal frequency for doing this will depend on how
often your site&#39;s tags are updated.&lt;/p&gt;
&lt;p&gt;Labeling each tag so the owner is obvious allows easier identification of who
is responsive for that tag and can say whether it is still needed.&lt;/p&gt;
&lt;p&gt;When auditing tags, do not forget about cleaning up triggers and variables as
well. They can easily be the cause of performance issues too.&lt;/p&gt;
&lt;p&gt;For more information, see &lt;a href=&quot;https://web.dev/controlling-third-party-scripts/&quot;&gt;Keeping third-party scripts under
control&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author><author>
      <name>Barry Pollard</name>
    </author>
  </entry>
  
  <entry>
    <title>Best practices for fonts</title>
    <link href="https://web.dev/font-best-practices/"/>
    <updated>2021-06-03T00:00:00Z</updated>
    <id>https://web.dev/font-best-practices/</id>
    <content type="html" mode="escaped">&lt;p&gt;This article discusses performance best practices for fonts. There are a variety of ways in which web fonts impact performance:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Delayed text rendering:&lt;/strong&gt; If a web font has not loaded, browsers typically delay text rendering. In many situations, this delays &lt;a href=&quot;https://web.dev/fcp&quot;&gt;First Contentful Paint (FCP)&lt;/a&gt;. In some situations, this delays &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Layout shifts:&lt;/strong&gt; The practice of font swapping has the potential to &lt;a href=&quot;https://web.dev/debug-layout-shifts/#identifying-the-cause-of-a-layout-shift&quot;&gt;cause layout shifts&lt;/a&gt; and so impact &lt;a href=&quot;https://web.dev/cls/&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt;. These layout shifts occur when a web font and its fallback font take up different amounts of space on the page.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This article is broken down into three sections: &lt;strong&gt;font loading&lt;/strong&gt;, &lt;strong&gt;font delivery&lt;/strong&gt;, and &lt;strong&gt;font rendering&lt;/strong&gt;. Each section explains how that particular aspect of the font lifecycle works and provides corresponding best practices.&lt;/p&gt;
&lt;h2 id=&quot;font-loading&quot;&gt;Font loading &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#font-loading&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Fonts are typically important resources, as without them the user might be unable to view page content. Thus, best practices for font loading generally focus on making sure that fonts get loaded as early as possible. Particular care should be given to fonts loaded from third-party sites as downloading these font files requires separate connection setups.&lt;/p&gt;
&lt;p&gt;If you&#39;re unsure if your page&#39;s fonts are being requested in time, check the &lt;strong&gt;Timing&lt;/strong&gt; tab within the &lt;strong&gt;Network&lt;/strong&gt; panel in Chrome DevTools for more information.&lt;/p&gt;
&lt;img alt=&quot;Screenshot of the Timing tab in DevTools&quot; decoding=&quot;async&quot; height=&quot;472&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/hFVGVzDQHymbC5aIAtD9.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h3 id=&quot;understanding-font-face&quot;&gt;Understanding &lt;code&gt;@font-face&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#understanding-font-face&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Before diving into best practices for font loading it&#39;s important to understand how &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@font-face&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;@font-face&lt;/code&gt;&lt;/a&gt; works and how this impacts font loading.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@font-face&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;@font-face&lt;/code&gt;&lt;/a&gt; declaration is an essential part of working with any web font. At a minimum, it declares the name that will be used to refer to the font and indicates the location of the corresponding font file.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Open Sans&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;/fonts/OpenSans-Regular-webfont.woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;A common misconception is that a font is requested when a &lt;code&gt;@font-face&lt;/code&gt; declaration is encountered—this is not true. By itself, &lt;code&gt;@font-face&lt;/code&gt; declaration does not trigger font download. Rather, a font is downloaded only if it is referenced by styling that is used on the page. For example, like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Open Sans&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;/fonts/OpenSans-Regular-webfont.woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;h1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Open Sans&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In other words, in the example above, &lt;code&gt;Open Sans&lt;/code&gt; would only be downloaded if the page contained a &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; element.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Other ways of loading a font are the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Preloading_content&quot;&gt;&lt;code&gt;preload&lt;/code&gt;&lt;/a&gt; resource hint and the &lt;a href=&quot;https://web.dev/optimize-webfont-loading/#the-font-loading-api&quot;&gt;Font Loading API&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Thus, when thinking about font optimization, it&#39;s important to give stylesheets just as much consideration as the font files themselves. Changing the contents or delivery of stylesheets can have a significant impact on when fonts arrive. Similarly, removing unused CSS and splitting stylesheets can reduce the number of fonts loaded by a page.&lt;/p&gt;
&lt;h3 id=&quot;inline-font-declarations&quot;&gt;Inline font declarations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#inline-font-declarations&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Most sites would strongly benefit from inlining font declarations and other critical styling in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of the main document rather than including them in an external stylesheet. This allows the browser to discover the font declarations sooner as the browser doesn&#39;t need to wait for the external stylesheet to download.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;&lt;br /&gt;    &lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Open Sans&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;/fonts/OpenSans-Regular-webfont.woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Open Sans&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    ...etc.&lt;br /&gt;&lt;br /&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Note that if only some of the CSS is inlined, then the browser will still need to wait for all the CSS to be loaded, before it can discover if fonts are needed.&lt;br /&gt; &lt;br /&gt; Also note that inlining the font files themselves is not recommended. Inlining large resources like fonts is likely to delay the delivery of the main document, and with it, the discovery of other resources. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Inlining critical CSS can be a more advanced technique that not all sites will be able to achieve. The performance benefits are clear, but it requires additional processes and build tools to ensure the necessarily CSS—and ideally only the critical CSS—is inlined correctly and that any additional CSS is delivered in a non-render blocking fashion.&lt;/p&gt;
&lt;h3 id=&quot;preconnect-to-critical-third-party-origins&quot;&gt;Preconnect to critical third-party origins &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#preconnect-to-critical-third-party-origins&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If your site loads fonts from a third-party site, it is highly recommended that you use the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Link_types/preconnect&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;preconnect&lt;/code&gt;&lt;/a&gt; resource hint to establish early connection(s) with the third-party origin. Resource hints should be placed in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of the document. The resource hint below sets up a connection for loading the font stylesheet.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preconnect&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://fonts.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;To preconnect the connection that is used to download the font file, add a separate &lt;code&gt;preconnect&lt;/code&gt; resource hint that uses the &lt;code&gt;crossorigin&lt;/code&gt; attribute. Unlike stylesheets, font files must be sent over a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/CORS#what_requests_use_cors&quot; rel=&quot;noopener&quot;&gt;CORS connection&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preconnect&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://fonts.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preconnect&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://fonts.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;crossorigin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;When using the &lt;code&gt;preconnect&lt;/code&gt; resource hint, keep in mind that a font provider may serve stylesheets and fonts from separate origins. For example, this is how the &lt;code&gt;preconnect&lt;/code&gt; resource hint would be used for Google Fonts.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preconnect&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://fonts.googleapis.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preconnect&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://fonts.gstatic.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;crossorigin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; &lt;a href=&quot;https://fonts.google.com/&quot;&gt;Google Fonts&lt;/a&gt; provides the option to load fonts via &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags or an &lt;code&gt;@import&lt;/code&gt; statement. The &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; code snippet includes a &lt;code&gt;preconnect&lt;/code&gt; resource hint and therefore will likely result in faster stylesheet delivery than using &lt;code&gt;@import&lt;/code&gt; version. These &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags should be placed as early in the document as possible. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;be-cautious-when-using-preload-to-load-fonts&quot;&gt;Be cautious when using &lt;code&gt;preload&lt;/code&gt; to load fonts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#be-cautious-when-using-preload-to-load-fonts&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Although &lt;code&gt;preload&lt;/code&gt; is highly effective at making fonts discoverable early in the page load process, this comes at the cost of taking away browser resources from the loading of other resources.&lt;/p&gt;
&lt;p&gt;Inlining font declarations and adjusting stylesheets may be a more effective approach. These adjustments come closer to addressing the root cause of late-discovered fonts—rather than just providing a workaround.&lt;/p&gt;
&lt;p&gt;In addition, using &lt;code&gt;preload&lt;/code&gt; as a font-loading strategy should also be used carefully as it bypasses some of the browser&#39;s built-in content negotiation strategies. For example, &lt;code&gt;preload&lt;/code&gt; ignores &lt;code&gt;unicode-range&lt;/code&gt; declarations, and if used prudently, should only be used to load a single font format.&lt;/p&gt;
&lt;p&gt;However, when using external stylesheets, preloading the most important fonts can be very effective since the browser will not otherwise discover whether the font is needed until much later.&lt;/p&gt;
&lt;h2 id=&quot;font-delivery&quot;&gt;Font delivery &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#font-delivery&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Faster font delivery yields faster text rendering. In addition, if a font is delivered early enough, this can help eliminate layout shifts resulting from font swapping.&lt;/p&gt;
&lt;h3 id=&quot;using-self-hosted-fonts&quot;&gt;Using self-hosted fonts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#using-self-hosted-fonts&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;On paper, using a self-hosted font should deliver better performance as it eliminates a third-party connection setup. However, in practice, the performance differences between these two options is less clear cut: for example, the &lt;a href=&quot;https://almanac.httparchive.org/en/2020/fonts#fig-7&quot; rel=&quot;noopener&quot;&gt;Web Almanac&lt;/a&gt; found that sites using third-party fonts had a faster render than fonts that used first-party fonts.&lt;/p&gt;
&lt;p&gt;If you are considering using self-hosted fonts, confirm that your site is using a &lt;a href=&quot;https://web.dev/content-delivery-networks/&quot;&gt;Content Delivery Network (CDN)&lt;/a&gt; and &lt;a href=&quot;https://web.dev/content-delivery-networks/#http2-and-http3&quot;&gt;HTTP/2&lt;/a&gt;. Without use of these technologies, it is much less likely that self-hosted fonts will deliver better performance. For more information, see &lt;a href=&quot;https://web.dev/content-delivery-networks/&quot;&gt;Content Delivery Networks&lt;/a&gt;.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; If you&#39;re unsure if using self-hosted fonts will deliver better performance, try serving a font file from your own servers and compare the transfer time (including connection setup) with that of a third-party font. If you have slow servers, don&#39;t use a CDN, or don&#39;t use HTTP/2 it becomes less likely that the self-hosted font will be more performant. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;If you use a self-hosted font, it is recommended that you also apply some of the font file optimizations that third-party font providers typically provide automatically—for example, font subsetting and WOFF2 compression. The amount of effort required to apply these optimizations will depend somewhat on the languages that your site supports. In particular, be aware that optimizing fonts for &lt;a href=&quot;https://en.wikipedia.org/wiki/CJK_characters&quot; rel=&quot;noopener&quot;&gt;CJK languages&lt;/a&gt; can be particularly challenging.&lt;/p&gt;
&lt;h3 id=&quot;use-woff2&quot;&gt;Use WOFF2 &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#use-woff2&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Of the modern font fonts, &lt;a href=&quot;https://www.w3.org/TR/WOFF2/&quot; rel=&quot;noopener&quot;&gt;WOFF2&lt;/a&gt; is the newest, has the widest browser support, and offers the best compression. Because it uses Brotli, WOFF2 compresses 30% better than WOFF, leading to less data to download and therefore faster performance.&lt;/p&gt;
&lt;p&gt;Given the browser support, experts now recommend only using WOFF2:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;
    In fact, we think it is also time to proclaim: Use only WOFF2 and forget about everything else.&lt;br /&gt;
    &lt;br /&gt;
    This will simplify your CSS and workflow massively and also prevents any accidental double or incorrect font downloads. WOFF2 is now supported everywhere. So, unless you need to support really ancient browsers, just use WOFF2. If you can&#39;t, consider not serving any web fonts to those older browsers at all. This will not be a problem if you have a robust fallback strategy in place. Visitors on older browsers will simply see your fallback fonts.
  &lt;/p&gt;
  &lt;cite&gt;
    &lt;a href=&quot;https://almanac.httparchive.org/en/2022/fonts#performance&quot;&gt;Bram Stein, from the 2022 Web Almanac&lt;/a&gt;
  &lt;/cite&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;subset-fonts&quot;&gt;Subset fonts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#subset-fonts&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Font files typically include a large number of &lt;a href=&quot;https://en.wikipedia.org/wiki/Glyph&quot; rel=&quot;noopener&quot;&gt;glyphs&lt;/a&gt; for all the various characters they support. But you may not need all the characters on your page and can reduce the size of font files by subsetting fonts.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@font-face/unicode-range&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;unicode-range&lt;/code&gt;&lt;/a&gt; descriptor in the &lt;code&gt;@font-face&lt;/code&gt; declartion informs the browser which characters a font can be used for.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Open Sans&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;/fonts/OpenSans-Regular-webfont.woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;unicode-range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; U+0025-00FF&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;A font file will be downloaded if the page contains one or more characters matching the unicode range. &lt;code&gt;unicode-range&lt;/code&gt; is commonly used to serve different font files depending on the language used by page content.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;unicode-range&lt;/code&gt; is often used in conjunction with the technique of subsetting. A subset font includes a smaller portion of the that were contained in the original font file. For example, rather than serve all characters to all users, a site might generate separate subset fonts for Latin and Cyrillic characters. The number of glyphs per font varies wildly: Latin fonts are usually on the magnitude of 100 to 1000 glyphs per font; &lt;a href=&quot;https://en.wikipedia.org/wiki/CJK_characters&quot; rel=&quot;noopener&quot;&gt;CJK&lt;/a&gt; fonts may have over 10,000 characters. Removing unused glyphs can significantly reduce the filesize of a font.&lt;/p&gt;
&lt;p&gt;Some font providers may provide different versions of fonts files with different subsets automatically. For example, &lt;a href=&quot;https://fonts.google.com/&quot; rel=&quot;noopener&quot;&gt;Google Fonts&lt;/a&gt; does this by default:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* devanagari */&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Poppins&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 400&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; swap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJbecnFHGPezSQ.woff2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;woff2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;unicode-range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; U+0900-097F&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+1CD0-1CF6&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+1CF8-1CF9&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+200C-200D&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+20A8&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+20B9&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+25CC&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+A830-A839&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+A8E0-A8FB&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;/* latin-ext */&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Poppins&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 400&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; swap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJnecnFHGPezSQ.woff2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;woff2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;unicode-range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; U+0100-024F&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+0259&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+1E00-1EFF&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+2020&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+20A0-20AB&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+20AD-20CF&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+2113&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+2C60-2C7F&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+A720-A7FF&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;/* latin */&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Poppins&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 400&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; swap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;https://fonts.gstatic.com/s/poppins/v20/pxiEyp8kv8JHgFVrJJfecnFHGPc.woff2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;woff2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;unicode-range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; U+0000-00FF&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+0131&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+0152-0153&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+02BB-02BC&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+02C6&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+02DA&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+02DC&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+2000-206F&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+2074&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+20AC&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+2122&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+2191&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+2193&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+2212&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+2215&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+FEFF&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; U+FFFD&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;When moving to self-hosting, this is an optimization that can easily be missed and lead to larger font files locally.&lt;/p&gt;
&lt;p&gt;It is also possible to manually subset fonts if your font provider allows this, either through an API (&lt;a href=&quot;https://developers.google.com/fonts/docs/getting_started#specifying_script_subsets&quot; rel=&quot;noopener&quot;&gt;Google Fonts supports this by providing a &lt;code&gt;text&lt;/code&gt; parameter&lt;/a&gt;), or by manually editing the font files and then self-hosting. Tools for generating font subsets include &lt;a href=&quot;https://github.com/Munter/subfont&quot; rel=&quot;noopener&quot;&gt;subfont&lt;/a&gt; and &lt;a href=&quot;https://github.com/zachleat/glyphhanger&quot; rel=&quot;noopener&quot;&gt;glyphanger&lt;/a&gt;. However, do &lt;a href=&quot;https://subsetting.xyz/&quot; rel=&quot;noopener&quot;&gt;check the licence for the fonts you use allow subsetting&lt;/a&gt; and self-hosting.&lt;/p&gt;
&lt;h3 id=&quot;use-fewer-web-fonts&quot;&gt;Use fewer web fonts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#use-fewer-web-fonts&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The fastest font to deliver is a font that isn&#39;t requested in the first place. System fonts and variable fonts are two ways to potentially reduce the number of web fonts used on your site.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;system font&lt;/strong&gt; is the default font used by the user interface of a user&#39;s device. System fonts typically vary by operating system and version. Because the font is already installed, the font does not need to be downloaded. System fonts can work particularly well for body text.&lt;/p&gt;
&lt;p&gt;To use the system font in your CSS, list &lt;code&gt;system-ui&lt;/code&gt; as the font-family:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; system-ui&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The idea behind &lt;strong&gt;&lt;a href=&quot;https://web.dev/variable-fonts/&quot;&gt;variable fonts&lt;/a&gt;&lt;/strong&gt; is that a single variable font can be used as a replacement for multiple font files. Variable fonts work by defining a &amp;quot;default&amp;quot; font style and providing &lt;a href=&quot;https://web.dev/variable-fonts/#axes-definitions&quot;&gt;&amp;quot;axes&amp;quot;&lt;/a&gt; for manipulating the font. For example, a variable font with a &lt;code&gt;Weight&lt;/code&gt; axis could be used to implement lettering that would previously require separate fonts for light, regular, bold, and extra bold.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; We often refer to &amp;quot;Times New Roman&amp;quot; and &amp;quot;Helvetica&amp;quot; as fonts. However, technically speaking, these are font &lt;em&gt;families&lt;/em&gt;. A family is composed of styles, which are particular variations of the typeface (for example, light, medium, or bold italic). A font file contains a single style unless it is a variable font. A typeface is the underlying design, which can be expressed as digital fonts—and in physical type, like carved woodblocks or metal. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Not everyone will benefit from switching to variable fonts. &lt;a href=&quot;https://web.dev/variable-fonts/&quot;&gt;Variable fonts&lt;/a&gt; contain many styles, so typically have larger file sizes than individual non-variable fonts that only contain one style. Sites that will see the largest improvement from using variable fonts are those that use (and need to use) a variety of font styles and weights.&lt;/p&gt;
&lt;h2 id=&quot;font-rendering&quot;&gt;Font rendering &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#font-rendering&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When faced with a web font that has not yet loaded, the browser is faced with a dilemma: should it hold off on rendering text until the web font has arrived? Or should it render the text in a fallback font until the web font arrives?&lt;/p&gt;
&lt;p&gt;Different browsers handle this scenario differently. By default, Chromium-based and Firefox browsers will block text rendering for up to 3 seconds if the associated web font has not loaded; Safari will block text rendering indefinitely.&lt;/p&gt;
&lt;p&gt;This behavior can be configured by using the &lt;code&gt;font-display&lt;/code&gt; attribute. This choice can have significant implications: &lt;code&gt;font-display&lt;/code&gt; has the potential to impact LCP, FCP, and layout stability.&lt;/p&gt;
&lt;h3 id=&quot;choose-an-appropriate-font-display-strategy&quot;&gt;Choose an appropriate &lt;code&gt;font-display&lt;/code&gt; strategy &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#choose-an-appropriate-font-display-strategy&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@font-face/font-display&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;font-display&lt;/code&gt;&lt;/a&gt; informs the browser how it should proceed with text rendering when the associated web font has not loaded. It is defined per font-face.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Roboto&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Sans-Serif&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;/fonts/roboto.woff&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;woff&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; swap&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;There are five possible values for &lt;code&gt;font-display&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;&lt;strong&gt;Value&lt;/strong&gt;&lt;/th&gt;
        &lt;th&gt;&lt;strong&gt;Block period&lt;/strong&gt;&lt;/th&gt;
        &lt;th&gt;&lt;strong&gt;Swap period&lt;/strong&gt;&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;Auto&lt;/td&gt;
        &lt;td&gt;Varies by browser&lt;/td&gt;
        &lt;td&gt;Varies by browser&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Block&lt;/td&gt;
        &lt;td&gt;2-3 seconds&lt;/td&gt;
        &lt;td&gt;Infinite&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Swap&lt;/td&gt;
        &lt;td&gt;0ms&lt;/td&gt;
        &lt;td&gt;Infinite&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Fallback&lt;/td&gt;
        &lt;td&gt;100ms&lt;/td&gt;
        &lt;td&gt;3 seconds&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Optional&lt;/td&gt;
        &lt;td&gt;100ms&lt;/td&gt;
        &lt;td&gt;None&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Block period&lt;/strong&gt;: The block period begins when the browser requests a web font. During the block period, if the web font is not available, the font is rendered in an &lt;em&gt;invisible&lt;/em&gt; fallback font and thus the text will not be visible to the user. If the font is not available at the end of the block period, it will be rendered in the fallback font.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Swap period&lt;/strong&gt;: The swap period comes after the block period. If the web font becomes available during the swap period, it will be &amp;quot;swapped&amp;quot; in.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;font-display&lt;/code&gt; strategies reflect different viewpoints about the tradeoff between performance and aesthetics. As such it&#39;s difficult to give a recommended approach as it does depend on individual preferences, how important the web font is to the page and brand, and how jarring a late-arriving font can be when swapped in.&lt;/p&gt;
&lt;p&gt;For most sites, these are the three strategies that will be most applicable:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;If performance is a top priority:&lt;/strong&gt; Use &lt;code&gt;font-display: optional&lt;/code&gt;. This is the most &amp;quot;performant&amp;quot; approach: text render is delayed for no longer than 100ms and there is assurance that there will be no font-swap related layout shifts. However, the downside here is the web font will not be used if it arrives late.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;If displaying text quickly is a top priority, but you want to still ensure the web-font is used:&lt;/strong&gt; Use &lt;code&gt;font-display: swap&lt;/code&gt; but make sure to deliver the font early enough that it does not cause a layout shift. The downside of this option is the jarring shift when the font arrives late.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;If ensuring text is displayed in a web font is a top priority:&lt;/strong&gt; Use &lt;code&gt;font-display: block&lt;/code&gt; but make sure to deliver the font early enough that it minimises the delay of the text. The downside of this is the initial text display will be delayed. Note despite this deplay, it can still cause a layout shift as the text is actually drawn invisible, and the fallback font space is therefore user to reserver the space. Once the web font loads, this may require difference space and hence a shift. This may, however, be a less jarring shift than &lt;code&gt;font-display: swap&lt;/code&gt; as the text itself will not be seen to shift.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; &lt;code&gt;font-display: auto&lt;/code&gt;, &lt;code&gt;font-display: block&lt;/code&gt;, &lt;code&gt;font-display: swap&lt;/code&gt;, and &lt;code&gt;font-display: fallback&lt;/code&gt; all have the potential to cause layout shifts when the font is swapped. However, of these approaches, &lt;code&gt;font-display: swap&lt;/code&gt; will delay text render the least. Thus, it can be the preferred approach for situations where it is important that text is displayed as quickly as possible, but ultimately gets rendered as a web font. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Also keep in mind that these two approaches can be combined: for example, use &lt;code&gt;font-display: swap&lt;/code&gt; for branding and other visually distinctive page elements; use &lt;code&gt;font-display: optional&lt;/code&gt; for fonts used in body text.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The &lt;code&gt;font-display&lt;/code&gt; strategies that work well for traditional web fonts don&#39;t work nearly as well for icon fonts. The fallback font for an icon font typically looks significantly different than the icon font and its characters may convey a completely different meaning. As a result, icon fonts are more likely to cause significant layout shifts. In addition, using a fallback font may not be practical. If possible, it&#39;s best to replace icon fonts with SVG (this is also better for accessibility). Newer versions of popular icon fonts typically support SVG. For more information on switching to SVG, see &lt;a href=&quot;https://fontawesome.com/v5.15/how-to-use/on-the-web/advanced/svg-sprites&quot;&gt;Font Awesome&lt;/a&gt; and &lt;a href=&quot;https://google.github.io/material-design-icons/#svg&quot;&gt;Material Icons&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;reduce-the-shift-between-your-fallback-font-and-your-webfont&quot;&gt;Reduce the shift between your fallback font and your webfont &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#reduce-the-shift-between-your-fallback-font-and-your-webfont&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To reduce the CLS impact, you can use the new &lt;code&gt;size-adjust&lt;/code&gt; attributes. For more information see &lt;a href=&quot;https://web.dev/css-size-adjust/&quot;&gt;this article&lt;/a&gt;. This is a very new addition to our toolset, so is more advanced and a bit manual at present. But definitely one to experiment with and watch for tooling improvements in the future!&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/font-best-practices/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Web fonts still be a performance bottleneck but we have an ever-growing range of options to allow us to optimize them to reduce this bottleneck as much as possible.&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author><author>
      <name>Barry Pollard</name>
    </author>
  </entry>
  
  <entry>
    <title>CSS for Web Vitals</title>
    <link href="https://web.dev/css-web-vitals/"/>
    <updated>2021-06-02T00:00:00Z</updated>
    <id>https://web.dev/css-web-vitals/</id>
    <content type="html" mode="escaped">&lt;p&gt;The way you write your styles and build layouts can have a major impact on &lt;a href=&quot;https://web.dev/learn-core-web-vitals/&quot;&gt;Core
Web Vitals&lt;/a&gt;. This is particularly true for
&lt;a href=&quot;https://web.dev/cls&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt; and &lt;a href=&quot;https://web.dev/lcp&quot;&gt;Largest Contentful
Paint (LCP)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This article covers CSS-related techniques for optimizing Web Vitals. These
optimizations are broken down by different aspects of a page: layout, images,
fonts, animations, and loading. Along the way, we&#39;ll explore improving an
&lt;a href=&quot;https://codepen.io/una/pen/vYyLKvY&quot; rel=&quot;noopener&quot;&gt;example page&lt;/a&gt;:&lt;/p&gt;
&lt;img alt=&quot;Screenshot of example site&quot; decoding=&quot;async&quot; height=&quot;646&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/pgmpMOmweK7BVBsVkQ5g.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/pgmpMOmweK7BVBsVkQ5g.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/pgmpMOmweK7BVBsVkQ5g.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/pgmpMOmweK7BVBsVkQ5g.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/pgmpMOmweK7BVBsVkQ5g.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/pgmpMOmweK7BVBsVkQ5g.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/pgmpMOmweK7BVBsVkQ5g.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/pgmpMOmweK7BVBsVkQ5g.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/pgmpMOmweK7BVBsVkQ5g.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/pgmpMOmweK7BVBsVkQ5g.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/pgmpMOmweK7BVBsVkQ5g.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/pgmpMOmweK7BVBsVkQ5g.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/pgmpMOmweK7BVBsVkQ5g.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/pgmpMOmweK7BVBsVkQ5g.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/pgmpMOmweK7BVBsVkQ5g.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/pgmpMOmweK7BVBsVkQ5g.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/pgmpMOmweK7BVBsVkQ5g.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/pgmpMOmweK7BVBsVkQ5g.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h2 id=&quot;layout&quot;&gt;Layout &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-web-vitals/#layout&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;inserting-content-into-the-dom&quot;&gt;Inserting content into the DOM &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-web-vitals/#inserting-content-into-the-dom&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Inserting content into a page after the surrounding content has already loaded
pushes everything else on the page down. This causes &lt;a href=&quot;https://web.dev/cls/#layout-shifts-in-detail&quot;&gt;layout
shifts&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/cookie-notice-best-practices/&quot;&gt;Cookie notices&lt;/a&gt;, particularly
those placed at the top of the page, are a common example of this problem. Other
page elements that often cause this type of layout shift when they load include
ads and embeds.&lt;/p&gt;
&lt;h4 id=&quot;identify&quot;&gt;Identify &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-web-vitals/#identify&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The Lighthouse &amp;quot;Avoid large layout shifts&amp;quot; audit identifies page elements that
have shifted. For this demo, the results look like this:&lt;/p&gt;
&lt;img alt=&quot;Lighthouse&amp;#x27;s &amp;#x27;Avoid large layout shifts&amp;#x27; audit&quot; decoding=&quot;async&quot; height=&quot;500&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/jaHtgwzDXCjx3vAFOO33.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/jaHtgwzDXCjx3vAFOO33.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/jaHtgwzDXCjx3vAFOO33.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/jaHtgwzDXCjx3vAFOO33.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/jaHtgwzDXCjx3vAFOO33.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/jaHtgwzDXCjx3vAFOO33.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/jaHtgwzDXCjx3vAFOO33.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/jaHtgwzDXCjx3vAFOO33.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/jaHtgwzDXCjx3vAFOO33.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/jaHtgwzDXCjx3vAFOO33.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/jaHtgwzDXCjx3vAFOO33.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/jaHtgwzDXCjx3vAFOO33.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/jaHtgwzDXCjx3vAFOO33.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/jaHtgwzDXCjx3vAFOO33.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/jaHtgwzDXCjx3vAFOO33.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/jaHtgwzDXCjx3vAFOO33.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/jaHtgwzDXCjx3vAFOO33.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/jaHtgwzDXCjx3vAFOO33.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;The cookie notice is not listed in these findings because the cookie notice
itself isn&#39;t shifting when it loads. Rather, it causes the items below it on the
page (that is, &lt;code&gt;div.hero&lt;/code&gt; and &lt;code&gt;article&lt;/code&gt;) to shift. For more information on
identifying and fixing layout shifts, see &lt;a href=&quot;https://web.dev/debugging-layout-shifts&quot;&gt;Debugging Layout
Shifts&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;  Lighthouse only analyzes a page&#39;s performance up until the &amp;quot;page load&amp;quot; event. Cookie banners, ads, and other widgets sometimes do not load until after page load. These layout shifts still affect users—even if they are not flagged by Lighthouse.  &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-web-vitals/#fix&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Place the cookie notice at the bottom of the page using absolute or fixed
positioning.&lt;/p&gt;
&lt;img alt=&quot;Cookie notice displayed at bottom of page&quot; decoding=&quot;async&quot; height=&quot;656&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/YBYLT9jJ9AXrbsaRNVoa.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/YBYLT9jJ9AXrbsaRNVoa.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/YBYLT9jJ9AXrbsaRNVoa.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/YBYLT9jJ9AXrbsaRNVoa.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/YBYLT9jJ9AXrbsaRNVoa.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/YBYLT9jJ9AXrbsaRNVoa.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/YBYLT9jJ9AXrbsaRNVoa.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/YBYLT9jJ9AXrbsaRNVoa.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/YBYLT9jJ9AXrbsaRNVoa.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/YBYLT9jJ9AXrbsaRNVoa.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/YBYLT9jJ9AXrbsaRNVoa.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/YBYLT9jJ9AXrbsaRNVoa.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/YBYLT9jJ9AXrbsaRNVoa.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/YBYLT9jJ9AXrbsaRNVoa.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/YBYLT9jJ9AXrbsaRNVoa.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/YBYLT9jJ9AXrbsaRNVoa.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/YBYLT9jJ9AXrbsaRNVoa.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/YBYLT9jJ9AXrbsaRNVoa.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Before:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.banner&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;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; sticky&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&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;After:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.banner&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;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fixed&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&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;Another way to fix this layout shift would be to reserve space for the cookie
notice at the top of the screen. This approach is equally effective. For more
information, see &lt;a href=&quot;https://web.dev/cookie-notice-best-practices/&quot;&gt;Cookie notice best
practices&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;  The cookie notice is one of multiple page elements that are triggering layout shifts when it loads. To help us get a closer look at these page elements, subsequent demo steps will not include the cookie notice.  &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;images&quot;&gt;Images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-web-vitals/#images&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;images-and-largest-contentful-paint-lcp&quot;&gt;Images and Largest Contentful Paint (LCP) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-web-vitals/#images-and-largest-contentful-paint-lcp&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Images are commonly the Largest Contentful Paint (LCP) element on a page. Other
&lt;a href=&quot;https://web.dev/lcp/#what-elements-are-considered&quot;&gt;page elements that can be the LCP
element&lt;/a&gt; include text blocks
and video poster images. The time at which the LCP element loads determines LCP.&lt;/p&gt;
&lt;p&gt;It&#39;s important to note that a page&#39;s LCP element can vary from page load to page
load depending on the content that is visible to the user when the page is first
displayed. For example, in this demo, the background of the cookie notice, the
hero image, and the article text are some of the potential LCP elements.&lt;/p&gt;
&lt;img alt=&quot;Diagram highlighting the page&amp;#x27;s LCP element in different scenarios.&quot; decoding=&quot;async&quot; height=&quot;498&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/bMoAoohyLOgTqV6B7lHr.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/bMoAoohyLOgTqV6B7lHr.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/bMoAoohyLOgTqV6B7lHr.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/bMoAoohyLOgTqV6B7lHr.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/bMoAoohyLOgTqV6B7lHr.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/bMoAoohyLOgTqV6B7lHr.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/bMoAoohyLOgTqV6B7lHr.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/bMoAoohyLOgTqV6B7lHr.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/bMoAoohyLOgTqV6B7lHr.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/bMoAoohyLOgTqV6B7lHr.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/bMoAoohyLOgTqV6B7lHr.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/bMoAoohyLOgTqV6B7lHr.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/bMoAoohyLOgTqV6B7lHr.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/bMoAoohyLOgTqV6B7lHr.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/bMoAoohyLOgTqV6B7lHr.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/bMoAoohyLOgTqV6B7lHr.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/bMoAoohyLOgTqV6B7lHr.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/bMoAoohyLOgTqV6B7lHr.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;In the example site, the background image of the cookie notice is actually a
large image. To improve LCP, you could instead paint the gradient in CSS, rather
than load an image to create the effect.&lt;/p&gt;
&lt;h4 id=&quot;fix-2&quot;&gt;Fix &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-web-vitals/#fix-2&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Change the &lt;code&gt;.banner&lt;/code&gt; CSS to use a CSS gradient rather than an image:&lt;/p&gt;
&lt;p&gt;Before:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;https://cdn.pixabay.com/photo/2015/07/15/06/14/gradient-845701\_960\_720.jpg&quot;&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;After:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;135deg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #fbc6ff 20%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; #bdfff9 90%&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;images-and-layout-shifts&quot;&gt;Images and layout shifts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-web-vitals/#images-and-layout-shifts&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Browsers can only determine the size of an image once the image loads. If the
image load occurs after the page has been rendered, but no space has been
reserved for the image, a layout shift occurs when the image appears. In the
demo, the hero image is causing a layout shift when it loads.&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 phenomenon of images causing layout shifts is more obvious in situations where images are slow to load - for example, on a slow connection or when loading an image with a particularly large file size. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;identify-2&quot;&gt;Identify &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-web-vitals/#identify-2&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;To identify images without explicit &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt;, use Lighthouse&#39;s
&amp;quot;Image elements have explicit width and height&amp;quot; audit.&lt;/p&gt;
&lt;img alt=&quot;Lighthouse&amp;#x27;s &amp;#x27;Image elements have explicit width and height&amp;#x27; audit&quot; decoding=&quot;async&quot; height=&quot;274&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wDGRVi7JaUOTjD9ODOk9.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wDGRVi7JaUOTjD9ODOk9.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wDGRVi7JaUOTjD9ODOk9.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wDGRVi7JaUOTjD9ODOk9.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wDGRVi7JaUOTjD9ODOk9.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wDGRVi7JaUOTjD9ODOk9.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wDGRVi7JaUOTjD9ODOk9.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wDGRVi7JaUOTjD9ODOk9.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wDGRVi7JaUOTjD9ODOk9.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wDGRVi7JaUOTjD9ODOk9.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wDGRVi7JaUOTjD9ODOk9.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wDGRVi7JaUOTjD9ODOk9.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wDGRVi7JaUOTjD9ODOk9.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wDGRVi7JaUOTjD9ODOk9.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wDGRVi7JaUOTjD9ODOk9.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wDGRVi7JaUOTjD9ODOk9.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wDGRVi7JaUOTjD9ODOk9.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wDGRVi7JaUOTjD9ODOk9.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;In this example, both the hero image and article image are missing &lt;code&gt;width&lt;/code&gt; and
&lt;code&gt;height&lt;/code&gt; attributes.&lt;/p&gt;
&lt;h4 id=&quot;fix-3&quot;&gt;Fix &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-web-vitals/#fix-3&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Set the &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes on these images to avoid layout shifts.&lt;/p&gt;
&lt;p&gt;Before:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://source.unsplash.com/random/2000x600&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image to load in&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://source.unsplash.com/random/800x600&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image to load in&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;After:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://source.unsplash.com/random/2000x600&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;2000&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;600&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image to load in&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://source.unsplash.com/random/800x600&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;800&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;600&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image to load in&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;figure&gt;
  &lt;video&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/j2RDdG43oidUy6AL6LovThjeX9c2/fLUscMGOlGhKnNHef2py.mp4&quot; type=&quot;video/mp4&quot; /&gt;    &lt;/video&gt;
  &lt;figcaption&gt;
    The image now loads without causing a layout shift.
  &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; Another approach to loading images is to use the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/HTMLImageElement/srcset&quot;&gt;&lt;code&gt;srcset&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/HTMLImageElement/sizes&quot;&gt;&lt;code&gt;sizes&lt;/code&gt;&lt;/a&gt; attributes in conjunction with specifying &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes. This has the additional performance advantage of allowing you to serve different sized images to different devices. For more information, see &lt;a href=&quot;https://web.dev/serve-responsive-images/&quot;&gt;Serve responsive images&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;fonts&quot;&gt;Fonts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-web-vitals/#fonts&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Fonts can delay text rendering and cause layout shifts. As a result, it is
important to deliver fonts quickly.&lt;/p&gt;
&lt;h3 id=&quot;delayed-text-rendering&quot;&gt;Delayed text rendering &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-web-vitals/#delayed-text-rendering&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;By default, a browser will not immediately render a text element if its
associated web fonts have not loaded yet. This is done to prevent a &lt;a href=&quot;https://en.wikipedia.org/wiki/Flash_of_unstyled_content&quot; rel=&quot;noopener&quot;&gt;&amp;quot;flash of
unstyled text&amp;quot; (FOUT)&lt;/a&gt;.
In many situations, this delays &lt;a href=&quot;https://web.dev/fcp&quot;&gt;First Contentful Paint
(FCP)&lt;/a&gt;. In some situations, this delays Largest Contentful
Paint (LCP).&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, Chromium-based and Firefox browsers will &lt;a href=&quot;https://developer.chrome.com/blog/font-display/&quot;&gt;block text rendering for up to 3 seconds&lt;/a&gt; if the associated web font has not loaded; Safari will block text rendering indefinitely. The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@font-face/font-display#the_font_display_timeline&quot;&gt;block period&lt;/a&gt; begins when the browser requests a web font. If the font has still not loaded by the end of the block period, the browser will render the text using a fallback font and swap in the web font once available.  &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;layout-shifts&quot;&gt;Layout shifts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-web-vitals/#layout-shifts&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Font swapping, while excellent for displaying content to the user quickly, has
the potential to cause layout shifts. These layout shifts occur when a web font
and its fallback font take up different amounts of space on the page. Using
similarly proportioned fonts will minimize the size of these layout shifts.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Diagram showing a layout shift caused by a font swap&quot; decoding=&quot;async&quot; height=&quot;452&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    In this example, font swapping caused page elements to shift upwards by five pixels.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h4 id=&quot;identify-3&quot;&gt;Identify &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-web-vitals/#identify-3&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;To see the fonts that are being loaded on a particular page, open the
&lt;strong&gt;Network&lt;/strong&gt; tab in DevTools and filter by &lt;strong&gt;Font&lt;/strong&gt;. Fonts can be large files, so
only using fewer fonts is generally better for performance.&lt;/p&gt;
&lt;img alt=&quot;Screenshot of a font displayed in DevTools&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/j2RDdG43oidUy6AL6LovThjeX9c2/Ts38bQtR6x0SDgufA9vz.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Ts38bQtR6x0SDgufA9vz.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Ts38bQtR6x0SDgufA9vz.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Ts38bQtR6x0SDgufA9vz.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Ts38bQtR6x0SDgufA9vz.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Ts38bQtR6x0SDgufA9vz.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Ts38bQtR6x0SDgufA9vz.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Ts38bQtR6x0SDgufA9vz.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Ts38bQtR6x0SDgufA9vz.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Ts38bQtR6x0SDgufA9vz.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Ts38bQtR6x0SDgufA9vz.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Ts38bQtR6x0SDgufA9vz.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Ts38bQtR6x0SDgufA9vz.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Ts38bQtR6x0SDgufA9vz.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Ts38bQtR6x0SDgufA9vz.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Ts38bQtR6x0SDgufA9vz.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Ts38bQtR6x0SDgufA9vz.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Ts38bQtR6x0SDgufA9vz.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;To see how long it takes for the font to be requested, click on the &lt;strong&gt;Timing&lt;/strong&gt;
tab. The sooner that a font is requested, the sooner it can be loaded and used.&lt;/p&gt;
&lt;img alt=&quot;Screenshot of &amp;#x27;Timing&amp;#x27; tab in DevTools&quot; decoding=&quot;async&quot; height=&quot;340&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wfS7qVThKMkGA7SHd439.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wfS7qVThKMkGA7SHd439.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wfS7qVThKMkGA7SHd439.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wfS7qVThKMkGA7SHd439.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wfS7qVThKMkGA7SHd439.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wfS7qVThKMkGA7SHd439.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wfS7qVThKMkGA7SHd439.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wfS7qVThKMkGA7SHd439.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wfS7qVThKMkGA7SHd439.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wfS7qVThKMkGA7SHd439.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wfS7qVThKMkGA7SHd439.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wfS7qVThKMkGA7SHd439.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wfS7qVThKMkGA7SHd439.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wfS7qVThKMkGA7SHd439.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wfS7qVThKMkGA7SHd439.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wfS7qVThKMkGA7SHd439.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wfS7qVThKMkGA7SHd439.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/wfS7qVThKMkGA7SHd439.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;To see the request chain for a font, click on the  &lt;strong&gt;Initiator&lt;/strong&gt; tab.
Generally speaking, the shorter the request chain, the sooner the font can be
requested.&lt;/p&gt;
&lt;img alt=&quot;Screenshot of &amp;#x27;Initiator&amp;#x27; tab in DevTools&quot; decoding=&quot;async&quot; height=&quot;189&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/0tau1GQnZfj5vPhzwnIQ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/0tau1GQnZfj5vPhzwnIQ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/0tau1GQnZfj5vPhzwnIQ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/0tau1GQnZfj5vPhzwnIQ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/0tau1GQnZfj5vPhzwnIQ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/0tau1GQnZfj5vPhzwnIQ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/0tau1GQnZfj5vPhzwnIQ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/0tau1GQnZfj5vPhzwnIQ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/0tau1GQnZfj5vPhzwnIQ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/0tau1GQnZfj5vPhzwnIQ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/0tau1GQnZfj5vPhzwnIQ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/0tau1GQnZfj5vPhzwnIQ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/0tau1GQnZfj5vPhzwnIQ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/0tau1GQnZfj5vPhzwnIQ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/0tau1GQnZfj5vPhzwnIQ.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/0tau1GQnZfj5vPhzwnIQ.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/0tau1GQnZfj5vPhzwnIQ.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/0tau1GQnZfj5vPhzwnIQ.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h4 id=&quot;fix-4&quot;&gt;Fix &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-web-vitals/#fix-4&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;This demo uses the Google Fonts API. Google Fonts provides the option to load
fonts via &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags or an &lt;code&gt;@import&lt;/code&gt; statement. The &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; code snippet
includes a &lt;code&gt;preconnect&lt;/code&gt; resource hint. This should result in faster
stylesheet delivery than using the &lt;code&gt;@import&lt;/code&gt; version.&lt;/p&gt;
&lt;p&gt;At a very high-level, you can think of &lt;a href=&quot;https://www.w3.org/TR/resource-hints/#resource-hints&quot; rel=&quot;noopener&quot;&gt;resource
hints&lt;/a&gt; as a way to hint
to the browser that it will need to set up a particular connection or download a
particular resource. As a result, the browser will prioritize these actions.
When using resource hints, keep in mind that prioritizing a particular action
takes away browser resources from other actions. Thus, resource hints should be
used thoughtfully and not for everything. For more information, see &lt;a href=&quot;https://web.dev/preconnect-and-dns-prefetch/&quot;&gt;Establish
network connections early to improve perceived page
speed&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Remove the following &lt;code&gt;@import&lt;/code&gt; statement from your stylesheet:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@import&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&#39;https://fonts.googleapis.com/css2?family=Montserrat:wght@400&amp;amp;family=Roboto:wght@300&amp;amp;display=swap&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Add the following &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags 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;preconnect&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://fonts.googleapis.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preconnect&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://fonts.gstatic.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;crossorigin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://fonts.googleapis.com/css2?family=Roboto:wght@100&amp;amp;display=swap&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;These link tags instruct the browser to establish an early connection to
the origins used by Google Fonts and to load the stylesheet that
contains the font declaration for Montserrat and Roboto. These &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags
should be placed as early in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; as possible.&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;  To only load a subset of a font from Google Fonts, add the &lt;a href=&quot;https://developers.google.com/fonts/docs/getting_started&quot;&gt;&lt;code&gt;?text=&lt;/code&gt;&lt;/a&gt; API parameter. For example, &lt;code&gt;?text=ABC&lt;/code&gt; loads only the characters necessary to render &amp;quot;ABC&amp;quot;. This is a good way to reduce the file size of a font.  &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;animations&quot;&gt;Animations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-web-vitals/#animations&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The primary way that animations affect Web Vitals is when they cause layout
shifts. There are two types of animations that you should avoid using:
&lt;a href=&quot;https://web.dev/animations-guide/#triggers&quot;&gt;animations that trigger layout&lt;/a&gt; and
&amp;quot;animation-like&amp;quot; effects that move page elements. Typically these animations can
be replaced with more performant equivalents by using CSS properties like
&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;,
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/opacity&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;opacity&lt;/code&gt;&lt;/a&gt;, and
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/filter&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;filter&lt;/code&gt;&lt;/a&gt;. For more
information, see &lt;a href=&quot;https://web.dev/animations/&quot;&gt;How to create high-performance CSS
animations&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;identify-4&quot;&gt;Identify &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-web-vitals/#identify-4&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Lighthouse &amp;quot;Avoid non-composited animations&amp;quot; audit may be helpful for
identifying non-performant animations.&lt;/p&gt;
&lt;img alt=&quot;Lighthouse&amp;#x27;s &amp;#x27;Avoid non-composited animations&amp;#x27; audit&quot; decoding=&quot;async&quot; height=&quot;132&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 512px) 512px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/mXgypW9x3qgvmWDLbIZx.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/mXgypW9x3qgvmWDLbIZx.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/mXgypW9x3qgvmWDLbIZx.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/mXgypW9x3qgvmWDLbIZx.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/mXgypW9x3qgvmWDLbIZx.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/mXgypW9x3qgvmWDLbIZx.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/mXgypW9x3qgvmWDLbIZx.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/mXgypW9x3qgvmWDLbIZx.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/mXgypW9x3qgvmWDLbIZx.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/mXgypW9x3qgvmWDLbIZx.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/mXgypW9x3qgvmWDLbIZx.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/mXgypW9x3qgvmWDLbIZx.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/mXgypW9x3qgvmWDLbIZx.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/mXgypW9x3qgvmWDLbIZx.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/mXgypW9x3qgvmWDLbIZx.png?auto=format&amp;w=1024 1024w&quot; width=&quot;512&quot; /&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 Lighthouse &amp;quot;Avoid non-composited animations&amp;quot; audit only identifies non-performant &lt;em&gt;CSS animations&lt;/em&gt;; JavaScript-driven animations (for example, using &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/WindowOrWorkerGlobalScope/setInterval&quot;&gt;&lt;code&gt;setInterval()&lt;/code&gt;&lt;/a&gt; to &amp;quot;animate&amp;quot; an element) are bad for performance but will not be flagged by this audit.  &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;fix-5&quot;&gt;Fix &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-web-vitals/#fix-5&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Change the &lt;code&gt;slideIn&lt;/code&gt; animation sequence to use &lt;code&gt;transform: translateX()&lt;/code&gt; rather
than transitioning the&lt;code&gt;margin-left&lt;/code&gt; property.&lt;/p&gt;
&lt;p&gt;Before:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.header&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;animation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; slideIn 1s 1 ease&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; slideIn&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token selector&quot;&gt;from&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;margin-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token selector&quot;&gt;to&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;margin-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;After:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.header&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;animation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; slideIn 1s 1 ease&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@keyframes&lt;/span&gt; slideIn&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token selector&quot;&gt;from&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;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;translateX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;-100%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token selector&quot;&gt;to&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;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;translateX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;critical-css&quot;&gt;Critical CSS &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-web-vitals/#critical-css&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Stylesheets are render-blocking. This means that the browser encounters a
stylesheet, it will stop downloading other resources until the browser has
downloaded and parsed the stylesheet. This may delay LCP. To improve
performance, consider &lt;a href=&quot;https://css-tricks.com/how-do-you-remove-unused-css-from-a-site/&quot; rel=&quot;noopener&quot;&gt;removing unused
CSS&lt;/a&gt;,
&lt;a href=&quot;https://web.dev/extract-critical-css/&quot;&gt;inlining critical CSS&lt;/a&gt;, and &lt;a href=&quot;https://web.dev/defer-non-critical-css/#optimize&quot;&gt;deferring
non-critical CSS&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/css-web-vitals/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Although there is still room for further improvements (for example, using &lt;a href=&quot;https://web.dev/use-imagemin-to-compress-images/&quot;&gt;image
compression&lt;/a&gt; to deliver images
more quickly), these changes have significantly improved the Web Vitals of this
site. If this were a real site, the next step would be to &lt;a href=&quot;https://web.dev/vitals-measurement-getting-started/#measuring-web-vitals-using-rum-data&quot;&gt;collect performance
data from real
users&lt;/a&gt;
to assess whether it is &lt;a href=&quot;https://web.dev/vitals-measurement-getting-started/#data-interpretation&quot;&gt;meeting the Web Vitals thresholds for most
users&lt;/a&gt;.
For more information about Web Vitals, see &lt;a href=&quot;https://web.dev/learn-core-web-vitals/&quot;&gt;Learn Web
Vitals&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author><author>
      <name>Una Kravets</name>
    </author>
  </entry>
  
  <entry>
    <title>Best practices for cookie notices</title>
    <link href="https://web.dev/cookie-notice-best-practices/"/>
    <updated>2021-03-30T00:00:00Z</updated>
    <id>https://web.dev/cookie-notice-best-practices/</id>
    <content type="html" mode="escaped">&lt;p&gt;This article discusses how cookie notices can affect performance, performance
measurement, and user experience.&lt;/p&gt;
&lt;h2 id=&quot;performance&quot;&gt;Performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#performance&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Cookie notices can have a significant impact on page performance due to
the fact that they are typically loaded early in the page load process, are
shown to all users, and can potentially influence the loading of ads and other
page content.&lt;/p&gt;
&lt;p&gt;Here&#39;s how cookie notices can impact Web Vitals metrics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Largest Contentful Paint (LCP):&lt;/strong&gt;
Most cookie consent notices are fairly small and therefore typically don&#39;t
contain a page&#39;s LCP element. However, this can happen—particularly on mobile
devices. On mobile devices, a cookie notice typically takes up a larger portion
of the screen. This usually occurs when a cookie notice contains a large
block of text (text blocks can be LCP elements too).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;First Input Delay (FID):&lt;/strong&gt;
Generally speaking, your cookie consent solution in and of itself should have a
minimal impact on FID—cookie consent requires little JavaScript execution.
However, the technologies that these cookies enable—namely advertising and
tracking scripts—may have a significant impact on page interactivity. Delaying
these scripts until cookie acceptance can serve as a technique to decrease
First Input Delay (FID).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cumulative Layout Shift (CLS):&lt;/strong&gt;
Cookie consent notices are a very common source of layout shifts.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Generally speaking, you can expect a cookie notice from third-party providers to
have a greater impact on performance than a cookie notice that you build
yourself. This is not a problem unique to cookie notices—but rather the nature
of third-party scripts in general.&lt;/p&gt;
&lt;h3 id=&quot;best-practices&quot;&gt;Best practices &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#best-practices&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The best practices in this section focus on third-party cookie notices. Some,
but not all, of these best practices will also be applicable to first-party
cookie notices.&lt;/p&gt;
&lt;h4 id=&quot;load-cookie-notices-scripts-asynchronously&quot;&gt;Load cookie notices scripts asynchronously &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#load-cookie-notices-scripts-asynchronously&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Cookie notice scripts should be loaded asynchronously. To do this, add the
&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;
attribute to the script tag.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;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://cookie-notice.com/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;async&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Scripts that are not asynchronous block the browser parser. This delays page
load and LCP. For more information, see &lt;a href=&quot;https://web.dev/efficiently-load-third-party-javascript/&quot;&gt;Efficiently load third-party
JavaScript&lt;/a&gt;.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; If you must use synchronous scripts (for example, some cookie notices rely on synchronous scripts to implement cookie blocking) you should make sure that this request loads as quickly as possible. One way to do this is to &lt;a href=&quot;https://web.dev/preconnect-and-dns-prefetch/&quot;&gt;use resource hints&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;load-cookie-notice-scripts-directly&quot;&gt;Load cookie notice scripts directly &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#load-cookie-notice-scripts-directly&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Cookie notice scripts should be loaded &amp;quot;directly&amp;quot; by placing the script tag in
the HTML of the main document—rather than loaded by a tag manager or other
script. Using a tag manager or secondary script to inject the cookie notice
script delays the loading of the cookie notice script: it obscures the script
from the browser&#39;s lookahead parser and it prevents the script from loading
before JavaScript execution.&lt;/p&gt;
&lt;h4 id=&quot;establish-an-early-connection-with-the-cookie-notice-origin&quot;&gt;Establish an early connection with the cookie notice origin &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#establish-an-early-connection-with-the-cookie-notice-origin&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;All sites that load their cookie notice scripts from a third-party location
should use either the &lt;code&gt;dns-prefetch&lt;/code&gt; or &lt;code&gt;preconnect&lt;/code&gt; resource hints to help
establish an early connection with the origin that hosts cookie notice
resources. For more information, see &lt;a href=&quot;https://web.dev/preconnect-and-dns-prefetch/&quot;&gt;Establish network connections early to
improve perceived page speed&lt;/a&gt;.&lt;/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.cookie-notice.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-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; It is common for cookie notices to load resources from multiple origins— for example, loading resources from both &lt;code&gt;www.cookie-notice.com&lt;/code&gt; and &lt;code&gt;cdn.cookie-notice.com&lt;/code&gt;. Separate origins require separate connections and therefore separate resource hints. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;preload-cookie-notices-as-appropriate&quot;&gt;Preload cookie notices as appropriate &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#preload-cookie-notices-as-appropriate&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Some sites would benefit from using the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Preloading_content&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;preload&lt;/code&gt;&lt;/a&gt;
resource hint to load their cookie notice script. The &lt;code&gt;preload&lt;/code&gt; resource hint
informs the browser to initiate an early request for the specified resource.&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;https://www.cookie-notice.com/cookie-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;&lt;code&gt;preload&lt;/code&gt; is most powerful when its usage is limited to fetching a couple key
resources per page. Thus, the usefulness of preloading the cookie notice script
will vary depending on the situation.&lt;/p&gt;
&lt;h4 id=&quot;be-aware-of-performance-tradeoffs-when-styling-cookie-notices&quot;&gt;Be aware of performance tradeoffs when styling cookie notices &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#be-aware-of-performance-tradeoffs-when-styling-cookie-notices&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Customizing the look and feel of a third-party cookie notice may incur
additional performance costs. For example, third-party cookie notices aren&#39;t
always able to reuse the same resources (for example, web fonts) that are used
elsewhere on the page. In addition, third-party cookie notices tend to load
styling at the end of long request chains. To avoid any surprises, be aware of
how your cookie notice loads and applies styling and related resources.&lt;/p&gt;
&lt;h4 id=&quot;avoid-layout-shifts&quot;&gt;Avoid layout shifts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#avoid-layout-shifts&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;These are some of the most common layout shift issues associated with cookie
notices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Top-of-screen cookie notices:&lt;/strong&gt; Top-of-screen cookie notices are a very
common source of layout shift. If a cookie notice is inserted into the DOM
after the surrounding page has already rendered, it will push the page
elements below it further down the page. This type of layout shift can be
eliminated by reserving space in the DOM for the consent notice. If this is
not a feasible solution—for example, if the dimensions of your cookie
notice vary by geography, consider using a sticky footer or modal to
display the cookie notice. Because both of these alternative approaches
display the cookie notice as an &amp;quot;overlay&amp;quot; on top of the rest of the page,
the cookie notice should not cause content shifts when it loads.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Animations&lt;/strong&gt;: Many cookie notices use animations—for example, &amp;quot;sliding in&amp;quot;
a cookie notice is a common design pattern. Depending on how these effects
are implemented, they can cause layout shifts. For more information, see
&lt;a href=&quot;https://web.dev/debugging-layout-shifts/&quot;&gt;Debugging layout shifts&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fonts&lt;/strong&gt;: Late-loading fonts can block render and or cause layout shifts.
This phenomena is more apparent on slow connections.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;advanced-loading-optimizations&quot;&gt;Advanced loading optimizations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#advanced-loading-optimizations&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;These techniques take more work to implement but can further optimize the
loading of cookie notice scripts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Caching and serving third-party cookie notice scripts from your own servers
can improve the delivery speed of these resources.&lt;/li&gt;
&lt;li&gt;Using &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Service_Worker_API/Using_Service_Workers&quot; rel=&quot;noopener&quot;&gt;service
workers&lt;/a&gt;
can allow you more control over the &lt;a href=&quot;https://developer.chrome.com/docs/workbox/caching-resources-during-runtime/#cross-origin-considerations&quot; rel=&quot;noopener&quot;&gt;fetching and caching of
third-party
scripts&lt;/a&gt;
such as cookie notice scripts.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;performance-measurement&quot;&gt;Performance measurement &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#performance-measurement&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Cookie notices can impact performance measurements. This section discusses some
of these implications and techniques for mitigating them.&lt;/p&gt;
&lt;h3 id=&quot;real-user-monitoring-rum&quot;&gt;Real User Monitoring (RUM) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#real-user-monitoring-rum&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Some analytics and RUM tools use cookies to collect performance data. In the
event that a user declines usage of cookies these tools cannot capture
performance data.&lt;/p&gt;
&lt;p&gt;Sites should be aware of this phenomenon; it is also worthwhile to understand
the mechanisms that your RUM tooling uses to collect its data. However, for the
typical site this discrepancy probably isn&#39;t a cause for alarm given the
direction and magnitude of the data skew. Cookie usage is not a technical
requirement for performance measurement. The
&lt;a href=&quot;https://github.com/GoogleChrome/web-vitals&quot; rel=&quot;noopener&quot;&gt;web-vitals&lt;/a&gt; JavaScript library is
an example of a library that does not use cookies.&lt;/p&gt;
&lt;p&gt;Depending on how your site uses cookies to collect
performance data (that is, whether the cookies contain personal information), as
well as the legislation in question, the use of cookies for performance
measurement might not be subject to the same legislative requirements as some of
the cookies used on your site for other purposes—for example, advertising
cookies. Some sites choose to break out performance cookies as a separate
category of cookies when asking for user consent.&lt;/p&gt;
&lt;h3 id=&quot;synthetic-monitoring&quot;&gt;Synthetic monitoring &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#synthetic-monitoring&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Without custom configuration, most synthetic tools (such as Lighthouse and WebPageTest) will only measure the
experience of a first-time user who has not responded to a cookie consent
notice. However, not only do variations in cache state (for example, an initial
visit versus a repeat visit) need to be considered when collecting performance
data, but also variations in cookie acceptance state—accepted, rejected, or
unresponded.&lt;/p&gt;
&lt;p&gt;The following sections discuss WebPageTest and Lighthouse settings that can be
helpful for incorporating cookie notices into performance measurement workflows.
However, cookies and cookie notices are just one of many factors that can be
difficult to perfectly simulate in lab environments. For this reason, it is
important to make &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/#how-metrics-are-measured&quot;&gt;RUM
data&lt;/a&gt;
the cornerstone of your performance benchmarking, rather than synthetic tooling.&lt;/p&gt;
&lt;h3 id=&quot;testing-cookie-notices-with-webpagetest&quot;&gt;Testing cookie notices with WebPageTest &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#testing-cookie-notices-with-webpagetest&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;scripting&quot;&gt;Scripting &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#scripting&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;You can use scripting to have a &lt;a href=&quot;https://webpagetest.org/&quot; rel=&quot;noopener&quot;&gt;WebPageTest&lt;/a&gt; &amp;quot;click&amp;quot;
the cookie consent banner while collecting a trace.&lt;/p&gt;
&lt;p&gt;Add a script by going to the &lt;strong&gt;Script&lt;/strong&gt; tab. The script below navigates to the URL
to be tested and then clicks the DOM element with the id &lt;code&gt;cookieButton&lt;/code&gt;.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; WebPageTest scripts are &lt;a href=&quot;https://github.com/WPO-Foundation/webpagetest-docs/blob/main/src/scripting.md#scripting&quot;&gt;tab-delimited&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;combineSteps&lt;br /&gt;navigate    %URL%&lt;br /&gt;clickAndWait    &lt;span class=&quot;token assign-left variable&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;cookieButton&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;When using this script be aware that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;combineSteps&lt;/code&gt; tells WebPageTest to &amp;quot;combine&amp;quot; the results of the scripting
steps that follow into a single set of traces and measurements. Running this
script without &lt;code&gt;combineSteps&lt;/code&gt; can also be useful—separate traces make it
easy to see whether resources were loaded before or after cookie acceptance.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%URL%&lt;/code&gt; is a WebPageTest convention that refers to the URL that is being
tested.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;clickAndWait&lt;/code&gt; tells WebPageTest to click on the element indicated by
&lt;code&gt;attribute=value&lt;/code&gt; and wait for the subsequent browser activity to complete.
It follows the format &lt;code&gt;clickAndWait attribute=Value&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&#39;ve configured this script correctly, the screenshot taken by WebPageTest
should not show a cookie notice (the cookie notice has been accepted).&lt;/p&gt;
&lt;p&gt;For more information on WebPageTest scripting, check out &lt;a href=&quot;https://docs.webpagetest.org/scripting/&quot; rel=&quot;noopener&quot;&gt;WebPageTest
documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;set-cookies&quot;&gt;Set cookies &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#set-cookies&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;To run WebPageTest with a cookie set, go to the &lt;strong&gt;Advanced&lt;/strong&gt; tab and add the
cookie header to the &lt;strong&gt;Custom headers&lt;/strong&gt; field:&lt;/p&gt;
&lt;img alt=&quot;Screeshot showing the &amp;#x27;Custom headers&amp;#x27; field in WebPageTest&quot; decoding=&quot;async&quot; height=&quot;181&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/qSccrAxF0H4yoSzYRYdh.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/qSccrAxF0H4yoSzYRYdh.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/qSccrAxF0H4yoSzYRYdh.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/qSccrAxF0H4yoSzYRYdh.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/qSccrAxF0H4yoSzYRYdh.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/qSccrAxF0H4yoSzYRYdh.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/qSccrAxF0H4yoSzYRYdh.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/qSccrAxF0H4yoSzYRYdh.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/qSccrAxF0H4yoSzYRYdh.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/qSccrAxF0H4yoSzYRYdh.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/qSccrAxF0H4yoSzYRYdh.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/qSccrAxF0H4yoSzYRYdh.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/qSccrAxF0H4yoSzYRYdh.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/qSccrAxF0H4yoSzYRYdh.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/qSccrAxF0H4yoSzYRYdh.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/qSccrAxF0H4yoSzYRYdh.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/qSccrAxF0H4yoSzYRYdh.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/qSccrAxF0H4yoSzYRYdh.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h4 id=&quot;change-the-test-location&quot;&gt;Change the test location &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#change-the-test-location&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;To change the test location used by WebPageTest, click the &lt;strong&gt;Test Location&lt;/strong&gt;
dropdown located on the &lt;strong&gt;Advanced Testing&lt;/strong&gt; tab.&lt;/p&gt;
&lt;img alt=&quot;Screenshot of the &amp;#x27;Test Location&amp;#x27; dropdown in WebPageTest&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/j2RDdG43oidUy6AL6LovThjeX9c2/J27NcDQ5LTtXYloaA1DN.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/J27NcDQ5LTtXYloaA1DN.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/J27NcDQ5LTtXYloaA1DN.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/J27NcDQ5LTtXYloaA1DN.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/J27NcDQ5LTtXYloaA1DN.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/J27NcDQ5LTtXYloaA1DN.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/J27NcDQ5LTtXYloaA1DN.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/J27NcDQ5LTtXYloaA1DN.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/J27NcDQ5LTtXYloaA1DN.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/J27NcDQ5LTtXYloaA1DN.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/J27NcDQ5LTtXYloaA1DN.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/J27NcDQ5LTtXYloaA1DN.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/J27NcDQ5LTtXYloaA1DN.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/J27NcDQ5LTtXYloaA1DN.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/J27NcDQ5LTtXYloaA1DN.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/J27NcDQ5LTtXYloaA1DN.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/J27NcDQ5LTtXYloaA1DN.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/J27NcDQ5LTtXYloaA1DN.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h3 id=&quot;testing-cookie-notices-with-lighthouse&quot;&gt;Testing cookie notices with Lighthouse &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#testing-cookie-notices-with-lighthouse&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Setting cookies on a Lighthouse run can serve as a mechanism for getting a
page into a particular state for testing by Lighthouse. Lighthouse&#39;s cookie
behavior varies slightly by context (DevTools, CLI, or PageSpeed Insights).&lt;/p&gt;
&lt;h4 id=&quot;devtools&quot;&gt;DevTools &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#devtools&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Cookies are not cleared when Lighthouse is run from DevTools. However, other
types of storage are cleared by default. This behavior can be changed by using
the &lt;strong&gt;Clear Storage&lt;/strong&gt; option in the &lt;strong&gt;Lighthouse&lt;/strong&gt; settings panel.&lt;/p&gt;
&lt;img alt=&quot;Screenshot higlighting the Lighthouse &amp;#x27;Clear Storage&amp;#x27; option&quot; decoding=&quot;async&quot; height=&quot;304&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/nmNDeSoGEQUVKeTP7q7R.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/nmNDeSoGEQUVKeTP7q7R.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/nmNDeSoGEQUVKeTP7q7R.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/nmNDeSoGEQUVKeTP7q7R.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/nmNDeSoGEQUVKeTP7q7R.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/nmNDeSoGEQUVKeTP7q7R.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/nmNDeSoGEQUVKeTP7q7R.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/nmNDeSoGEQUVKeTP7q7R.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/nmNDeSoGEQUVKeTP7q7R.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/nmNDeSoGEQUVKeTP7q7R.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/nmNDeSoGEQUVKeTP7q7R.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/nmNDeSoGEQUVKeTP7q7R.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/nmNDeSoGEQUVKeTP7q7R.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/nmNDeSoGEQUVKeTP7q7R.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/nmNDeSoGEQUVKeTP7q7R.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/nmNDeSoGEQUVKeTP7q7R.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/nmNDeSoGEQUVKeTP7q7R.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/nmNDeSoGEQUVKeTP7q7R.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h4 id=&quot;cli&quot;&gt;CLI &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#cli&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Running Lighthouse from the CLI uses a fresh Chrome instance, so no cookies
are set by default. To run Lighthouse from the CLI with a particular cookie set,
use the following
&lt;a href=&quot;https://github.com/GoogleChrome/lighthouse#cli-options&quot; rel=&quot;noopener&quot;&gt;command&lt;/a&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;lighthouse &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;url&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; --extra-headers &lt;span class=&quot;token string&quot;&gt;&quot;{&lt;span class=&quot;token entity&quot; title=&quot;\&amp;quot;&quot;&gt;\&quot;&lt;/span&gt;Cookie&lt;span class=&quot;token entity&quot; title=&quot;\&amp;quot;&quot;&gt;\&quot;&lt;/span&gt;:&lt;span class=&quot;token entity&quot; title=&quot;\&amp;quot;&quot;&gt;\&quot;&lt;/span&gt;cookie1=abc; cookie2=def; \_id=foo&lt;span class=&quot;token entity&quot; title=&quot;\&amp;quot;&quot;&gt;\&quot;&lt;/span&gt;}&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;For more information on setting custom request headers in Lighthouse CLI, see
&lt;a href=&quot;https://github.com/GoogleChrome/lighthouse/blob/master/docs/authenticated-pages.md&quot; rel=&quot;noopener&quot;&gt;Running Lighthouse on Authenticated
Pages&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;pagespeed-insights&quot;&gt;PageSpeed Insights &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#pagespeed-insights&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Running Lighthouse from PageSpeed Insights uses a fresh Chrome instance and does
not set any cookies. PageSeed Insights cannot be configured to set particular
cookies.&lt;/p&gt;
&lt;h2 id=&quot;user-experience&quot;&gt;User experience &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#user-experience&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The user experience (UX) of different cookie consent notices is primarily the
result of two decisions: the location of the cookie notice within the page and
the extent to which the user can customize a site&#39;s use of cookies. This section
discusses potential approaches to these two decisions.&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; Cookie notice UX is often heavily influenced by legislation which can vary widely by geography. Thus, some of the design patterns discussed in this section may not be relevant to your particular situation. This article should not be considered a substitute for legal advice. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;When considering potential designs for your cookie notice, here are some things
to think about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;UX: Is this a good user experience? How will this particular design affect
existing page elements and user flows?&lt;/li&gt;
&lt;li&gt;Business: What is your site&#39;s cookie strategy? What are your goals for the
cookie notice?&lt;/li&gt;
&lt;li&gt;Legal: Does this comply with legal requirements?&lt;/li&gt;
&lt;li&gt;Engineering: How much work would this be to implement and maintain? How
difficult would it be to change?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;placement&quot;&gt;Placement &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#placement&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Cookie notices can be displayed as a header, inline element, or footer. They can
also be displayed on top of page content using a modal or served as an
&lt;a href=&quot;https://en.wikipedia.org/wiki/Interstitial_webpage&quot; rel=&quot;noopener&quot;&gt;interstitial&lt;/a&gt;.&lt;/p&gt;
&lt;img alt=&quot;Diagram showing examples of different placement options for cookie notices&quot; decoding=&quot;async&quot; height=&quot;345&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/LLqHAhp7W6x4E3rZh0Oc.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/LLqHAhp7W6x4E3rZh0Oc.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/LLqHAhp7W6x4E3rZh0Oc.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/LLqHAhp7W6x4E3rZh0Oc.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/LLqHAhp7W6x4E3rZh0Oc.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/LLqHAhp7W6x4E3rZh0Oc.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/LLqHAhp7W6x4E3rZh0Oc.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/LLqHAhp7W6x4E3rZh0Oc.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/LLqHAhp7W6x4E3rZh0Oc.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/LLqHAhp7W6x4E3rZh0Oc.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/LLqHAhp7W6x4E3rZh0Oc.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/LLqHAhp7W6x4E3rZh0Oc.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/LLqHAhp7W6x4E3rZh0Oc.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/LLqHAhp7W6x4E3rZh0Oc.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/LLqHAhp7W6x4E3rZh0Oc.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/LLqHAhp7W6x4E3rZh0Oc.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/LLqHAhp7W6x4E3rZh0Oc.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/LLqHAhp7W6x4E3rZh0Oc.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h4 id=&quot;header,-footer,-and-inline-cookie-notices&quot;&gt;Header, footer, and inline cookie notices &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#header,-footer,-and-inline-cookie-notices&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Cookie notices are commonly placed in the header or footer. Of these two
options, the footer placement is generally preferable because it is unobtrusive,
does not compete for attention with banner ads or notifications, and typically
does not cause CLS. In addition, it is a common place for placing privacy
policies and terms of use.&lt;/p&gt;
&lt;p&gt;Although inline cookie notices are an option, they can be difficult to integrate
into existing user interfaces, and therefore are uncommon.&lt;/p&gt;
&lt;h4 id=&quot;modals&quot;&gt;Modals &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#modals&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Modals are cookie consent notices that are displayed on top of page content.
Modals can look and perform quite differently depending on their size.&lt;/p&gt;
&lt;p&gt;Smaller, partial-screen modals can be a good alternative for sites that are
struggling to implement cookie notices in a way that doesn&#39;t cause &lt;a href=&quot;https://web.dev/cls/&quot;&gt;layout
shifts&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;On the other hand, large modals that obscure the majority of page content should
be used carefully. In particular, smaller sites may find that users bounce
rather than accept the cookie notice of an unfamiliar site with obscured
content. Although they are not necessarily synonymous concepts, if you are
considering using a full-screen cookie consent modal, you should be aware of
legislation regarding &lt;a href=&quot;https://techcrunch.com/2020/05/06/no-cookie-consent-walls-and-no-scrolling-isnt-consent-says-eu-data-protection-body/&quot; rel=&quot;noopener&quot;&gt;cookie
walls&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; Large modals can be considered a type of interstitial. Google Search &lt;a href=&quot;https://developers.google.com/search/blog/2016/08/helping-users-easily-access-content-on&quot;&gt;does not penalize&lt;/a&gt; the usage of interstitials when they are used to comply with legal regulations such as in the case of cookie banners. However, their usage of interstitials in other contexts—particularly if they are intrusive or create a poor user experience—may be penalized. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;configurability&quot;&gt;Configurability &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#configurability&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Cookie notice interfaces give users varying levels of control over which cookies
they accept.&lt;/p&gt;
&lt;h4 id=&quot;no-configurability&quot;&gt;No configurability &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#no-configurability&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;These notice-style cookie banners do not present users with direct UX controls
for opting out of cookies. Instead, they typically include a link to the site&#39;s
cookie policy which may provide users with information about managing cookies
using their web browser. These notices typically include either a &amp;quot;dismiss&amp;quot; and/or
&amp;quot;Accept&amp;quot; button.&lt;/p&gt;
&lt;img alt=&quot;Diagram showing examples of cookie notices with no cookie configurability&quot; decoding=&quot;async&quot; height=&quot;518&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/RlAg8DCjBC0bX7Ki5MuE.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/RlAg8DCjBC0bX7Ki5MuE.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/RlAg8DCjBC0bX7Ki5MuE.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/RlAg8DCjBC0bX7Ki5MuE.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/RlAg8DCjBC0bX7Ki5MuE.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/RlAg8DCjBC0bX7Ki5MuE.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/RlAg8DCjBC0bX7Ki5MuE.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/RlAg8DCjBC0bX7Ki5MuE.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/RlAg8DCjBC0bX7Ki5MuE.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/RlAg8DCjBC0bX7Ki5MuE.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/RlAg8DCjBC0bX7Ki5MuE.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/RlAg8DCjBC0bX7Ki5MuE.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/RlAg8DCjBC0bX7Ki5MuE.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/RlAg8DCjBC0bX7Ki5MuE.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/RlAg8DCjBC0bX7Ki5MuE.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/RlAg8DCjBC0bX7Ki5MuE.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/RlAg8DCjBC0bX7Ki5MuE.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/RlAg8DCjBC0bX7Ki5MuE.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h4 id=&quot;some-configurability&quot;&gt;Some configurability &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#some-configurability&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;These cookie notices give the user the option of declining cookies but do not
support more granular controls. This approach to cookie notices is less common.&lt;/p&gt;
&lt;img alt=&quot;Diagram showing examples of cookie notices with some cookie configurability&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/j2RDdG43oidUy6AL6LovThjeX9c2/MOl8u9NcnyjCWogxzjdz.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/MOl8u9NcnyjCWogxzjdz.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/MOl8u9NcnyjCWogxzjdz.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/MOl8u9NcnyjCWogxzjdz.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/MOl8u9NcnyjCWogxzjdz.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/MOl8u9NcnyjCWogxzjdz.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/MOl8u9NcnyjCWogxzjdz.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/MOl8u9NcnyjCWogxzjdz.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/MOl8u9NcnyjCWogxzjdz.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/MOl8u9NcnyjCWogxzjdz.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/MOl8u9NcnyjCWogxzjdz.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/MOl8u9NcnyjCWogxzjdz.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/MOl8u9NcnyjCWogxzjdz.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/MOl8u9NcnyjCWogxzjdz.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/MOl8u9NcnyjCWogxzjdz.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/MOl8u9NcnyjCWogxzjdz.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/MOl8u9NcnyjCWogxzjdz.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/MOl8u9NcnyjCWogxzjdz.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h4 id=&quot;full-configurability&quot;&gt;Full configurability &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cookie-notice-best-practices/#full-configurability&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;These cookie notices provide users with more fine-grained controls for
configuring the cookie usage that they accept.&lt;/p&gt;
&lt;img alt=&quot;Diagram showing examples of chookie notices with full cookie configurability&quot; decoding=&quot;async&quot; height=&quot;467&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QfFoqkkmdKHAYlftIH0n.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QfFoqkkmdKHAYlftIH0n.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QfFoqkkmdKHAYlftIH0n.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QfFoqkkmdKHAYlftIH0n.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QfFoqkkmdKHAYlftIH0n.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QfFoqkkmdKHAYlftIH0n.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QfFoqkkmdKHAYlftIH0n.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QfFoqkkmdKHAYlftIH0n.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QfFoqkkmdKHAYlftIH0n.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QfFoqkkmdKHAYlftIH0n.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QfFoqkkmdKHAYlftIH0n.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QfFoqkkmdKHAYlftIH0n.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QfFoqkkmdKHAYlftIH0n.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QfFoqkkmdKHAYlftIH0n.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QfFoqkkmdKHAYlftIH0n.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QfFoqkkmdKHAYlftIH0n.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QfFoqkkmdKHAYlftIH0n.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/QfFoqkkmdKHAYlftIH0n.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;UX:&lt;/strong&gt;
Controls for configuring cookie usage are most commonly displayed using a
separate modal that is launched when the user responds to the initial cookie
consent notice. However, if space permits, some sites will display these
controls inline within the initial cookie consent notice.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Granularity:&lt;/strong&gt;
The most common approach to cookie configurability is to allow users to
opt-in to cookies by cookie &amp;quot;category&amp;quot;. Examples of common cookie categories
include functional, targeting, and social media cookies.&lt;/p&gt;
&lt;p&gt;However, some sites will go a step further and allow users to opt-in on a
per-cookie basis. Alternatively, another way of providing users with more
specific controls is to break down cookie categories like &amp;quot;advertising&amp;quot; into
specific use cases—for example, allowing users to separately opt-in to
&amp;quot;basic ads&amp;quot; and &amp;quot;personalized ads&amp;quot;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;img alt=&quot;Diagram showing examples of cookie notices with full cookie configurability&quot; decoding=&quot;async&quot; height=&quot;372&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/z7zFPtCkFi8GEpkfubek.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/z7zFPtCkFi8GEpkfubek.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/z7zFPtCkFi8GEpkfubek.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/z7zFPtCkFi8GEpkfubek.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/z7zFPtCkFi8GEpkfubek.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/z7zFPtCkFi8GEpkfubek.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/z7zFPtCkFi8GEpkfubek.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/z7zFPtCkFi8GEpkfubek.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/z7zFPtCkFi8GEpkfubek.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/z7zFPtCkFi8GEpkfubek.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/z7zFPtCkFi8GEpkfubek.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/z7zFPtCkFi8GEpkfubek.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/z7zFPtCkFi8GEpkfubek.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/z7zFPtCkFi8GEpkfubek.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/z7zFPtCkFi8GEpkfubek.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/z7zFPtCkFi8GEpkfubek.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/z7zFPtCkFi8GEpkfubek.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/z7zFPtCkFi8GEpkfubek.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Debug layout shifts</title>
    <link href="https://web.dev/debug-layout-shifts/"/>
    <updated>2021-03-11T00:00:00Z</updated>
    <id>https://web.dev/debug-layout-shifts/</id>
    <content type="html" mode="escaped">&lt;p&gt;The first part of this article discusses tooling for debugging layout shifts,
while the second part discusses the thought process to use when
identifying the cause of a layout shift.&lt;/p&gt;
&lt;h2 id=&quot;tooling&quot;&gt;Tooling &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-layout-shifts/#tooling&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;layout-instability-api&quot;&gt;Layout Instability API &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-layout-shifts/#layout-instability-api&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://wicg.github.io/layout-instability/&quot; rel=&quot;noopener&quot;&gt;Layout Instability API&lt;/a&gt; is the
browser mechanism for measuring and reporting layout shifts. All tools for
debugging layout shifts, including DevTools, are ultimately built upon the
Layout Instability API. However, using the Layout Instability API directly is a
powerful debugging tool due to its flexibility.&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 Layout Instability API is only &lt;a href=&quot;https://caniuse.com/mdn-api_layoutshift&quot;&gt;supported&lt;/a&gt; by Chromium browsers. At the current time there is no way to measure or debug layout shifts in non-Chromium browsers. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;usage&quot;&gt;Usage &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-layout-shifts/#usage&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The same code &lt;a href=&quot;https://web.dev/cls/#measure-cls-in-javascript&quot;&gt;snippet&lt;/a&gt; that
measures &lt;a href=&quot;https://web.dev/cls/&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt; can also
serve to debug layout shifts. The snippet below logs information about layout
shifts to the console. Inspecting this log will provide you information
about when, where, and how a layout shift occurred.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; cls &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token 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;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hadRecentInput&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      cls &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&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;Current CLS value:&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cls&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;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;p&gt;When running this script be aware that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;buffered: true&lt;/code&gt; option indicates that the
&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;
should check the browser&#39;s &lt;a href=&quot;https://www.w3.org/TR/performance-timeline-2/#dfn-performance-entry-buffer&quot; rel=&quot;noopener&quot;&gt;performance entry
buffer&lt;/a&gt;
for performance entries that were created before the observer&#39;s
initialization. As a result, the &lt;code&gt;PerformanceObserver&lt;/code&gt; will report layout
shifts that happened both before and after it was initialized. Keep this in
mind when inspecting the console logs. An initial glut of layout shifts can
reflect a reporting backlog, rather than the sudden occurrence of numerous
layout shifts.&lt;/li&gt;
&lt;li&gt;To avoid impacting performance, the &lt;code&gt;PerformanceObserver&lt;/code&gt; waits until the main
thread is idle to report on layout shifts. As a result, depending on how
busy the main thread is, there may be a slight delay between when a layout
shift occurs and when it is logged in the console.&lt;/li&gt;
&lt;li&gt;This script ignores layout shifts that occurred within 500 ms of user input
and therefore do not count towards CLS.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Information about layout shifts is reported using a combination of two APIs: the
&lt;a href=&quot;https://wicg.github.io/layout-instability/#layoutshift&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;LayoutShift&lt;/code&gt;&lt;/a&gt; and
&lt;a href=&quot;https://wicg.github.io/layout-instability/#sec-layout-shift-attribution&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;LayoutShiftAttribution&lt;/code&gt;&lt;/a&gt;
interfaces. Each of these interfaces are explained in more detail in the
following sections.&lt;/p&gt;
&lt;h4 id=&quot;layoutshift&quot;&gt;LayoutShift &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-layout-shifts/#layoutshift&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Each layout shift is reported using the &lt;code&gt;LayoutShift&lt;/code&gt; interface. The contents of
an entry look like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token literal-property property&quot;&gt;entryType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;layout-shift&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token literal-property property&quot;&gt;hadRecentInput&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token literal-property property&quot;&gt;lastInputTime&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token literal-property property&quot;&gt;sources&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 number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;LayoutShiftAttribution&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; LayoutShiftAttribution&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; LayoutShiftAttribution&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token literal-property property&quot;&gt;startTime&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11317.934999999125&lt;/span&gt;&lt;br /&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 number&quot;&gt;0.17508567530168798&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The entry above indicates a layout shift during which three DOM elements changed
position. The layout shift score of this particular layout shift was &lt;code&gt;0.175&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;These are the properties of a &lt;code&gt;LayoutShift&lt;/code&gt; instance that are most relevant to
debugging layout shifts:&lt;/p&gt;
&lt;div class=&quot;table-wrapper scrollbar&quot;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sources&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The &lt;code&gt;sources&lt;/code&gt; property lists the DOM elements that moved during the layout shift. This array can contain up to five sources. In the event that there are more than five elements impacted by the layout shift, the five largest (as measured by impact on layout stability) sources of layout shift are reported. This information is reported using the LayoutShiftAttribution interface (explained in more detail below).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;value&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The &lt;code&gt;value&lt;/code&gt; property reports the &lt;a href=&quot;https://web.dev/cls/#layout-shift-score&quot;&gt;layout shift score&lt;/a&gt; for a particular layout shift.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hadRecentInput&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The &lt;code&gt;hadRecentInput&lt;/code&gt; property indicates whether a layout shift occurred within 500 milliseconds of user input.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;startTime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The &lt;code&gt;startTime&lt;/code&gt; property indicates when a layout shift occurred. &lt;code&gt;startTime&lt;/code&gt; is indicated in milliseconds and is measured relative to the &lt;a href=&quot;https://www.w3.org/TR/hr-time-2/#sec-time-origin&quot; rel=&quot;noopener&quot;&gt;time that the page load was initiated&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;duration&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The &lt;code&gt;duration&lt;/code&gt; property will always be set to &lt;code&gt;0&lt;/code&gt;. This property is inherited from the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/PerformanceEntry&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;PerformanceEntry&lt;/code&gt;&lt;/a&gt; interface (the &lt;code&gt;LayoutShift&lt;/code&gt; interface extends the &lt;code&gt;PerformanceEntry&lt;/code&gt; interface). However, the concept of duration does not apply to layout shift events, so it is set to &lt;code&gt;0&lt;/code&gt;. For information on the &lt;code&gt;PerformanceEntry&lt;/code&gt; interface, refer to the &lt;a href=&quot;https://w3c.github.io/performance-timeline/#the-performanceentry-interface&quot; rel=&quot;noopener&quot;&gt;spec&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The &lt;a href=&quot;https://chrome.google.com/webstore/detail/web-vitals/ahfhijdlegdabablpippeagghigmibma&quot;&gt;Web Vitals Extension&lt;/a&gt; can log layout shift info to the console. To enable this feature, go to &lt;strong&gt;Options &amp;gt; Console Logging&lt;/strong&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;layoutshiftattribution&quot;&gt;LayoutShiftAttribution &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-layout-shifts/#layoutshiftattribution&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;LayoutShiftAttribution&lt;/code&gt; interface describes a single shift of a single DOM
element. If multiple elements shift during a layout shift, the &lt;code&gt;sources&lt;/code&gt;
property contains multiple entries.&lt;/p&gt;
&lt;p&gt;For example, the JSON below corresponds to a layout shift with one source: the
downward shift of the &lt;code&gt;&amp;lt;div id=&#39;banner&#39;&amp;gt;&lt;/code&gt; DOM element from &lt;code&gt;y: 76&lt;/code&gt; to
&lt;code&gt;y:246&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 comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;sources&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;node&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;div#banner&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;previousRect&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;x&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;311&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;y&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;76&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;width&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&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;height&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;18&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;top&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;76&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;right&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;315&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;bottom&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;94&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;left&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;311&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token property&quot;&gt;&quot;currentRect&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;x&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;311&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;y&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;246&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;width&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&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;height&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;18&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;top&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;246&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;right&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;315&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;bottom&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;264&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;left&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;311&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The &lt;code&gt;node&lt;/code&gt; property identifies the HTML element that shifted. Hovering on this
property in DevTools highlights the corresponding page element.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;previousRect&lt;/code&gt; and &lt;code&gt;currentRect&lt;/code&gt; properties report the size and position of
the node.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; coordinates report the x-coordinate and y-coordinate
respectively of the top-left corner of the element&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; properties report the width and height respectively
of the element.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;right&lt;/code&gt;, &lt;code&gt;bottom&lt;/code&gt;, and &lt;code&gt;left&lt;/code&gt; properties report the x or y
coordinate values corresponding to the given edge of the element. In other
words, the value of &lt;code&gt;top&lt;/code&gt; is equal to &lt;code&gt;y&lt;/code&gt;; the value of &lt;code&gt;bottom&lt;/code&gt; is equal to
&lt;code&gt;y+height&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If all properties of &lt;code&gt;previousRect&lt;/code&gt; are set to 0 this means that the element has
shifted into view. If all properties of &lt;code&gt;currentRect&lt;/code&gt; are set to 0 this means
that the element has shifted out of view.&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; Layout Instability API currently does not ignore elements that shifted but were not visible due to another element being positioned in front of them. Use &lt;code&gt;display: none&lt;/code&gt;, &lt;code&gt;visibility: hidden&lt;/code&gt;, or &lt;code&gt;opacity: 0&lt;/code&gt; to avoid layout shifts in cases where you need to run layout on some elements before you make them visible to the user. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;One of the most important things to understand when interpreting these outputs
is that elements listed as &lt;em&gt;sources&lt;/em&gt; are the elements that shifted during the
layout shift. However, it&#39;s possible that these elements are only indirectly
related to the &amp;quot;root cause&amp;quot; of layout instability. Here are a few examples.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example #1&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This layout shift would be reported with one source: element B. However, the
root cause of this layout shift is the change in size of element A.&lt;/p&gt;
&lt;img alt=&quot;Example showing a layout shift caused by a change in element dimensions&quot; decoding=&quot;async&quot; height=&quot;452&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/oaM41OYL7mFtGcpIN8KF.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/oaM41OYL7mFtGcpIN8KF.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/oaM41OYL7mFtGcpIN8KF.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/oaM41OYL7mFtGcpIN8KF.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/oaM41OYL7mFtGcpIN8KF.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/oaM41OYL7mFtGcpIN8KF.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/oaM41OYL7mFtGcpIN8KF.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/oaM41OYL7mFtGcpIN8KF.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/oaM41OYL7mFtGcpIN8KF.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/oaM41OYL7mFtGcpIN8KF.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/oaM41OYL7mFtGcpIN8KF.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/oaM41OYL7mFtGcpIN8KF.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/oaM41OYL7mFtGcpIN8KF.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/oaM41OYL7mFtGcpIN8KF.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/oaM41OYL7mFtGcpIN8KF.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/oaM41OYL7mFtGcpIN8KF.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/oaM41OYL7mFtGcpIN8KF.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/oaM41OYL7mFtGcpIN8KF.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;&lt;strong&gt;Example #2&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The layout shift in this example would be reported with two sources: element A
and element B. The root cause of this layout shift is the change in position of
element A.&lt;/p&gt;
&lt;img alt=&quot;Example showing a layout shift caused by a change in element position&quot; decoding=&quot;async&quot; height=&quot;451&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AhaslIEWb5fFMMgiZcI2.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AhaslIEWb5fFMMgiZcI2.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AhaslIEWb5fFMMgiZcI2.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AhaslIEWb5fFMMgiZcI2.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AhaslIEWb5fFMMgiZcI2.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AhaslIEWb5fFMMgiZcI2.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AhaslIEWb5fFMMgiZcI2.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AhaslIEWb5fFMMgiZcI2.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AhaslIEWb5fFMMgiZcI2.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AhaslIEWb5fFMMgiZcI2.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AhaslIEWb5fFMMgiZcI2.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AhaslIEWb5fFMMgiZcI2.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AhaslIEWb5fFMMgiZcI2.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AhaslIEWb5fFMMgiZcI2.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AhaslIEWb5fFMMgiZcI2.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AhaslIEWb5fFMMgiZcI2.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AhaslIEWb5fFMMgiZcI2.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AhaslIEWb5fFMMgiZcI2.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;&lt;strong&gt;Example #3&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The layout shift in this example would be reported with one source: element B.
Changing the position of element B resulted in this layout shift.&lt;/p&gt;
&lt;img alt=&quot;Example showing a layout shift caused by a change in element position&quot; decoding=&quot;async&quot; height=&quot;451&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/6zKjd4Ua6YJ94LMlqiMR.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/6zKjd4Ua6YJ94LMlqiMR.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/6zKjd4Ua6YJ94LMlqiMR.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/6zKjd4Ua6YJ94LMlqiMR.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/6zKjd4Ua6YJ94LMlqiMR.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/6zKjd4Ua6YJ94LMlqiMR.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/6zKjd4Ua6YJ94LMlqiMR.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/6zKjd4Ua6YJ94LMlqiMR.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/6zKjd4Ua6YJ94LMlqiMR.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/6zKjd4Ua6YJ94LMlqiMR.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/6zKjd4Ua6YJ94LMlqiMR.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/6zKjd4Ua6YJ94LMlqiMR.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/6zKjd4Ua6YJ94LMlqiMR.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/6zKjd4Ua6YJ94LMlqiMR.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/6zKjd4Ua6YJ94LMlqiMR.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/6zKjd4Ua6YJ94LMlqiMR.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/6zKjd4Ua6YJ94LMlqiMR.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/6zKjd4Ua6YJ94LMlqiMR.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;&lt;strong&gt;Example #4&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Although element B changes size, there is no layout shift in this example.&lt;/p&gt;
&lt;img alt=&quot;Example showing a element changing size but not causing a layout shift&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/j2RDdG43oidUy6AL6LovThjeX9c2/ZujHWxsXI3C7tupe42oD.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/ZujHWxsXI3C7tupe42oD.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/ZujHWxsXI3C7tupe42oD.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/ZujHWxsXI3C7tupe42oD.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/ZujHWxsXI3C7tupe42oD.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/ZujHWxsXI3C7tupe42oD.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/ZujHWxsXI3C7tupe42oD.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/ZujHWxsXI3C7tupe42oD.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/ZujHWxsXI3C7tupe42oD.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/ZujHWxsXI3C7tupe42oD.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/ZujHWxsXI3C7tupe42oD.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/ZujHWxsXI3C7tupe42oD.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/ZujHWxsXI3C7tupe42oD.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/ZujHWxsXI3C7tupe42oD.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/ZujHWxsXI3C7tupe42oD.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/ZujHWxsXI3C7tupe42oD.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/ZujHWxsXI3C7tupe42oD.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/ZujHWxsXI3C7tupe42oD.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Check out a &lt;a href=&quot;https://desert-righteous-router.glitch.me/&quot; rel=&quot;noopener&quot;&gt;demo of how DOM changes are reported by the Layout Instability API&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;devtools&quot;&gt;DevTools &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-layout-shifts/#devtools&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;performance-panel&quot;&gt;Performance panel &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-layout-shifts/#performance-panel&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;strong&gt;Experience&lt;/strong&gt; pane of the DevTools &lt;strong&gt;Performance&lt;/strong&gt; panel displays all
layout shifts that occur during a given performance trace—even if they occur
within 500 ms of a user interaction and therefore don&#39;t count towards CLS.
Hovering over a particular layout shift in the &lt;strong&gt;Experience&lt;/strong&gt; panel highlights
the affected DOM element.&lt;/p&gt;
&lt;img alt=&quot;Screenshot of a layout shift displayed in the DevTools Network panel&quot; decoding=&quot;async&quot; height=&quot;629&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 724px) 724px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Uug2fnJT8mOc2YQmxo2l.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Uug2fnJT8mOc2YQmxo2l.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Uug2fnJT8mOc2YQmxo2l.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Uug2fnJT8mOc2YQmxo2l.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Uug2fnJT8mOc2YQmxo2l.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Uug2fnJT8mOc2YQmxo2l.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Uug2fnJT8mOc2YQmxo2l.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Uug2fnJT8mOc2YQmxo2l.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Uug2fnJT8mOc2YQmxo2l.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Uug2fnJT8mOc2YQmxo2l.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Uug2fnJT8mOc2YQmxo2l.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Uug2fnJT8mOc2YQmxo2l.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Uug2fnJT8mOc2YQmxo2l.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Uug2fnJT8mOc2YQmxo2l.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Uug2fnJT8mOc2YQmxo2l.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Uug2fnJT8mOc2YQmxo2l.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Uug2fnJT8mOc2YQmxo2l.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/Uug2fnJT8mOc2YQmxo2l.png?auto=format&amp;w=1448 1448w&quot; width=&quot;724&quot; /&gt;
&lt;p&gt;To view more information about the layout shift, click on the layout shift, then
open the &lt;strong&gt;Summary&lt;/strong&gt; drawer. Changes to the element&#39;s dimensions are listed
using the format &lt;code&gt;[width, height]&lt;/code&gt;; changes to the element&#39;s position are listed
using the format &lt;code&gt;[x,y]&lt;/code&gt;. The &lt;strong&gt;Had recent input&lt;/strong&gt; property indicates whether a
layout shift occurred within 500 ms of a user interaction.&lt;/p&gt;
&lt;img alt=&quot;Screenshot of the DevTools &amp;#x27;Summary&amp;#x27; tab for a layout shift&quot; decoding=&quot;async&quot; height=&quot;354&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 612px) 612px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AfVjsH9Nl9w0lJwQZEjR.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AfVjsH9Nl9w0lJwQZEjR.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AfVjsH9Nl9w0lJwQZEjR.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AfVjsH9Nl9w0lJwQZEjR.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AfVjsH9Nl9w0lJwQZEjR.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AfVjsH9Nl9w0lJwQZEjR.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AfVjsH9Nl9w0lJwQZEjR.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AfVjsH9Nl9w0lJwQZEjR.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AfVjsH9Nl9w0lJwQZEjR.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AfVjsH9Nl9w0lJwQZEjR.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AfVjsH9Nl9w0lJwQZEjR.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AfVjsH9Nl9w0lJwQZEjR.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AfVjsH9Nl9w0lJwQZEjR.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AfVjsH9Nl9w0lJwQZEjR.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AfVjsH9Nl9w0lJwQZEjR.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/AfVjsH9Nl9w0lJwQZEjR.png?auto=format&amp;w=1224 1224w&quot; width=&quot;612&quot; /&gt;
&lt;p&gt;For information on the duration of a layout shift, open the &lt;strong&gt;Event Log&lt;/strong&gt; tab.
The duration of a layout shift can also be approximated by looking in the
&lt;strong&gt;Experience&lt;/strong&gt; pane for the length of the red layout shift rectangle.&lt;/p&gt;
&lt;img alt=&quot;Screenshot of the DevTools &amp;#x27;Event Log&amp;#x27; tab for a layout shift&quot; decoding=&quot;async&quot; height=&quot;354&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 612px) 612px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/124Dm7vV3EGM7M9fiugs.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/124Dm7vV3EGM7M9fiugs.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/124Dm7vV3EGM7M9fiugs.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/124Dm7vV3EGM7M9fiugs.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/124Dm7vV3EGM7M9fiugs.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/124Dm7vV3EGM7M9fiugs.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/124Dm7vV3EGM7M9fiugs.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/124Dm7vV3EGM7M9fiugs.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/124Dm7vV3EGM7M9fiugs.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/124Dm7vV3EGM7M9fiugs.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/124Dm7vV3EGM7M9fiugs.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/124Dm7vV3EGM7M9fiugs.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/124Dm7vV3EGM7M9fiugs.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/124Dm7vV3EGM7M9fiugs.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/124Dm7vV3EGM7M9fiugs.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/124Dm7vV3EGM7M9fiugs.png?auto=format&amp;w=1224 1224w&quot; width=&quot;612&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 duration of a layout shift has no impact on its layout shift score. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;For more information on using the &lt;strong&gt;Performance&lt;/strong&gt; panel, refer to &lt;a href=&quot;https://developer.chrome.com/docs/devtools/evaluate-performance/reference/&quot; rel=&quot;noopener&quot;&gt;Performance
Analysis
Reference&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;highlight-layout-shift-regions&quot;&gt;Highlight layout shift regions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-layout-shifts/#highlight-layout-shift-regions&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Highlighting layout shift regions can be a helpful technique for getting a
quick, at-a-glance feel for the location and timing of the layout shifts
occurring on a page.&lt;/p&gt;
&lt;p&gt;To enable Layout Shift Regions in DevTools, go to &lt;strong&gt;Settings &amp;gt; More Tools &amp;gt;
Rendering &amp;gt; Layout Shift Regions&lt;/strong&gt; then refresh the page that you wish to debug.
Areas of layout shift will be briefly highlighted in purple.&lt;/p&gt;
&lt;h2 id=&quot;thought-process-for-identifying-the-cause-of-layout-shifts&quot;&gt;Thought process for identifying the cause of layout shifts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-layout-shifts/#thought-process-for-identifying-the-cause-of-layout-shifts&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can use the steps below to identify the cause of layout shifts
regardless of when or how the layout shift occurs. These steps can be
supplemented with running Lighthouse—however, keep in mind that Lighthouse can
only identify layout shifts that occurred during the initial page load. In
addition, Lighthouse also can only provide suggestions for some causes of layout
shifts—for example, image elements that do not have explicit width and height.&lt;/p&gt;
&lt;h3 id=&quot;identifying-the-cause-of-a-layout-shift&quot;&gt;Identifying the cause of a layout shift &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-layout-shifts/#identifying-the-cause-of-a-layout-shift&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Layout shifts can be caused by the following events:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Changes to the position of a DOM element&lt;/li&gt;
&lt;li&gt;Changes to the dimensions of a DOM element&lt;/li&gt;
&lt;li&gt;Insertion or removal of a DOM element&lt;/li&gt;
&lt;li&gt;Animations that trigger layout&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In particular, the DOM element immediately preceding the shifted element is the
element most likely to be involved in &amp;quot;causing&amp;quot; layout shift. Thus, when
investigating why a layout shift occurred consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Did the position or dimensions of the preceding element change?&lt;/li&gt;
&lt;li&gt;Was a DOM element inserted or removed before the shifted element?&lt;/li&gt;
&lt;li&gt;Was the position of the shifted element explicitly changed?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the preceding element did not cause the layout shift, continue your search by
considering other preceding and nearby elements.&lt;/p&gt;
&lt;p&gt;In addition, the direction and distance of a layout shift can provide hints
about root cause. For example, a large downward shift often indicates the
insertion of a DOM element, whereas a 1 px or 2 px layout shift often indicates
the application of conflicting CSS styles or the loading and application of a
web font.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Diagram showing a layout shift caused by a font swap&quot; decoding=&quot;async&quot; height=&quot;452&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/g0892nhvz3SnSaasaO1b.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    In this example, font swapping caused page elements to shift upwards by five pixels.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;These are some of the specific behaviors that most frequently cause layout shift
events:&lt;/p&gt;
&lt;h4 id=&quot;changes-to-the-position-of-an-element-that-arent-due-to-the-movement-of-another-element&quot;&gt;Changes to the position of an element (that aren&#39;t due to the movement of another element) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-layout-shifts/#changes-to-the-position-of-an-element-that-arent-due-to-the-movement-of-another-element&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;This type of change is often a result of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Stylesheets that are loaded late or overwrite previously declared styles.&lt;/li&gt;
&lt;li&gt;Animation and transition effects.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;changes-to-the-dimensions-of-an-element&quot;&gt;Changes to the dimensions of an element &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-layout-shifts/#changes-to-the-dimensions-of-an-element&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;This type of change is often a result of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Stylesheets that are loaded late or overwrite previously declared styles.&lt;/li&gt;
&lt;li&gt;Images and iframes without &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes that load after
their &amp;quot;slot&amp;quot; has been rendered.&lt;/li&gt;
&lt;li&gt;Text blocks without &lt;code&gt;width&lt;/code&gt; or &lt;code&gt;height&lt;/code&gt; attributes that swap fonts after the
text has been rendered.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;the-insertion-or-removal-of-dom-elements&quot;&gt;The insertion or removal of DOM elements &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-layout-shifts/#the-insertion-or-removal-of-dom-elements&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;This is often the result of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Insertion of ads and other third-party embeds.&lt;/li&gt;
&lt;li&gt;Insertion of banners, alerts, and modals.&lt;/li&gt;
&lt;li&gt;Infinite scroll and other UX patterns that load additional content above
existing content.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;animations-that-trigger-layout&quot;&gt;Animations that trigger layout &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-layout-shifts/#animations-that-trigger-layout&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Some animation effects can &lt;a href=&quot;https://gist.github.com/paulirish/5d52fb081b3570c81e3a&quot; rel=&quot;noopener&quot;&gt;trigger
layout&lt;/a&gt;. A common
example of this is when DOM elements are &#39;animated&#39; by incrementing properties
like &lt;code&gt;top&lt;/code&gt; or &lt;code&gt;left&lt;/code&gt; rather than using CSS&#39;s
&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. Read &lt;a href=&quot;https://web.dev/animations-guide/&quot;&gt;How to create high-performance CSS animations&lt;/a&gt;
for more information.&lt;/p&gt;
&lt;h3 id=&quot;reproducing-layout-shifts&quot;&gt;Reproducing layout shifts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/debug-layout-shifts/#reproducing-layout-shifts&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You can&#39;t fix layout shifts that you can&#39;t reproduce. One of the simplest, yet
most effective things you can do to get a better sense of your site&#39;s layout
stability is take 5-10 minutes to interact with your site with the goal
triggering layout shifts. Keep the console open while doing this and use the
Layout Instability API to report on layout shifts.&lt;/p&gt;
&lt;p&gt;For hard to locate layout shifts, consider repeating this exercise with
different devices and connection speeds. In particular, using a slower
connection speed can make it easier to identify layout shifts. In addition,
you can use a &lt;code&gt;debugger&lt;/code&gt; statement to make it easier to step through layout
shifts.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;highlight-line&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;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&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;/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;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hadRecentInput&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;      cls &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token keyword&quot;&gt;debugger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;      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;Current CLS value:&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cls&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;/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&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 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;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Lastly, for layout issues that aren&#39;t reproducible in development, consider
using the Layout Instability API in conjunction with your front-end logging tool
of choice to collect more information on these issues. Check out
the example &lt;a href=&quot;https://github.com/GoogleChromeLabs/web-vitals-report/blob/71b0879334798c732f460945ded5267cab5a36bf/src/js/analytics.js#L104-L118&quot; rel=&quot;noopener&quot;&gt;code for how to track the largest shifted element on a page&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Best practices for carousels</title>
    <link href="https://web.dev/carousel-best-practices/"/>
    <updated>2021-01-26T00:00:00Z</updated>
    <id>https://web.dev/carousel-best-practices/</id>
    <content type="html" mode="escaped">&lt;p&gt;A carousel is a UX component that displays content in slideshow-like manner.
Carousels can &amp;quot;autoplay&amp;quot; or be navigated manually by users. Although carousels
can be used elsewhere, they are most frequently used to display images,
products, and promotions on homepages.&lt;/p&gt;
&lt;p&gt;This article discusses performance and UX best practices for carousels.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Image showing a carousel&quot; decoding=&quot;async&quot; height=&quot;420&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 629px) 629px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/u2FlXalClwBeDOBBiwxu.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/u2FlXalClwBeDOBBiwxu.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/u2FlXalClwBeDOBBiwxu.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/u2FlXalClwBeDOBBiwxu.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/u2FlXalClwBeDOBBiwxu.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/u2FlXalClwBeDOBBiwxu.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/u2FlXalClwBeDOBBiwxu.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/u2FlXalClwBeDOBBiwxu.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/u2FlXalClwBeDOBBiwxu.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/u2FlXalClwBeDOBBiwxu.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/u2FlXalClwBeDOBBiwxu.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/u2FlXalClwBeDOBBiwxu.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/u2FlXalClwBeDOBBiwxu.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/u2FlXalClwBeDOBBiwxu.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/u2FlXalClwBeDOBBiwxu.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/u2FlXalClwBeDOBBiwxu.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/u2FlXalClwBeDOBBiwxu.png?auto=format&amp;w=1258 1258w&quot; width=&quot;629&quot; /&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;performance&quot;&gt;Performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/carousel-best-practices/#performance&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A well-implemented carousel, in and of itself, should have very minimal or no
impact on performance. However, carousels often contain large media assets.
Large assets can impact performance regardless of whether they are displayed in
a carousel or elsewhere.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;LCP (Largest Contentful Paint)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Large, above-the-fold carousels often contain the page&#39;s LCP element, and
therefore can have a significant impact on LCP. In these scenarios,
optimizing the carousel may significantly improve LCP. For an in-depth
explanation of how LCP measurement works on pages containing carousels,
refer to the &lt;a href=&quot;https://web.dev/carousel-best-practices/#lcp-measurement-for-carousels&quot;&gt;LCP measurement for carousels&lt;/a&gt;
section.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;FID (First Input Delay)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Carousels have minimal JavaScript requirements and therefore should not
impact page interactivity. If you discover that your site&#39;s carousel has
long-running scripts, you should consider replacing your carousel tooling.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;CLS (Cumulative Layout Shift)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A surprising number of carousels use janky, non-composited animations that
can contribute to CLS. On pages with autoplaying carousels, this has the
potential to cause infinite CLS. This type of CLS typically isn&#39;t apparent
to the human eye, which makes the issue easy to overlook. To avoid this
issue, &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/non-composited-animations/&quot; rel=&quot;noopener&quot;&gt;avoid using non-composited animations&lt;/a&gt;
in your carousel (for example, during slide transitions).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;performance-best-practices&quot;&gt;Performance best practices &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/carousel-best-practices/#performance-best-practices&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;load-carousel-content-using-html&quot;&gt;Load carousel content using HTML &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/carousel-best-practices/#load-carousel-content-using-html&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Carousel content should be loaded via the page&#39;s HTML markup so that it is
discoverable by the browser early in the page load process. Using JavaScript to
initiate the loading of carousel content is probably the single biggest
performance mistake to avoid when using carousels. This delays image loading and
can negatively impact LCP.&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;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;slides&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://example.com/cat1.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://example.com/cat2.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://example.com/cat3.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/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-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; slides &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;&quot;.slides&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 keyword&quot;&gt;const&lt;/span&gt; newSlide &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;img&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;newSlide&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;htttp://example.com/cat1.jpg&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;slides&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newSlide&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;For advanced carousel optimization, consider loading the first slide statically,
then progressively enhancing it to include navigation controls and additional
content. This technique is most applicable to environments where you have a
user&#39;s prolonged attention—this gives the additional content time to load. In
environments like home pages, where users may only stick around for a second or
two, only loading a single image may be similarly effective.&lt;/p&gt;
&lt;h3 id=&quot;avoid-layout-shifts&quot;&gt;Avoid layout shifts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/carousel-best-practices/#avoid-layout-shifts&quot;&gt;#&lt;/a&gt;&lt;/h3&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 88-90 shipped a variety of &lt;a href=&quot;https://chromium.googlesource.com/chromium/src/+/master/docs/speed/metrics_changelog/cls.md&quot;&gt;bug fixes&lt;/a&gt; related to how layout shifts are calculated. Many of these bug fixes are relevant to carousels. As a result of these fixes, sites should expect to see lower carousel-related layout shift scores in later versions of Chrome.  &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Slide transitions and navigation controls are the two most common sources of
layout shifts in carousels:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Slide transitions:&lt;/strong&gt; Layout shifts that occur during slide transitions are
usually caused by updating the layout-inducing properties of DOM elements.
Examples of some of these properties include: &lt;code&gt;left&lt;/code&gt;, &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;width&lt;/code&gt;, and
&lt;code&gt;marginTop&lt;/code&gt;. To avoid layout shifts, instead use the CSS
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/transform&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;transform&lt;/code&gt;&lt;/a&gt;
property to transition these elements. This
&lt;a href=&quot;https://glitch.com/~basic-carousel&quot; rel=&quot;noopener&quot;&gt;demo&lt;/a&gt; shows how to use &lt;code&gt;transform&lt;/code&gt; to
build a basic carousel.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Navigation controls:&lt;/strong&gt; Moving or adding/removing carousel navigation
controls from the DOM can cause layout shifts depending on how these changes
are implemented. Carousels that exhibit this behavior typically do so in
response to user hover.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are some of the common points of confusion regarding CLS measurement for
carousels:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Autoplay carousels:&lt;/strong&gt; Slide transitions are the most common source of
carousel-related layout shifts. In a non-autoplay carousel these layout shifts
typically occur within 500ms of a user interaction and &lt;a href=&quot;https://web.dev/cls/#expected-vs-unexpected-layout-shifts&quot;&gt;therefore do not count
towards Cumulative Layout Shift
(CLS)&lt;/a&gt;. However,
for autoplay carousels, not only can these layout shifts potentially count
towards CLS - but they can also repeat indefinitely. Thus, it is particularly
important to verify that an autoplay carousel is not a source of layout
shifts.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Scrolling:&lt;/strong&gt; Some carousels allow users to use scrolling to navigate through
carousel slides. If an element&#39;s start position changes but its scroll offset
(that is,
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Element/scrollLeft&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;scrollLeft&lt;/code&gt;&lt;/a&gt;
or
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Element/scrollTop&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;scrollTop&lt;/code&gt;&lt;/a&gt;)
changes by the same amount (but in the opposite direction) this is not
considered a layout shift provided that they occur in the same frame.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more information on layout shifts, see &lt;a href=&quot;https://web.dev/debug-layout-shifts/#identifying-the-cause-of-a-layout-shift&quot;&gt;Debug layout
shifts&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;use-modern-technology&quot;&gt;Use modern technology &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/carousel-best-practices/#use-modern-technology&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Many sites use &lt;a href=&quot;https://web.dev/third-party-javascript&quot;&gt;third-party JavaScript&lt;/a&gt; libraries to
implement carousels. If you currently use older carousel tooling, you may be
able to improve performance by switching to newer tooling. Newer tools tend to
use more efficient APIs and are less likely to require additional dependencies
like jQuery.&lt;/p&gt;
&lt;p&gt;However, depending on the type of carousel you are building, you may not need
JavaScript at all. The new &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/CSS_Scroll_Snap&quot; rel=&quot;noopener&quot;&gt;Scroll
Snap&lt;/a&gt; API
makes it possible to implement carousel-like transitions using only HTML and
CSS.&lt;/p&gt;
&lt;p&gt;Here are some resources on using &lt;code&gt;scroll-snap&lt;/code&gt; that you may find helpful:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/building-a-stories-component/&quot;&gt;Building a Stories component (web.dev)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/next-gen-css-2019/#scroll-snap&quot;&gt;Next-generation web styling: scroll snap (web.dev)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/css-only-carousel/&quot; rel=&quot;noopener&quot;&gt;CSS-Only Carousel (CSS Tricks)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/how-to-make-a-css-only-carousel/&quot; rel=&quot;noopener&quot;&gt;How to Make a CSS-Only Carousel (CSS Tricks)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;optimize-carousel-content&quot;&gt;Optimize carousel content &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/carousel-best-practices/#optimize-carousel-content&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Carousels often contain some of a site&#39;s largest images, so it can be worth your
time to make sure that these images are fully optimized. Choosing the right
image format and compression level, &lt;a href=&quot;https://web.dev/image-cdns&quot;&gt;using an image CDN&lt;/a&gt;, and
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/CSS_Scroll_Snap&quot; rel=&quot;noopener&quot;&gt;using srcset to serve multiple image
versions&lt;/a&gt; are
all techniques that can reduce the transfer size of images.&lt;/p&gt;
&lt;h2 id=&quot;performance-measurement&quot;&gt;Performance measurement &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/carousel-best-practices/#performance-measurement&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This section discusses LCP measurement as it relates to carousels. Although
carousels are treated no differently than any other UX element during LCP
calculation, the mechanics of calculating LCP for autoplaying carousels is a
common point of confusion.&lt;/p&gt;
&lt;h3 id=&quot;lcp-measurement-for-carousels&quot;&gt;LCP measurement for carousels &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/carousel-best-practices/#lcp-measurement-for-carousels&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;These are the key points to understanding how LCP calculation works for carousels:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LCP considers page elements as they are painted to the frame. New candidates
for the LCP element are no longer considered once the user interacts (taps,
scrolls, or keypresses) with the page. Thus, any slide in an autoplaying
carousel has the potential to be the final LCP element—whereas in a static
carousel only the first slide would be a potential LCP candidate.&lt;/li&gt;
&lt;li&gt;If two equally sized images are rendered, the first image will be considered
the LCP element. The LCP element is only updated when the LCP candidate is
larger than the current LCP element. Thus, if all carousel elements are
equally sized, the LCP element should be the first image that is displayed.&lt;/li&gt;
&lt;li&gt;When evaluating LCP candidates, LCP considers the &amp;quot;&lt;a href=&quot;https://web.dev/lcp&quot;&gt;visible size or the
intrinsic size, whichever is smaller&lt;/a&gt;.&amp;quot; Thus, if an autoplaying
carousel displays images at a consistent size, but contains images of
varying &lt;a href=&quot;https://developer.mozilla.org/docs/Glossary/Intrinsic_Size&quot; rel=&quot;noopener&quot;&gt;intrinsic
sizes&lt;/a&gt;
that are smaller than the display size, the LCP element may change as new
slides are displayed. In this case, if all images are displayed at the same
size, the image with the largest intrinsic size will be considered the LCP
element. To keep LCP low, you should ensure that all items in an autoplaying
carousel are the same intrinsic size.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;changes-to-lcp-calculation-for-carousels-in-chrome-88&quot;&gt;Changes to LCP calculation for carousels in Chrome 88 &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/carousel-best-practices/#changes-to-lcp-calculation-for-carousels-in-chrome-88&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As of &lt;a href=&quot;https://chromium.googlesource.com/chromium/src/+/master/docs/speed/metrics_changelog/2020_11_lcp.md&quot; rel=&quot;noopener&quot;&gt;Chrome
88&lt;/a&gt;,
images that are later removed from the DOM are considered as potential largest
contentful paints. Prior to Chrome 88, these images were excluded from
consideration. For sites that use autoplaying carousels, this definition change
will either have a neutral or positive impact on LCP scores.&lt;/p&gt;
&lt;p&gt;This change was made in response to the
&lt;a href=&quot;https://github.com/anniesullie/LCP_Examples/tree/master/removed_from_dom&quot; rel=&quot;noopener&quot;&gt;observation&lt;/a&gt;
that many sites implement carousel transitions by removing the previously
displayed image from the DOM tree. Prior to Chrome 88, each time that a new
slide was presented, the removal of the previous element would trigger an LCP
update. This change only affects autoplaying carousels-by definition, potential
largest contentful paints can only occur before a user first interacts with the
page.&lt;/p&gt;
&lt;h2 id=&quot;other-considerations&quot;&gt;Other considerations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/carousel-best-practices/#other-considerations&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This section discusses UX and product best practices that you should keep in
mind when implementing carousels. Carousels should advance your business goals
and present content in a way that is easy to navigate and read.&lt;/p&gt;
&lt;h3 id=&quot;navigation-best-practices&quot;&gt;Navigation best practices &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/carousel-best-practices/#navigation-best-practices&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;provide-prominent-navigation-controls&quot;&gt;Provide prominent navigation controls &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/carousel-best-practices/#provide-prominent-navigation-controls&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Carousel navigation controls should be easy to click and highly visible. This is
something that is rarely done well-most carousels have navigation controls that
are both small and subtle. Keep in mind that a single color or style of
navigation control will rarely work in all situations. For example, an arrow
that is clearly visible against a dark background might be difficult to see
against a light background.&lt;/p&gt;
&lt;h4 id=&quot;indicate-navigation-progress&quot;&gt;Indicate navigation progress &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/carousel-best-practices/#indicate-navigation-progress&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Carousel navigation controls should provide context about the total number of
slides and the user&#39;s progress through them. This information makes it easier
for the user to navigate to a particular slide and understand which content has
already been viewed. In some situations providing a preview of upcoming
content—whether it be an excerpt of the next slide or a list of thumbnails-can
also be helpful and increase engagement.&lt;/p&gt;
&lt;h4 id=&quot;support-mobile-gestures&quot;&gt;Support mobile gestures &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/carousel-best-practices/#support-mobile-gestures&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;On mobile, swipe gestures should be supported in addition to traditional
navigation controls (such as on screen buttons).&lt;/p&gt;
&lt;h4 id=&quot;provide-alternate-navigation-paths&quot;&gt;Provide alternate navigation paths &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/carousel-best-practices/#provide-alternate-navigation-paths&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Because it&#39;s unlikely that most users will engage with all carousel content, the
content that carousel slides link to should be accessible from other navigation
paths.&lt;/p&gt;
&lt;h3 id=&quot;readability-best-practices&quot;&gt;Readability best practices &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/carousel-best-practices/#readability-best-practices&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;dont-use-autoplay&quot;&gt;Don&#39;t use autoplay &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/carousel-best-practices/#dont-use-autoplay&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The use of autoplay creates two almost paradoxical problems: on-screen
animations tend to distract users and move the eyes away from more important
content; simultaneously, because users often associate animations with ads, they
will ignore carousels that autoplay.&lt;/p&gt;
&lt;p&gt;Thus, it&#39;s rare that autoplay is a good choice. If content is important, not
using autoplay will maximize its exposure; if carousel content is not important,
then the use of autoplay will detract from more important content. In addition,
autoplaying carousels can be difficult to read (and annoying, too). People read
at different speeds, so it&#39;s rare that a carousel consistently transitions at
the &amp;quot;right&amp;quot; time for different users.&lt;/p&gt;
&lt;p&gt;Ideally, slide navigation should be user-directed via navigation controls. If
you must use autoplay, autoplay should be disabled on user hover. In addition,
the slide transition rate should take slide content into account-the more text
that a slide contains, the longer it should be displayed on screen.&lt;/p&gt;
&lt;h4 id=&quot;keep-text-and-images-separate&quot;&gt;Keep text and images separate &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/carousel-best-practices/#keep-text-and-images-separate&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Carousel text content is often &amp;quot;baked into&amp;quot; the corresponding image file, rather
than displayed separately using HTML markup. This approach is bad for
accessibility, localization, and compression rates. It also encourages a
one-size-fits-all approach to asset creation. However, the same image and text
formatting is rarely equally readable across desktop and mobile formats.&lt;/p&gt;
&lt;h4 id=&quot;be-concise&quot;&gt;Be concise &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/carousel-best-practices/#be-concise&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;You only have a fraction of a second to catch a user&#39;s attention. Short,
to-the-point copy will increase the odds that your message gets across.&lt;/p&gt;
&lt;h3 id=&quot;product-best-practices&quot;&gt;Product best practices &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/carousel-best-practices/#product-best-practices&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Carousels work well in situations where using additional vertical space to
display additional content is not an option. Carousels on product pages are
often a good example of this use case.&lt;/p&gt;
&lt;p&gt;However, carousels are not always used effectively.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Carousels, particularly if they contain promotions or advance automatically,
are easily &lt;a href=&quot;https://www.nngroup.com/articles/auto-forwarding/&quot; rel=&quot;noopener&quot;&gt;mistaken&lt;/a&gt; for
advertisements by users. Users tend to ignore advertisements—a phenomenon
known as &lt;a href=&quot;https://www.nngroup.com/articles/banner-blindness-old-and-new-findings/&quot; rel=&quot;noopener&quot;&gt;banner
blindness&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Carousels are often used to placate multiple departments and avoid making
decisions about business priorities. As a result, carousels can easily turn
into a dumping ground for ineffective content.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;test-your-assumptions&quot;&gt;Test your assumptions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/carousel-best-practices/#test-your-assumptions&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The business impact of carousels, particularly those on homepages, should be
evaluated and tested. Carousel clickthrough rates can help you determine whether
a carousel and its content is effective.&lt;/p&gt;
&lt;h4 id=&quot;be-relevant&quot;&gt;Be relevant &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/carousel-best-practices/#be-relevant&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Carousels work best when they contain interesting and relevant content that is
presented with a clear context. If content wouldn&#39;t engage a user outside of a
carousel—placing it in a carousel won&#39;t make it perform any better. If you must
use a carousel, prioritize content and ensure that each slide is sufficiently
relevant that a user would want to click through to the subsequent slide.&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Signed Exchanges (SXGs)</title>
    <link href="https://web.dev/signed-exchanges/"/>
    <updated>2020-10-14T00:00:00Z</updated>
    <id>https://web.dev/signed-exchanges/</id>
    <content type="html" mode="escaped">&lt;p&gt;Signed exchanges (SXG) are a delivery mechanism that makes it possible to authenticate the origin of a resource independently of how it was delivered. Implementing SXG can improve Largest Contentful Paint (LCP) by enabling &lt;a href=&quot;https://web.dev/signed-exchanges/#speeding-up-page-loads-with-signed-exchanges&quot;&gt;privacy-preserving cross-origin prefetch&lt;/a&gt;. Additionally, this decoupling advances a variety of use cases such as offline internet experiences and serving from third-party caches.&lt;/p&gt;
&lt;p&gt;This article provides a comprehensive overview of SXG: how it works, use cases, and tooling.&lt;/p&gt;
&lt;h2 id=&quot;browser-compatibility&quot;&gt;Browser compatibility &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#browser-compatibility&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;SXG is &lt;a href=&quot;https://caniuse.com/#feat=sxg&quot; rel=&quot;noopener&quot;&gt;supported&lt;/a&gt; by Chromium-based browsers
(starting with versions: Chrome 73, Edge 79, and Opera 64).&lt;/p&gt;
&lt;h2 id=&quot;overview&quot;&gt;Overview &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#overview&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As its primary use case, SXG uses a cache to prefetch and serve content that has been cryptographically signed by the origin. This helps speed cross origin navigations from referer sites while also ensuring that pages remain unaltered and properly attributed to their origin. Any potentially identifying information is hidden until after the user navigates to a site thereby protecting the user’s privacy.
Google Search is an early adopter of SXG prefetching capabilities and for sites that receive a large portion of their traffic from Google Search, SXG can be an important tool for delivering faster page loads to users. Over time, we hope this impact will expand to additional referrers.&lt;/p&gt;
&lt;h3 id=&quot;how-it-works&quot;&gt;How it Works &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#how-it-works&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A site signs a request/response pair (an &amp;quot;HTTP exchange&amp;quot;) in a way that makes it possible for
the browser to verify the origin and integrity of the content independently of
how the content was distributed. As a result, the browser can display the URL of
the origin site in the address bar, rather than the URL of the server that
delivered the content.&lt;/p&gt;
&lt;img alt=&quot;Diagram explaining how Signed Exchanges Works. Browser communicating with the cache which communicates with the destination site&quot; decoding=&quot;async&quot; height=&quot;392&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/GiC3si2rdySvEXRggAJBPhgTHFr2/kGUL8NalZ2OaC81a5q8g.PNG?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/GiC3si2rdySvEXRggAJBPhgTHFr2/kGUL8NalZ2OaC81a5q8g.PNG?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/GiC3si2rdySvEXRggAJBPhgTHFr2/kGUL8NalZ2OaC81a5q8g.PNG?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/GiC3si2rdySvEXRggAJBPhgTHFr2/kGUL8NalZ2OaC81a5q8g.PNG?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/GiC3si2rdySvEXRggAJBPhgTHFr2/kGUL8NalZ2OaC81a5q8g.PNG?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/GiC3si2rdySvEXRggAJBPhgTHFr2/kGUL8NalZ2OaC81a5q8g.PNG?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/GiC3si2rdySvEXRggAJBPhgTHFr2/kGUL8NalZ2OaC81a5q8g.PNG?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/GiC3si2rdySvEXRggAJBPhgTHFr2/kGUL8NalZ2OaC81a5q8g.PNG?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/GiC3si2rdySvEXRggAJBPhgTHFr2/kGUL8NalZ2OaC81a5q8g.PNG?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/GiC3si2rdySvEXRggAJBPhgTHFr2/kGUL8NalZ2OaC81a5q8g.PNG?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/GiC3si2rdySvEXRggAJBPhgTHFr2/kGUL8NalZ2OaC81a5q8g.PNG?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/GiC3si2rdySvEXRggAJBPhgTHFr2/kGUL8NalZ2OaC81a5q8g.PNG?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/GiC3si2rdySvEXRggAJBPhgTHFr2/kGUL8NalZ2OaC81a5q8g.PNG?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/GiC3si2rdySvEXRggAJBPhgTHFr2/kGUL8NalZ2OaC81a5q8g.PNG?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/GiC3si2rdySvEXRggAJBPhgTHFr2/kGUL8NalZ2OaC81a5q8g.PNG?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/GiC3si2rdySvEXRggAJBPhgTHFr2/kGUL8NalZ2OaC81a5q8g.PNG?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/GiC3si2rdySvEXRggAJBPhgTHFr2/kGUL8NalZ2OaC81a5q8g.PNG?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/GiC3si2rdySvEXRggAJBPhgTHFr2/kGUL8NalZ2OaC81a5q8g.PNG?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Historically, the only way for a
site to use a third-party to distribute its content while maintaining
attribution has been for the site to share its SSL certificates with the
distributor. This has security drawbacks; moreover, it is a far stretch from
making content truly portable.&lt;/p&gt;
&lt;h3 id=&quot;the-sxg-format&quot;&gt;The SXG format &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#the-sxg-format&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;An SXG is encapsulated in a &lt;a href=&quot;https://cbor.io/&quot; rel=&quot;noopener&quot;&gt;binary-encoded&lt;/a&gt; file that has two
primary components: an HTTP exchange and a
&lt;a href=&quot;https://developer.mozilla.org/docs/Glossary/Signature/Security&quot; rel=&quot;noopener&quot;&gt;signature&lt;/a&gt;
that covers the exchange. The HTTP exchange consists of a request URL, content
negotiation information, and an HTTP response.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;
  Here&#39;s an example of a decoded SXG file.
&lt;/summary&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;format version: 1b3&lt;br /&gt;request:&lt;br /&gt;  method: GET&lt;br /&gt;  uri: https://example.org/&lt;br /&gt;  headers:&lt;br /&gt;response:&lt;br /&gt;  status: 200&lt;br /&gt;  headers:&lt;br /&gt;    Cache-Control: max-age=604800&lt;br /&gt;    Digest: mi-sha256-03=kcwVP6aOwYmA/j9JbUU0GbuiZdnjaBVB/1ag6miNUMY=&lt;br /&gt;    Expires: Mon, 24 Aug 2020 16:08:24 GMT&lt;br /&gt;    Content-Type: text/html; charset=UTF-8&lt;br /&gt;    Content-Encoding: mi-sha256-03&lt;br /&gt;    Date: Mon, 17 Aug 2020 16:08:24 GMT&lt;br /&gt;    Vary: Accept-Encoding&lt;br /&gt;signature:&lt;br /&gt;    label;cert-sha256=*ViFgi0WfQ+NotPJf8PBo2T5dEuZ13NdZefPybXq/HhE=*;&lt;br /&gt;    cert-url=&quot;https://test.web.app/ViFgi0WfQ-NotPJf8PBo2T5dEuZ13NdZefPybXq_HhE&quot;;&lt;br /&gt;    date=1597680503;expires=1598285303;integrity=&quot;digest/mi-sha256-03&quot;;sig=*MEUCIQD5VqojZ1ujXXQaBt1CPKgJxuJTvFlIGLgkyNkC6d7LdAIgQUQ8lC4eaoxBjcVNKLrbS9kRMoCHKG67MweqNXy6wJg=*;&lt;br /&gt;    validity-url=&quot;https://example.org/webpkg/validity&quot;&lt;br /&gt;header integrity: sha256-Gl9bFHnNvHppKsv+bFEZwlYbbJ4vyf4MnaMMvTitTGQ=&lt;br /&gt;&lt;br /&gt;The exchange has a valid signature.&lt;br /&gt;payload [1256 bytes]:&lt;br /&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;doctype&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&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;html&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;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;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;SXG example&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;title&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;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;charset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;utf-8&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;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;http-equiv&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Content-type&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text/html; charset=utf-8&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;style&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;text/css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;&lt;br /&gt;    &lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #f0f0f2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;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;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Hello&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&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;html&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;/details&gt;
&lt;p&gt;The &lt;code&gt;expires&lt;/code&gt; parameter in the signature indicates a SXG&#39;s expiration date. A
SXG may be valid for at most 7 days. Find more information on
the signature header in the &lt;a href=&quot;https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#section-3.1&quot; rel=&quot;noopener&quot;&gt;Signed HTTP Exchanges
spec&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;support-for-server-side-personalization&quot;&gt;Support for server-side personalization &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#support-for-server-side-personalization&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;An SXG containing a &lt;code&gt;Vary: Cookie&lt;/code&gt; header will be shown only to users who don&#39;t
have cookies for the signed request URL. If your site presents different HTML
to its logged-in users, you can use this feature to take advantage of SXGs
without altering that experience. See details on &lt;a href=&quot;https://web.dev/blog/sxg-desktop/#support-for-server-side-personalization&quot;&gt;server-side personalization
with Dynamic SXG&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;web-packaging&quot;&gt;Web Packaging &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#web-packaging&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;SXG is part of the broader &lt;a href=&quot;https://github.com/WICG/webpackage&quot; rel=&quot;noopener&quot;&gt;Web
Packaging&lt;/a&gt; spec proposal family. In addition
to SXGs, the other major component of the Web Packaging spec is &lt;a href=&quot;https://web.dev/web-bundles/&quot;&gt;Web Bundles&lt;/a&gt;
(&amp;quot;bundled HTTP exchanges&amp;quot;). Web Bundles are a collection of HTTP resources and
the metadata necessary to interpret the bundle.&lt;/p&gt;
&lt;p&gt;The relationship between SXGs and Web Bundles is a common point of confusion.
SXG and Web Bundles are two distinct technologies that don&#39;t depend on each
other—Web Bundles can be used with both signed and unsigned exchanges. A common
goal advanced by both SXGs and Web Bundles is the creation of a &amp;quot;web packaging&amp;quot;
format that allows sites to be shared in their entirety for offline consumption.&lt;/p&gt;
&lt;h2 id=&quot;speeding-up-page-loads-with-signed-exchanges&quot;&gt;Speeding up Page Loads with Signed Exchanges &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#speeding-up-page-loads-with-signed-exchanges&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Enabling Signed Exchanges can help speed up web page performance and thereby impact your site’s Core Web Vitals, in Particular &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt;. As an early adopter, Google Search uses SXG to provide users with a faster page load experience for pages loaded from the search results page.&lt;/p&gt;
&lt;p&gt;Google Search crawls and caches SXGs when available and prefetches SXG that the user is likely to visit—for example, the page corresponding to the first search result.&lt;/p&gt;
&lt;p&gt;SXG works best in tandem with other performance optimizations such as use of CDNs and reduction of render-blocking subresources. After implementing, follow &lt;a href=&quot;https://developer.chrome.com/blog/optimizing-lcp-using-signed-exchanges/&quot; rel=&quot;noopener&quot;&gt;these recommendations&lt;/a&gt; to maximize the LCP benefit from prefetching SXGs. In many cases, such optimization can result in nearly instant page loads coming from Google Search:&lt;/p&gt;
&lt;p&gt;&lt;video controls=&quot;&quot; poster=&quot;https://web-dev.imgix.net/image/rULxC7pPw3PFS4o9xr7v8isFmCv1/cdP5lEt76GS8N3Bix2X2.jpg?auto=format&quot;&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/rULxC7pPw3PFS4o9xr7v8isFmCv1/MQwtXWQD41XWNTzRHLie.mp4&quot; type=&quot;video/mp4&quot; /&gt;    &lt;/video&gt;&lt;/p&gt;
&lt;h3 id=&quot;impact-of-signed-exchanges&quot;&gt;Impact of Signed Exchanges &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#impact-of-signed-exchanges&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;From past experiments we have observed an average of 300ms to 400ms reduction in LCP from SXG-enabled prefetches. This helps sites make a better first-impression on users and often has a positive impact on business metrics.&lt;/p&gt;
&lt;p&gt;Several global brands and sites have already benefited from Signed Exchanges. As a case study, lets look at how implementing Signed Exchanges helped &lt;a href=&quot;https://www.rebelmouse.com/signed-exchange?utm_medium=post&amp;amp;utm_source=google&amp;amp;utm_campaign=signed-exchange-co-marketing&amp;amp;utm_content=signed-exchange&quot; rel=&quot;noopener&quot;&gt;RebelMouse&lt;/a&gt;, a prominent Content Management System (CMS), improve their customers&#39; performance and business metrics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Narcity &lt;strong&gt;improved LCP by 41%&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Paper Magazine noticed a &lt;strong&gt;27% increase in Sessions per user&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;MLT Blog &lt;strong&gt;decreased Page Load time by 21%&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.cloudflare.com/automatic-signed-exchanges-desktop-android/&quot; rel=&quot;noopener&quot;&gt;Cloudflare found&lt;/a&gt; that SXG &lt;strong&gt;improved TTFB for 98% of sites&lt;/strong&gt; it tested, and &lt;strong&gt;improved LCP for 85% of sites&lt;/strong&gt;, with a median improvement of over 20% in SXG-eligible page loads.&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; SXG enables &lt;a href=&quot;https://developer.chrome.com/blog/cross-origin-prefetch/&quot;&gt;cross-origin prefetching&lt;/a&gt; because it allows prefetch requests via an SXG cache server operated by the referrer, ensuring that the user&#39;s potentially identifying information is not revealed to the origin server until they navigate. This technique is available to any site that wishes to make its outlinks faster or more resilient to limited network access. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;indexing&quot;&gt;Indexing &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#indexing&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The SXG and non-SXG representations of a page are not ranked or indexed
differently by Google Search. SXG is ultimately a delivery mechanism—it does not
change the underlying content.&lt;/p&gt;
&lt;h3 id=&quot;amp&quot;&gt;AMP &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#amp&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;AMP content can be delivered using SXG. SXG allows AMP content to be prefetched
and displayed using its canonical URL, rather than its AMP URL.AMP has its own separate
&lt;a href=&quot;https://github.com/ampproject/amppackager&quot; rel=&quot;noopener&quot;&gt;tooling&lt;/a&gt; for generating SXGs.Learn how to serve AMP using signed exchanges on
&lt;a href=&quot;https://amp.dev/documentation/guides-and-tutorials/optimize-and-measure/signed-exchange/&quot; rel=&quot;noopener&quot;&gt;amp.dev&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;debugging&quot;&gt;Debugging SXGs with Chrome DevTools &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#debugging&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To see a SXG firsthand, use a Chromium browser, open DevTools, open the Network panel, and visit this &lt;a href=&quot;https://www.google.com/search?q=site%3Asigned-exchange-testing.dev+valid&quot; rel=&quot;noopener&quot;&gt;example search page&lt;/a&gt;. Signed Exchanges can be identified by looking for &lt;code&gt;signed-exchange&lt;/code&gt; in the &lt;strong&gt;Type&lt;/strong&gt; column.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot showing a SXG request within the &amp;#x27;Network&amp;#x27; panel in DevTools&quot; decoding=&quot;async&quot; height=&quot;201&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 696px) 696px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/cNdohSaeXqGHFBwD7L3B.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/cNdohSaeXqGHFBwD7L3B.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/cNdohSaeXqGHFBwD7L3B.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/cNdohSaeXqGHFBwD7L3B.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/cNdohSaeXqGHFBwD7L3B.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/cNdohSaeXqGHFBwD7L3B.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/cNdohSaeXqGHFBwD7L3B.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/cNdohSaeXqGHFBwD7L3B.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/cNdohSaeXqGHFBwD7L3B.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/cNdohSaeXqGHFBwD7L3B.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/cNdohSaeXqGHFBwD7L3B.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/cNdohSaeXqGHFBwD7L3B.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/cNdohSaeXqGHFBwD7L3B.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/cNdohSaeXqGHFBwD7L3B.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/cNdohSaeXqGHFBwD7L3B.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/cNdohSaeXqGHFBwD7L3B.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/cNdohSaeXqGHFBwD7L3B.png?auto=format&amp;w=1392 1392w&quot; width=&quot;696&quot; /&gt;
  &lt;figcaption&gt;The &lt;b&gt;Network&lt;/b&gt; panel in DevTools&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The &lt;strong&gt;Preview&lt;/strong&gt; tab provides more information about the contents of a SXG.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of the &amp;#x27;Preview&amp;#x27; tab for a SXG&quot; decoding=&quot;async&quot; height=&quot;561&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/E0rBwuxk4BxFmLJ3gXhP.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/E0rBwuxk4BxFmLJ3gXhP.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/E0rBwuxk4BxFmLJ3gXhP.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/E0rBwuxk4BxFmLJ3gXhP.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/E0rBwuxk4BxFmLJ3gXhP.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/E0rBwuxk4BxFmLJ3gXhP.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/E0rBwuxk4BxFmLJ3gXhP.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/E0rBwuxk4BxFmLJ3gXhP.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/E0rBwuxk4BxFmLJ3gXhP.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/E0rBwuxk4BxFmLJ3gXhP.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/E0rBwuxk4BxFmLJ3gXhP.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/E0rBwuxk4BxFmLJ3gXhP.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/E0rBwuxk4BxFmLJ3gXhP.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/E0rBwuxk4BxFmLJ3gXhP.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/E0rBwuxk4BxFmLJ3gXhP.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/E0rBwuxk4BxFmLJ3gXhP.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/E0rBwuxk4BxFmLJ3gXhP.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/E0rBwuxk4BxFmLJ3gXhP.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;The &lt;b&gt;Preview&lt;/b&gt; tab in DevTools&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;tooling&quot;&gt;Tooling &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#tooling&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Implementing SXGs consists of generating the SXG corresponding to a given URL
and then serving that SXG to requestors (usually crawlers).&lt;/p&gt;
&lt;h3 id=&quot;certificates&quot;&gt;Certificates &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#certificates&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To generate a SXG you will need a certificate that can sign SXGs, although some tools acquire these automatically. &lt;a href=&quot;https://github.com/google/webpackager/wiki/Certificate-Authorities&quot; rel=&quot;noopener&quot;&gt;This page&lt;/a&gt; lists the certificate authorities that can issue this type of certificate.
Certificates can be obtained automatically from the Google certificate authority using any ACME client. Web Packager Server has a built-in ACME client, and sxg-rs will soon.&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; Production use of SXGs requires a certificate that supports the &lt;code&gt;CanSignHttpExchanges&lt;/code&gt; extension. Per &lt;a href=&quot;https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#section-4.2&quot;&gt;spec&lt;/a&gt;, certificates with this extension must have a validity period no longer than 90 days and require that the requesting domain have a &lt;a href=&quot;https://en.wikipedia.org/wiki/DNS_Certification_Authority_Authorization&quot;&gt;DNS CAA record&lt;/a&gt; configured. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;platform-specific-sxg-tooling&quot;&gt;Platform-specific SXG tooling &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#platform-specific-sxg-tooling&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;These tools support specific technology stacks. If you are already using a
platform supported by one of these tools, you may find it easier to set up than
a general-purpose tool.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/google/sxg-rs/tree/main/cloudflare_worker&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;sxg-rs/cloudflare_worker&lt;/code&gt;&lt;/a&gt;
runs on &lt;a href=&quot;https://workers.cloudflare.com/&quot; rel=&quot;noopener&quot;&gt;Cloudflare Workers&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/google/sxg-rs/tree/main/fastly_compute&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;sxg-rs/fastly_compute&lt;/code&gt;&lt;/a&gt;
runs on &lt;a href=&quot;https://www.fastly.com/products/edge-compute/serverless&quot; rel=&quot;noopener&quot;&gt;Fastly
Compute@Edge&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.cloudflare.com/automatic-signed-exchanges/&quot; rel=&quot;noopener&quot;&gt;Automatic Signed
Exchanges&lt;/a&gt; is a
Cloudflare feature that automatically acquires certificates and generates
Signed Exchanges.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/google/nginx-sxg-module&quot; rel=&quot;noopener&quot;&gt;NGINX SXG module&lt;/a&gt; generates
and serves SXGs for sites that use &lt;a href=&quot;https://nginx.org/&quot; rel=&quot;noopener&quot;&gt;nginx&lt;/a&gt;. Setup
instructions can be found &lt;a href=&quot;https://web.dev/how-to-set-up-signed-http-exchanges/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/sxg_filter&quot; rel=&quot;noopener&quot;&gt;Envoy SXG
Filter&lt;/a&gt;
generates and serves SXGs for sites that use
&lt;a href=&quot;https://www.envoyproxy.io/&quot; rel=&quot;noopener&quot;&gt;Envoy&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;general-purpose-sxg-tooling&quot;&gt;General-purpose SXG tooling &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#general-purpose-sxg-tooling&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;sxg-rs-http-server&quot;&gt;sxg-rs HTTP server &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#sxg-rs-http-server&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/google/webpackager/blob/main/cmd/webpkgserver/README.md&quot; rel=&quot;noopener&quot;&gt;sxg-rs
http_server&lt;/a&gt;
acts as a &lt;a href=&quot;https://en.wikipedia.org/wiki/Reverse_proxy&quot; rel=&quot;noopener&quot;&gt;reverse proxy&lt;/a&gt; for
serving SXGs. For requests from SXG crawlers, &lt;code&gt;http_server&lt;/code&gt; will sign the
responses from the backend and respond with an SXG. For installation
instructions, see &lt;a href=&quot;https://github.com/google/webpackager/blob/main/cmd/webpkgserver/README.md&quot; rel=&quot;noopener&quot;&gt;the
README&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;web-packager-server&quot;&gt;Web Packager Server &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#web-packager-server&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/google/webpackager/blob/main/cmd/webpkgserver/README.md&quot; rel=&quot;noopener&quot;&gt;Web Packager
Server&lt;/a&gt;,
&lt;code&gt;webpkgserver&lt;/code&gt;, is an alternative to sxg-rs http_server, written in Go. For
instructions on setting up the Web Packager server, see &lt;a href=&quot;https://web.dev/signed-exchanges-webpackager&quot;&gt;How to set up signed
exchanges using Web Packager&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;web-packager-cli&quot;&gt;Web Packager CLI &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#web-packager-cli&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/google/webpackager&quot; rel=&quot;noopener&quot;&gt;Web Packager CLI&lt;/a&gt; generates a SXG
corresponding to a given URL.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;webpackager &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;    --private&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;_key&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;private.key &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;    --cert&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;_url&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;https://example.com/certificate.cbor &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;    --url&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;https://example.com&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Once the SXG file has been generated, upload it to your server and serve it with
the &lt;code&gt;application/signed-exchange;v=b3&lt;/code&gt; MIME type. In addition, you will need to
serve the SXG certificate as &lt;code&gt;application/cert-chain+cbor&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;sxg-libraries&quot;&gt;SXG libraries &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#sxg-libraries&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;These libraries could be used to build your own SXG generator:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/google/sxg-rs/tree/main/sxg_rs&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;sxg_rs&lt;/code&gt;&lt;/a&gt; is a Rust library for
generating SXGs. It is the most featureful SXG library and is used as the
basis for the &lt;code&gt;cloudflare_worker&lt;/code&gt; and &lt;code&gt;fastly_compute&lt;/code&gt; tools.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/google/libsxg&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;libsxg&lt;/code&gt;&lt;/a&gt; is a minimal C library for
generating SXGs. It is used as the basis for the NGINX SXG module and the
Envoy SXG Filter.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/WICG/webpackage/tree/main/go/signedexchange&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;go/signed-exchange&lt;/code&gt;&lt;/a&gt;
is a minimal Go library provided by the webpackage specification as a
&lt;a href=&quot;https://en.wikipedia.org/wiki/Reference_implementation&quot; rel=&quot;noopener&quot;&gt;reference
implementation&lt;/a&gt; of
generating SXGs. It is used as the basis for its reference CLI tool,
&lt;a href=&quot;https://github.com/WICG/webpackage/tree/main/go/signedexchange&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;gen-signedexchange&lt;/code&gt;&lt;/a&gt;
and the more featureful Web Packager tools.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;content-negotiation&quot;&gt;Content negotiation &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#content-negotiation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Servers should serve SXG when the Accept header indicates that the q-value for application/signed-exchange is greater than or equal to the q-value for text/html. In practice, this means that an origin server will serve SXG to crawlers, but not browsers. Many of the above tools do this by default, but for other tools, the following regular expression can be used to match the Accept header of requests that should be served as SXG:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Accept&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;/(^|,)\s\*application\/signed-exchange\s\*;\s\*v=[[:alnum:]\_-]+\s\*(,|$)/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/google/webpackager/blob/main/cmd/webpkgserver/README.md#content-negotiation&quot; rel=&quot;noopener&quot;&gt;This recommendation&lt;/a&gt; includes examples for Apache and nginx.&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; SXG can deliver superior performance when used with caching or prefetching. However, for content that is loaded directly from the origin server without the benefit of these optimizations, text/html delivers better performance than SXG. Serving content as SXG allows crawlers and other intermediaries to cache SXGs for faster delivery to users. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;update-cache-api&quot;&gt;Update cache API &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#update-cache-api&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Google SXG Cache has an API that site owners can use to remove SXGs from the cache before they&#39;ve expired due to &lt;code&gt;Cache-Control: max-age&lt;/code&gt;. See the &lt;a href=&quot;https://github.com/google/webpackager/blob/main/docs/update_cache_api.md&quot; rel=&quot;noopener&quot;&gt;update cache API reference&lt;/a&gt; for details.&lt;/p&gt;
&lt;h3 id=&quot;linking-to-sxg&quot;&gt;Linking to SXG &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#linking-to-sxg&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Any site can cache, serve, and prefetch SXGs of the pages that it links to, where available, using the &lt;link /&gt; and &lt;a&gt; tags:&lt;/a&gt;&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://example.com/article.html.sxg&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;prefetch&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;document&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/article.html.sxg&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://web.dev/how-to-distribute-signed-http-exchanges/&quot;&gt;This article&lt;/a&gt; illustrates how to use nginx to distribute SXGs.&lt;/p&gt;
&lt;h2 id=&quot;unique-advantages&quot;&gt;Unique Advantages &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#unique-advantages&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;SXG is one of many possible technologies to enable cross-origin prefetching. When deciding which technology to use, you may need to trade off between optimizing different aspects. The following sections illustrate a few of the unique values that SXG provides in the space of possible solutions. These factors may change over time as the space of available solutions evolves.&lt;/p&gt;
&lt;h3 id=&quot;fewer-requests-to-serve&quot;&gt;Fewer requests to serve &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#fewer-requests-to-serve&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;With cross-site prefetching, your server may need to serve additional requests. This corresponds to cases where a page was prefetched, but either the user didn&#39;t visit the page, or the prefetched bytes couldn&#39;t be shown to the user. For SXG, these additional unused requests can be significantly reduced:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SXGs are cached and may be sent to users until they expire. Thus, many prefetches can be handled solely by the cache server.&lt;/li&gt;
&lt;li&gt;SXGs can be shown to users both with and without cookies on your site. Thus, there are fewer times when the page will need to be fetched again after navigation.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;page-speed-improvement&quot;&gt;Page speed improvement &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#page-speed-improvement&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You may see additional page speed improvement due to the prefetch surfaces and capabilities it currently supports:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SXGs can be shown to users with cookies for your site.&lt;/li&gt;
&lt;li&gt;SXG also prefetches subresources for your pages, such as JavaScript, CSS, fonts, and images, when specified using a &lt;code&gt;Link&lt;/code&gt; header.&lt;/li&gt;
&lt;li&gt;In the near future, SXG prefetching from Google Search will be available on more search result types.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Signed Exchanges are a delivery mechanism that make it possible to verify the
origin and validity of a resource independently of how the resource was
delivered. As a result, SXGs can be distributed by third-parties while
maintaining full publisher attribution.&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further reading &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges/#further-reading&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html&quot; rel=&quot;noopener&quot;&gt;Draft spec for Signed HTTP Exchanges&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/WICG/webpackage/tree/main/explainers&quot; rel=&quot;noopener&quot;&gt;Web Packaging explainers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developers.google.com/search/docs/advanced/experience/signed-exchange&quot; rel=&quot;noopener&quot;&gt;Get started with signed exchanges on Google Search&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/signed-exchanges-webpackager&quot;&gt;How to set up Signed Exchanges using Web Packager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://signed-exchange-testing.dev/&quot; rel=&quot;noopener&quot;&gt;Demo of Signed Exchanges&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author><author>
      <name>Devin Mullins</name>
    </author>
  </entry>
  
  <entry>
    <title>Content delivery networks (CDNs)</title>
    <link href="https://web.dev/content-delivery-networks/"/>
    <updated>2020-09-22T00:00:00Z</updated>
    <id>https://web.dev/content-delivery-networks/</id>
    <content type="html" mode="escaped">&lt;p&gt;Content delivery networks (CDNs) improve site performance by using a distributed network of servers to deliver resources to users. Because CDNs reduce server load, they reduce server costs and are well-suited to handling traffic spikes. This article discusses how CDNs work and provides platform-agnostic guidance on choosing, configuring, and optimizing a CDN setup.&lt;/p&gt;
&lt;h2 id=&quot;overview&quot;&gt;Overview &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#overview&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A content delivery network consists of a network of servers that are optimized for quickly delivering content to users. Although CDNs are arguably best known for serving cached content, CDNs can also improve the delivery of uncacheable content. Generally speaking, the more of your site delivered by your CDN, the better.&lt;/p&gt;
&lt;p&gt;At a high-level, the performance benefits of CDNs stem from a handful of principles: CDN servers are located closer to users than &lt;a href=&quot;https://en.wikipedia.org/wiki/Upstream_server&quot; rel=&quot;noopener&quot;&gt;origin servers&lt;/a&gt; and therefore have a shorter &lt;a href=&quot;https://en.wikipedia.org/wiki/Round-trip_delay&quot; rel=&quot;noopener&quot;&gt;round-trip time (RTT)&lt;/a&gt; latency; networking optimizations allow CDNs to deliver content more quickly than if the content was loaded &amp;quot;directly&amp;quot; from the origin server; lastly, CDN caches eliminate the need for a request to travel to the origin server.&lt;/p&gt;
&lt;aside class=&quot;aside flow color-secondary-box-text bg-secondary-box-bg&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Highlighter pen&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M10.22 9.49l-5.91 6c-.77.8-.7 2.05.08 2.85L.77 22h5.68l.74-.75c.78.81 1.95.86 2.73.05l5.96-6.05-5.66-5.76zm12.46-4l-2.82-2.87c-.78-.8-2.07-.84-2.84-.04l-5.75 5.85 5.66 5.75 5.69-5.78c.77-.81.83-2.11.06-2.91z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Key Term&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Origin server&lt;/strong&gt; refers to the server that a CDN retrieves content from. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;resource-delivery&quot;&gt;Resource delivery &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#resource-delivery&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Although it may seem non-intuitive, using a CDN to deliver resources (even uncacheable ones) will typically be faster than having the user load the resource &amp;quot;directly&amp;quot; from your servers.&lt;/p&gt;
&lt;p&gt;When a CDN is used to deliver resources from the origin, a new connection is established between the client and a nearby CDN server. The remainder of the journey (in other words, the data transfer between the CDN server and origin) occurs over the CDN&#39;s network - which often includes existing, persistent connections with the origin. The benefits of this are twofold: terminating the new connection as close to the user as possible eliminates unnecessary connection setup costs (establishing a new connection is expensive and requires multiple roundtrips); using a pre-warmed connection allows data to be immediately transferred at the maximum possible throughput.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Comparison of connection setup with and without a CDN&quot; decoding=&quot;async&quot; height=&quot;512&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/M9kzM7J7FenUyO7E9MF0.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/M9kzM7J7FenUyO7E9MF0.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/M9kzM7J7FenUyO7E9MF0.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/M9kzM7J7FenUyO7E9MF0.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/M9kzM7J7FenUyO7E9MF0.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/M9kzM7J7FenUyO7E9MF0.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/M9kzM7J7FenUyO7E9MF0.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/M9kzM7J7FenUyO7E9MF0.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/M9kzM7J7FenUyO7E9MF0.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/M9kzM7J7FenUyO7E9MF0.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/M9kzM7J7FenUyO7E9MF0.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/M9kzM7J7FenUyO7E9MF0.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/M9kzM7J7FenUyO7E9MF0.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/M9kzM7J7FenUyO7E9MF0.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/M9kzM7J7FenUyO7E9MF0.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/M9kzM7J7FenUyO7E9MF0.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/M9kzM7J7FenUyO7E9MF0.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/M9kzM7J7FenUyO7E9MF0.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Some CDNs improve upon this even further by routing traffic to the origin through multiple CDN servers spread across the Internet. Connections between CDN servers occur over reliable and highly optimized routes, rather than routes determined by the &lt;a href=&quot;https://en.wikipedia.org/wiki/Border_Gateway_Protocol&quot; rel=&quot;noopener&quot;&gt;Border Gateway Protocol (BGP)&lt;/a&gt;. Although BGP is the internet&#39;s de facto routing protocol, its routing decisions are not always performance-oriented. Therefore, BGP-determined routes are likely to be less performant than the finely-tuned routes between CDN servers.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Comparison of connection setup with and without a CDN&quot; decoding=&quot;async&quot; height=&quot;449&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/ZLMPFySQgBkpWvgujuJP.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/ZLMPFySQgBkpWvgujuJP.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/ZLMPFySQgBkpWvgujuJP.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/ZLMPFySQgBkpWvgujuJP.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/ZLMPFySQgBkpWvgujuJP.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/ZLMPFySQgBkpWvgujuJP.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/ZLMPFySQgBkpWvgujuJP.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/ZLMPFySQgBkpWvgujuJP.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/ZLMPFySQgBkpWvgujuJP.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/ZLMPFySQgBkpWvgujuJP.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/ZLMPFySQgBkpWvgujuJP.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/ZLMPFySQgBkpWvgujuJP.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/ZLMPFySQgBkpWvgujuJP.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/ZLMPFySQgBkpWvgujuJP.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/ZLMPFySQgBkpWvgujuJP.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/ZLMPFySQgBkpWvgujuJP.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/ZLMPFySQgBkpWvgujuJP.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/ZLMPFySQgBkpWvgujuJP.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;caching&quot;&gt;Caching &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#caching&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Caching resources on a CDN&#39;s servers eliminates the need for a request to travel all the way to the origin in order to be served. As a result, the resource is delivered more quickly; this also reduces the load on the origin server.&lt;/p&gt;
&lt;h4 id=&quot;adding-resources-to-the-cache&quot;&gt;Adding resources to the cache &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#adding-resources-to-the-cache&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The most commonly used method of populating CDN caches is to have the CDN &amp;quot;pull&amp;quot; resources as they are needed - this is known as &amp;quot;origin pull&amp;quot;. The first time that a particular resource is requested from the cache the CDN will request it from the origin server and cache the response. In this manner, the contents of the cache are built-up over time as additional uncached resources are requested.&lt;/p&gt;
&lt;h4 id=&quot;removing-resources-from-the-cache&quot;&gt;Removing resources from the cache &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#removing-resources-from-the-cache&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;CDNs use cache eviction to periodically remove not-so-useful resources from the cache. In addition, site owners can use purging to explicitly remove resources.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cache eviction&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Caches have a finite storage capacity. When a cache nears its capacity, it makes room for new resources by removing resources that haven&#39;t been accessed recently, or which take up a lot of space. This process is known as cache eviction. A resource being evicted from one cache does not necessarily mean that it has been evicted from all caches in a CDN network.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Purging&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Purging (also known as &amp;quot;cache invalidation&amp;quot;) is a mechanism for removing a resource from a CDN&#39;s caches without having to wait for it to expire or be evicted. It is typically executed via an API. Purging is critical in situations where content needs to be retracted (for example, correcting typos, pricing errors, or incorrect news articles). On top of that, it can also play a crucial role in a site&#39;s caching strategy.&lt;/p&gt;
&lt;p&gt;If a CDN supports near instant purging, purging can be used as a mechanism for managing the caching of dynamic content: cache dynamic content using a long TTL, then purge the resource whenever it is updated. In this way, it is possible to maximize the caching duration of a dynamic resource, despite not knowing in advance when the resource will change. This technique is sometimes referred to as &amp;quot;hold-till-told caching&amp;quot;.&lt;/p&gt;
&lt;p&gt;When purging is used at scale it is typically used in conjunction with a concept known as &amp;quot;cache tags&amp;quot; or &amp;quot;surrogate cache keys&amp;quot;. This mechanism allows site owners to associate one or more additional identifiers (sometimes referred to as &amp;quot;tags&amp;quot;) with a cached resource. These tags can then be used to carry out highly granular purging. For example, you might add a &amp;quot;footer&amp;quot; tag to all resources (for example, &lt;code&gt;/about&lt;/code&gt;, &lt;code&gt;/blog&lt;/code&gt;) that contain your site footer. When the footer is updated, instruct your CDN to purge all resources associated with the &amp;quot;footer&amp;quot; tag.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;cacheable-resources&quot;&gt;Cacheable resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#cacheable-resources&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;If and how a resource should be cached depends on whether it is public or private; static or dynamic.&lt;/p&gt;
&lt;h5 id=&quot;private-and-public-resources&quot;&gt;Private and public resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#private-and-public-resources&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Private Resources&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Private resources contain data intended for a single user and therefore should not be cached by a CDN. Private resources are indicated by the &lt;code&gt;Cache-Control: private&lt;/code&gt; header.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Public Resources&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Public resources do not contain user-specific information and therefore are cacheable by a CDN. A resource may be considered cacheable by a CDN if it does not have a &lt;code&gt;Cache-Control: no-store&lt;/code&gt; or &lt;code&gt;Cache-Control: private&lt;/code&gt; header. The length of time that a public resource can be cached depends on how frequently the asset changes.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;dynamic-and-static-content&quot;&gt;Dynamic and static content &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#dynamic-and-static-content&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Dynamic content&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Dynamic content is content that changes frequently. An API response and a store homepage are examples of this content type. However, the fact that this content changes frequently doesn&#39;t necessarily preclude it from being cached. During periods of heavy traffic, caching these responses for very short periods of time (for example, 5 seconds) can significantly reduce the load on the origin server, while having minimal impact on data freshness.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Static content&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Static content changes infrequently, if ever. Images, videos, and versioned libraries are typically examples of this content type. Because static content does not change, it should be cached with a long Time to Live (TTL) - for example, 6 months or 1 year.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;choosing-a-cdn&quot;&gt;Choosing a CDN &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#choosing-a-cdn&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Performance is typically a top consideration when choosing a CDN. However, the other features that a CDN offers (for example, security and analytics features), as well as a CDN&#39;s pricing, support, and onboarding are all important to consider when choosing a CDN.&lt;/p&gt;
&lt;h3 id=&quot;performance&quot;&gt;Performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#performance&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;At a high-level, a CDN&#39;s performance strategy can be thought of in terms of the tradeoff between minimizing latency and maximizing cache hit ratio. CDNs with many points of presence (PoPs) can deliver lower latency but may experience lower cache hit ratios as a result of traffic being split across more caches. Conversely, CDNs with fewer PoPs may be located geographically further from users, but can achieve higher cache hit ratios.&lt;/p&gt;
&lt;p&gt;As a result of this tradeoff, some CDNs use a tiered approach to caching: PoPs located close to users (also known as &amp;quot;edge caches&amp;quot;) are supplemented with central PoPs that have higher cache hit ratios. When an edge cache can&#39;t find a resource, it will look to a central PoP for the resource. This approach trades slightly greater latency for a higher likelihood that the resource can be served from a CDN cache - though not necessarily an edge cache.&lt;/p&gt;
&lt;p&gt;The tradeoff between minimizing latency and maximizing cache hit ratio is a spectrum. No particular approach is universally better; however, depending on the nature of your site and its user base, you may find that one of these approaches delivers significantly better performance than the other.&lt;/p&gt;
&lt;p&gt;It&#39;s also worth noting that CDN performance can vary significantly depending on geography, time of day, and even current events. Although it&#39;s always a good idea to do your own research on a CDN&#39;s performance, it can be difficult to predict the exact performance you&#39;ll get from a CDN.&lt;/p&gt;
&lt;h3 id=&quot;additional-features&quot;&gt;Additional features &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#additional-features&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;CDNs typically offer a wide variety of features in addition to their core CDN offering. Commonly offered features include: load balancing, image optimization, video streaming, edge computing, and security products.&lt;/p&gt;
&lt;h2 id=&quot;how-to-setup-and-configure-a-cdn&quot;&gt;How to setup and configure a CDN &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#how-to-setup-and-configure-a-cdn&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Ideally you should use a CDN to serve your entire site. At a high-level, the setup process for this consists of signing up with a CDN provider, then updating your CNAME DNS record to point at the CDN provider. For example, the CNAME record for &lt;code&gt;www.example.com&lt;/code&gt; might point to &lt;code&gt;example.my-cdn.com&lt;/code&gt;. As a result of this DNS change, traffic to your site will be routed through the CDN.&lt;/p&gt;
&lt;p&gt;If using a CDN to serve all resources is not an option, you can configure a CDN to only serve a subset of resources - for example, only static resources. You can do this by creating a separate CNAME record that will only be used for resources that should be served by the CDN. For example, you might create a &lt;code&gt;static.example.com&lt;/code&gt; CNAME record that points to &lt;code&gt;example.my-cdn.com&lt;/code&gt;. You would also need to rewrite the URLs of resources being served by the CDN to point to the &lt;code&gt;static.example.com&lt;/code&gt; subdomain that you created.&lt;/p&gt;
&lt;p&gt;Although your CDN will be set up at this point, there will likely be inefficiencies in your configuration. The next two sections of this article will explain how to get the most out of your CDN by increasing cache hit ratio and enabling performance features.&lt;/p&gt;
&lt;h2 id=&quot;improving-cache-hit-ratio&quot;&gt;Improving cache hit ratio &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#improving-cache-hit-ratio&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An effective CDN setup will serve as many resources as possible from the cache. This is commonly measured by cache hit ratio (CHR). Cache hit ratio is defined as the number of cache hits divided by the number of total requests during a given time interval.&lt;/p&gt;
&lt;p&gt;A freshly initialized cache will have a CHR of 0 but this increases as the cache is populated with resources. A CHR of 90% is a good goal for most sites. Your CDN provider should supply you with analytics and reporting regarding your CHR.&lt;/p&gt;
&lt;p&gt;When optimizing CHR, the first thing to verify is that all cacheable resources are being cached and cached for the correct length of time. This is a simple assessment that should be undertaken by all sites.&lt;/p&gt;
&lt;p&gt;The next level of CHR optimization, broadly speaking, is to fine tune your CDN settings to make sure that logically equivalent server responses aren&#39;t being cached separately. This is a common inefficiency that occurs due to the impact of factors like query params, cookies, and request headers on caching.&lt;/p&gt;
&lt;h3 id=&quot;initial-audit&quot;&gt;Initial audit &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#initial-audit&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Most CDNs will provide cache analytics. In addition, tools like &lt;a href=&quot;https://webpagetest.org/&quot; rel=&quot;noopener&quot;&gt;WebPageTest&lt;/a&gt; and &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/uses-long-cache-ttl/&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt; can also be used to quickly verify that all of a page&#39;s static resources are being cached for the correct length of time. This is accomplished by checking the HTTP Cache headers of each resource. Caching a resource using the maximum appropriate Time To Live (TTL) will avoid unnecessary origin fetches in the future and therefore increase CHR.&lt;/p&gt;
&lt;p&gt;At a minimum, one of these headers typically needs to be set in order for a resource to be cached by a CDN:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Cache-Control: max-age=&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Cache-Control: s-maxage=&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Expires&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition, although it does not impact if or how a resource is cached by a CDN, it is good practice to also set the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Cache-Control#Revalidation_and_reloading&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Cache-Control: immutable&lt;/code&gt;&lt;/a&gt; directive.&lt;code&gt;Cache-Control: immutable&lt;/code&gt; indicates that a resource &amp;quot;will not be updated during its freshness lifetime&amp;quot;. As a result, the browser will not revalidate the resource when serving it from the browser cache, thereby eliminating an unnecessary server request. Unfortunately, this directive is only &lt;a href=&quot;https://caniuse.com/#feat=mdn-http_headers_cache-control_immutable&quot; rel=&quot;noopener&quot;&gt;supported&lt;/a&gt; by Firefox and Safari - it is not supported by Chromium-based browsers. This &lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/detail?id=611416&quot; rel=&quot;noopener&quot;&gt;issue&lt;/a&gt; tracks Chromium support for &lt;code&gt;Cache-Control: immutable&lt;/code&gt;. Starring this issue can help encourage support for this feature.&lt;/p&gt;
&lt;p&gt;For a more detailed explanation of HTTP caching, refer to &lt;a href=&quot;https://web.dev/http-cache/&quot;&gt;Prevent unnecessary network requests with the HTTP Cache&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;fine-tuning&quot;&gt;Fine tuning &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#fine-tuning&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A slightly simplified explanation of how CDN caches work is that the URL of a resource is used as the key for caching and retrieving the resource from the cache. In practice, this is still overwhelmingly true, but is complicated slightly by the impact of things like request headers and query params. As a result, rewriting request URLs is an important technique for both maximizing CHR and ensuring that the correct content is served to users. A properly configured CDN instance strikes the correct balance between overly granular caching (which hurts CHR) and insufficiently granular caching (which results in incorrect responses being served to users).&lt;/p&gt;
&lt;h4 id=&quot;query-params&quot;&gt;Query params &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#query-params&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;By default, CDNs take query params into consideration when caching a resource. However, small adjustments to query param handling can have a significant impact on CHR. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Unnecessary query params&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;By default, a CDN would cache &lt;code&gt;example.com/blog&lt;/code&gt; and &lt;code&gt;example.com/blog?referral_id=2zjk&lt;/code&gt; separately even though they are likely the same underlying resource. This is fixed by adjusting a CDN&#39;s configuration to ignore the &lt;code&gt;referral\_id&lt;/code&gt; query param.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Query param order&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A CDN will cache &lt;code&gt;example.com/blog?id=123&amp;amp;query=dogs&lt;/code&gt; separately from &lt;code&gt;example.com/blog?query=dogs&amp;amp;id=123&lt;/code&gt;. For most sites, query param order does not matter, so configuring the CDN to sort the query params (thereby normalizing the URL used to cache the server response) will increase CHR.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;vary&quot;&gt;Vary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#vary&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Vary&quot; rel=&quot;noopener&quot;&gt;Vary&lt;/a&gt; response header informs caches that the server response corresponding to a particular URL can vary depending on the headers set on the request (for example, the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Accept-Language&quot; rel=&quot;noopener&quot;&gt;Accept-Language&lt;/a&gt; or &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Accept-Encoding&quot; rel=&quot;noopener&quot;&gt;Accept-Encoding&lt;/a&gt; request headers). As a result, it instructs a CDN to cache these responses separately. The Vary header is not widely supported by CDNs and may result in an otherwise cacheable resource not being served from a cache.&lt;/p&gt;
&lt;p&gt;Although the Vary header can be a useful tool, inappropriate usage hurts CHR. In addition, if you do use &lt;code&gt;Vary&lt;/code&gt;, normalizing request headers will help improve CHR. For example, without normalization the request headers &lt;code&gt;Accept-Language: en-US&lt;/code&gt; and &lt;code&gt;Accept-Language: en-US,en;q=0.9&lt;/code&gt; would result in two separate cache entries, even though their contents would likely be identical.&lt;/p&gt;
&lt;h4 id=&quot;cookies&quot;&gt;Cookies &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#cookies&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Cookies are set on requests via the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Cookie&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Cookie&lt;/code&gt;&lt;/a&gt; header; they are set on responses via the &lt;code&gt;Set-Cookie&lt;/code&gt; header. Unnecessary use of &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;&lt;/a&gt; header should be avoided given that caches will typically not cache server responses containing this header.&lt;/p&gt;
&lt;h2 id=&quot;performance-features&quot;&gt;Performance features &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#performance-features&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This section discusses performance features that are commonly offered by CDNs as part of their core product offering. Many sites forget to enable these features, thereby losing out on easy performance wins.&lt;/p&gt;
&lt;h3 id=&quot;compression&quot;&gt;Compression &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#compression&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;All text-based responses should be &lt;a href=&quot;https://web.dev/reduce-network-payloads-using-text-compression/#data-compression&quot;&gt;compressed&lt;/a&gt; with either gzip or Brotli. If you have the choice, choose Brotli over gzip. Brotli is a newer compression algorithm, and compared to gzip, it can achieve higher compression ratios.&lt;/p&gt;
&lt;p&gt;There are two types of CDN support for Brotli compression: &amp;quot;Brotli from origin&amp;quot; and &amp;quot;automatic Brotli compression&amp;quot;.&lt;/p&gt;
&lt;h4 id=&quot;brotli-from-origin&quot;&gt;Brotli from origin &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#brotli-from-origin&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Brotli from origin is when a CDN serves resources that were Brotli-compressed by the origin. Although this may seem like a feature that all CDNs should be able to support out of the box, it requires that a CDN be able to cache multiple versions (in other words, gzip-compressed and Brotli-compressed versions) of the resource corresponding to a given URL.&lt;/p&gt;
&lt;h4 id=&quot;automatic-brotli-compression&quot;&gt;Automatic Brotli compression &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#automatic-brotli-compression&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Automatic Brotli compression is when resources are Brotli compressed by the CDN. CDNs can compress both cacheable and non-cacheable resources.&lt;/p&gt;
&lt;p&gt;The first time that a resource is requested it is served using &amp;quot;good enough&amp;quot; compression - for example, Brotli-5. This type of compression is applicable to both cacheable and non-cacheable resources.&lt;/p&gt;
&lt;p&gt;Meanwhile, if a resource is cacheable, the CDN will use offline processing to compress the resource at a more powerful but far slower compression level - for example, Brotli-11. Once this compression completes, the more compressed version will be cached and used for subsequent requests.&lt;/p&gt;
&lt;h4 id=&quot;compression-best-practices&quot;&gt;Compression best practices &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#compression-best-practices&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Sites that want to maximize performance should apply Brotli compression at both their origin server and CDN. Brotli compression at the origin minimizes the transfer size of resources that can&#39;t be served from the cache. To prevent delays in serving requests, the origin should compress dynamic resources using a fairly conservative compression level - for example, Brotli-4; static resources can be compressed using Brotli-11. If an origin does not support Brotli, gzip-6 can be used to compress dynamic resources; gzip-9 can be used to compress static resources.&lt;/p&gt;
&lt;h3 id=&quot;tls-13&quot;&gt;TLS 1.3 &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#tls-13&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;TLS 1.3 is the newest version of &lt;a href=&quot;https://en.wikipedia.org/wiki/Transport_Layer_Security&quot; rel=&quot;noopener&quot;&gt;Transport Layer Security (TLS)&lt;/a&gt;, the cryptographic protocol used by &lt;a href=&quot;https://en.wikipedia.org/wiki/HTTPS&quot; rel=&quot;noopener&quot;&gt;HTTPS&lt;/a&gt;. TLS 1.3 provides better privacy and performance compared to TLS 1.2.&lt;/p&gt;
&lt;p&gt;TLS 1.3 shortens the TLS handshake from two roundtrips to one. For connections using HTTP/1 or HTTP/2, shortening the TLS handshake to one roundtrip effectively reduces connection setup time by 33%.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Comparison of the TLS 1.2 and TLS 1.3 handshakes&quot; decoding=&quot;async&quot; height=&quot;448&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/FnCSj1W23jXaiOWCp0Bw.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/FnCSj1W23jXaiOWCp0Bw.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/FnCSj1W23jXaiOWCp0Bw.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/FnCSj1W23jXaiOWCp0Bw.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/FnCSj1W23jXaiOWCp0Bw.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/FnCSj1W23jXaiOWCp0Bw.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/FnCSj1W23jXaiOWCp0Bw.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/FnCSj1W23jXaiOWCp0Bw.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/FnCSj1W23jXaiOWCp0Bw.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/FnCSj1W23jXaiOWCp0Bw.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/FnCSj1W23jXaiOWCp0Bw.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/FnCSj1W23jXaiOWCp0Bw.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/FnCSj1W23jXaiOWCp0Bw.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/FnCSj1W23jXaiOWCp0Bw.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/FnCSj1W23jXaiOWCp0Bw.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/FnCSj1W23jXaiOWCp0Bw.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/FnCSj1W23jXaiOWCp0Bw.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/FnCSj1W23jXaiOWCp0Bw.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;http2-and-http3&quot;&gt;HTTP/2 and HTTP/3 &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#http2-and-http3&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;HTTP/2 and HTTP/3 both provide performance benefits over HTTP/1. Of the two, HTTP/3 offers greater &lt;em&gt;potential&lt;/em&gt; performance benefits. HTTP/3 isn&#39;t fully standardized yet, but it will be widely &lt;a href=&quot;https://caniuse.com/#feat=http3&quot; rel=&quot;noopener&quot;&gt;supported&lt;/a&gt; once this occurs.&lt;/p&gt;
&lt;h4 id=&quot;http2&quot;&gt;HTTP/2 &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#http2&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;If your CDN hasn&#39;t already enabled &lt;a href=&quot;https://almanac.httparchive.org/en/2019/http2&quot; rel=&quot;noopener&quot;&gt;HTTP/2&lt;/a&gt; by default, you should consider turning it on. HTTP/2 provides multiple &lt;a href=&quot;https://hpbn.co/http2&quot; rel=&quot;noopener&quot;&gt;performance benefits&lt;/a&gt; over HTTP/1 and is &lt;a href=&quot;https://caniuse.com/#feat=http2&quot; rel=&quot;noopener&quot;&gt;supported&lt;/a&gt; by all major browsers. Performance features of HTTP/2 include: &lt;a href=&quot;https://hpbn.co/http2/#request-and-response-multiplexing&quot; rel=&quot;noopener&quot;&gt;multiplexing&lt;/a&gt;, &lt;a href=&quot;https://hpbn.co/http2/#stream-prioritization&quot; rel=&quot;noopener&quot;&gt;stream prioritization&lt;/a&gt;, &lt;a href=&quot;https://almanac.httparchive.org/en/2019/http2#http2-push&quot; rel=&quot;noopener&quot;&gt;server push&lt;/a&gt;, and &lt;a href=&quot;https://tools.ietf.org/html/rfc7541/&quot; rel=&quot;noopener&quot;&gt;header compression&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Multiplexing&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Multiplexing is arguably the most important feature of HTTP/2. Multiplexing enables a single TCP connection to serve multiple request-response pairs at the same time. This eliminates the overhead of unnecessary connection setups; given that the number of connections that a browser can have open at a given time is limited, this also has the implication that the browser is now able to request more of a page&#39;s resources in parallel. Multiplexing theoretically removes the need for HTTP/1 optimizations like concatenation and sprite sheets - however, in practice, these techniques will remain relevant given that larger files compress better.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stream prioritization&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Multiplexing enables multiple concurrent streams; &lt;a href=&quot;https://httpwg.org/specs/rfc7540.html#StreamPriority&quot; rel=&quot;noopener&quot;&gt;stream prioritization&lt;/a&gt; provides an interface for communicating relative priority of each of these streams. This helps the server to send the most important resources first - even if they weren&#39;t requested first.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Stream prioritization is expressed by the browser via a dependency tree and is merely a statement of &lt;em&gt;preference&lt;/em&gt;: in other words, the server is not obligated to meet (or even consider) the priorities supplied by the browser. Stream prioritization becomes more effective when more of a site is served through a CDN.&lt;/p&gt;
&lt;p&gt;CDN implementations of HTTP/2 resource prioritization vary wildly. To identify whether your CDN fully and properly supports HTTP/2 resource prioritization, check out &lt;a href=&quot;https://ishttp2fastyet.com/&quot; rel=&quot;noopener&quot;&gt;Is HTTP/2 Fast Yet?&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Although switching your CDN instance to HTTP/2 is largely a matter of flipping a switch, it&#39;s important to thoroughly test this change before enabling it in production. HTTP/1 and HTTP/2 use the same conventions for request and response headers - but HTTP/2 is far less forgiving when these conventions aren&#39;t adhered to. As a result, non-spec practices like including non-ASCII or uppercase characters in headers may begin causing errors once HTTP/2 is enabled. If this occurs, a browser&#39;s attempts to download the resource will fail. The failed download attempt will be visible in the &amp;quot;Network&amp;quot; tab of DevTools. In addition, the error message &amp;quot;ERR_HTTP2_PROTOCOL_ERROR&amp;quot; will be displayed in the console.&lt;/p&gt;
&lt;h4 id=&quot;http3&quot;&gt;HTTP/3 &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#http3&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/HTTP/3&quot; rel=&quot;noopener&quot;&gt;HTTP/3&lt;/a&gt; is the successor to &lt;a href=&quot;https://en.wikipedia.org/wiki/HTTP/2&quot; rel=&quot;noopener&quot;&gt;HTTP/2&lt;/a&gt;. As of September 2020, all major browsers have experimental &lt;a href=&quot;https://caniuse.com/#feat=http3&quot; rel=&quot;noopener&quot;&gt;support&lt;/a&gt; for HTTP/3 and some CDNs support it. Performance is the primary benefit of HTTP/3 over HTTP/2. Specifically, HTTP/3 eliminates head-of-line blocking at the connection level and reduces connection setup time.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Elimination of head-of-line blocking&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;HTTP/2 introduced multiplexing, a feature that allows a single connection to be used to transmit multiple streams of data simultaneously. However, with HTTP/2, a single dropped packet blocks all streams on a connection (a phenomena known as a head-of-line blocking). With HTTP/3, a dropped packet only blocks a single stream. This improvement is largely the result of HTTP/3 using &lt;a href=&quot;https://en.wikipedia.org/wiki/User_Datagram_Protocol&quot; rel=&quot;noopener&quot;&gt;UDP&lt;/a&gt; (HTTP/3 uses UDP via &lt;a href=&quot;https://en.wikipedia.org/wiki/QUIC&quot; rel=&quot;noopener&quot;&gt;QUIC&lt;/a&gt;) rather than &lt;a href=&quot;https://en.wikipedia.org/wiki/Transmission_Control_Protocol&quot; rel=&quot;noopener&quot;&gt;TCP&lt;/a&gt;. This makes HTTP/3 particularly useful for data transfer over congested or lossy networks.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Diagram showing the differences in data transmission between HTTP/1, HTTP/2, and HTTP/3&quot; decoding=&quot;async&quot; height=&quot;449&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/B7YKfqGG4eS2toSoTDdS.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/B7YKfqGG4eS2toSoTDdS.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/B7YKfqGG4eS2toSoTDdS.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/B7YKfqGG4eS2toSoTDdS.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/B7YKfqGG4eS2toSoTDdS.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/B7YKfqGG4eS2toSoTDdS.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/B7YKfqGG4eS2toSoTDdS.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/B7YKfqGG4eS2toSoTDdS.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/B7YKfqGG4eS2toSoTDdS.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/B7YKfqGG4eS2toSoTDdS.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/B7YKfqGG4eS2toSoTDdS.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/B7YKfqGG4eS2toSoTDdS.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/B7YKfqGG4eS2toSoTDdS.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/B7YKfqGG4eS2toSoTDdS.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/B7YKfqGG4eS2toSoTDdS.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/B7YKfqGG4eS2toSoTDdS.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/B7YKfqGG4eS2toSoTDdS.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/B7YKfqGG4eS2toSoTDdS.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reduced connection setup time&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;HTTP/3 uses TLS 1.3 and therefore shares its performance benefits: establishing a new connection only requires a single round-trip and resuming an existing connection does not require any roundtrips.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Comparison of connection resumption between TLS 1.2, TLS 1.3, TLS 1.3 0-RTT, and HTTP/3&quot; decoding=&quot;async&quot; height=&quot;400&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/7ffDEjblsisTNsfkynt6.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/7ffDEjblsisTNsfkynt6.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/7ffDEjblsisTNsfkynt6.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/7ffDEjblsisTNsfkynt6.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/7ffDEjblsisTNsfkynt6.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/7ffDEjblsisTNsfkynt6.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/7ffDEjblsisTNsfkynt6.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/7ffDEjblsisTNsfkynt6.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/7ffDEjblsisTNsfkynt6.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/7ffDEjblsisTNsfkynt6.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/7ffDEjblsisTNsfkynt6.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/7ffDEjblsisTNsfkynt6.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/7ffDEjblsisTNsfkynt6.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/7ffDEjblsisTNsfkynt6.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/7ffDEjblsisTNsfkynt6.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/7ffDEjblsisTNsfkynt6.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/7ffDEjblsisTNsfkynt6.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/7ffDEjblsisTNsfkynt6.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;HTTP/3 will have the biggest impact on users on poor network connections: not only because HTTP/3 handles packet loss better than its predecessors, but also because the absolute time savings resulting from a 0-RTT or 1-RTT connection setup will be greater on networks with high latency.&lt;/p&gt;
&lt;h3 id=&quot;image-optimization&quot;&gt;Image optimization &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#image-optimization&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;CDN image optimization services typically focus on image optimizations that can be applied automatically in order to reduce image transfer size. For example: stripping &lt;a href=&quot;https://en.wikipedia.org/wiki/Exif&quot; rel=&quot;noopener&quot;&gt;EXIF&lt;/a&gt; data, applying lossless compression, and converting images to newer file formats (for example, WebP). Images make up ~50% of the transfer bytes on the median web page, so optimizing images can significantly reduce page size.&lt;/p&gt;
&lt;h3 id=&quot;minification&quot;&gt;Minification &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#minification&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/reduce-network-payloads-using-text-compression/#minification&quot;&gt;Minification&lt;/a&gt; removes unnecessary characters from JavaScript, CSS, and HTML. It&#39;s preferable to do minification at the origin server, rather than the CDN. Site owners have more context about the code to be minified and therefore can often use more aggressive minification techniques than those employed by CDNs. However, if minifying code at the origin is not an option, minification by the CDN is a good alternative.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/content-delivery-networks/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Use a CDN:&lt;/strong&gt; CDNs deliver resources quickly, reduce load on the origin server, and are helpful for dealing with traffic spikes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cache content as aggressively as possible:&lt;/strong&gt; Both static and dynamic content can and should be cached - albeit for varying durations. Periodically audit your site to make sure that you are optimally cacheing content.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enable CDN performance features:&lt;/strong&gt; Features like Brotli, TLS 1.3, HTTP/2, and HTTP/3 further improve performance.&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Performance monitoring with Lighthouse CI</title>
    <link href="https://web.dev/lighthouse-ci/"/>
    <updated>2020-07-27T00:00:00Z</updated>
    <id>https://web.dev/lighthouse-ci/</id>
    <content type="html" mode="escaped">&lt;p&gt;&lt;a href=&quot;https://github.com/GoogleChrome/lighthouse-ci&quot; rel=&quot;noopener&quot;&gt;Lighthouse CI&lt;/a&gt; is a suite of
tools for using Lighthouse during continuous integration. Lighthouse CI can be
incorporated into developer workflows in many different ways. This guide covers
the following topics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using the Lighthouse CI CLI.&lt;/li&gt;
&lt;li&gt;Configuring your CI provider to run Lighthouse CI.&lt;/li&gt;
&lt;li&gt;Setting up a &lt;a href=&quot;https://github.com/features/actions&quot; rel=&quot;noopener&quot;&gt;GitHub Action&lt;/a&gt; and
&lt;a href=&quot;https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-status-checks&quot; rel=&quot;noopener&quot;&gt;status
check&lt;/a&gt;
for Lighthouse CI. This will automatically display Lighthouse results on
GitHub pull requests.&lt;/li&gt;
&lt;li&gt;Building a performance dashboard and data store for Lighthouse reports.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;overview&quot;&gt;Overview &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lighthouse-ci/#overview&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Lighthouse CI is a suite of free tools that facilitate using Lighthouse for
performance monitoring. A single Lighthouse report provides a snapshot of a web
page&#39;s performance at the time that it is run; Lighthouse CI shows how these
findings have changed over time. This can be used to identify the impact of
particular code changes or ensure that performance thresholds are met during
continuous integration processes. Although performance monitoring is the most
common use case for Lighthouse CI, it can be used to monitor other aspects of
the Lighthouse report - for example, SEO or accessibility.&lt;/p&gt;
&lt;p&gt;The core functionality of Lighthouse CI is provided by the Lighthouse CI command
line interface. (Note: This is a separate tool than the &lt;a href=&quot;https://github.com/GoogleChrome/lighthouse#using-the-node-cli&quot; rel=&quot;noopener&quot;&gt;Lighthouse
CLI&lt;/a&gt;.) The
Lighthouse CI CLI provides a set of
&lt;a href=&quot;https://github.com/GoogleChrome/lighthouse-ci/blob/master/docs/configuration.md#commands&quot; rel=&quot;noopener&quot;&gt;commands&lt;/a&gt;
for using Lighthouse CI. For example, the &lt;code&gt;autorun&lt;/code&gt; command executes multiple
Lighthouse runs, identifies the median Lighthouse report, and uploads the report
for storage. This behavior can be heavily customized by passing additional flags
or customizing Lighthouse CI&#39;s configuration file, &lt;code&gt;lighthouserc.js&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Although the core functionality of Lighthouse CI is primarily encapsulated in
the Lighthouse CI CLI, Lighthouse CI is typically used through one of the
following approaches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Running Lighthouse CI as part of continuous integration&lt;/li&gt;
&lt;li&gt;Using a Lighthouse CI GitHub Action that runs and comments on every pull
request&lt;/li&gt;
&lt;li&gt;Tracking performance over time via the dashboard provided by Lighthouse
Server.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of these approaches are built upon the Lighthouse CI CLI.&lt;/p&gt;
&lt;p&gt;Alternatives to Lighthouse CI include third-party performance monitoring
services or writing your own script to collect performance data during the CI
process. You should consider using a third-party service if you&#39;d prefer to let
someone else handle the management of your performance monitoring server and
test devices, or, if you want notification capabilities (such as email or Slack
integration) without having to build these features yourself.&lt;/p&gt;
&lt;h2 id=&quot;cli&quot;&gt;Use Lighthouse CI locally &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lighthouse-ci/#cli&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This section explains how to run and install the Lighthouse CI CLI locally and
how to configure &lt;code&gt;lighthouserc.js&lt;/code&gt;. Running the Lighthouse CI CLI locally is the
easiest way to make sure that your &lt;code&gt;lighthouserc.js&lt;/code&gt; is configured correctly.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install the Lighthouse CI CLI.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; -g @lhci/cli&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Lighthouse CI is configured by placing a &lt;code&gt;lighthouserc.js&lt;/code&gt; file in the root of
your project&#39;s repo. This file is mandatory and will contain Lighthouse CI
related configuration information. Although Lighthouse CI can be &lt;a href=&quot;https://github.com/GoogleChrome/lighthouse-ci/blob/master/docs/configuration.md#build-context&quot; rel=&quot;noopener&quot;&gt;configured to
be used without a git
repo&lt;/a&gt;,
the instructions in this article assume that your project repo is configured to
use git.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the root of your repository, create a &lt;code&gt;lighthouserc.js&lt;/code&gt; &lt;a href=&quot;https://github.com/GoogleChrome/lighthouse-ci/blob/v0.4.1/docs/configuration.md#configuration-file&quot; rel=&quot;noopener&quot;&gt;configuration
file&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; lighthouserc.js&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to &lt;code&gt;lighthouserc.js&lt;/code&gt;. This code is an empty
Lighthouse CI configuration. You will be adding to this configuration in
later steps.&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 literal-property property&quot;&gt;ci&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;collect&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 comment&quot;&gt;/* Add configuration here */&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;upload&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 comment&quot;&gt;/* Add configuration here */&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Every time that Lighthouse CI runs, it starts a server to serve your site.
This server is what enables Lighthouse to load your site even when no other
servers are running. When Lighthouse CI finishes running, it will
automatically shutdown the server. To ensure that serving works correctly,
you should configure either the
&lt;a href=&quot;https://github.com/GoogleChrome/lighthouse-ci/blob/master/docs/configuration.md#detecting-collectstaticdistdir&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;staticDistDir&lt;/code&gt;&lt;/a&gt;
or
&lt;a href=&quot;https://github.com/GoogleChrome/lighthouse-ci/blob/v0.4.1/docs/configuration.md#startservercommand&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;startServerCommand&lt;/code&gt;&lt;/a&gt;
properties.&lt;/p&gt;
&lt;p&gt;If your site is static, add the &lt;code&gt;staticDistDir&lt;/code&gt; property to the &lt;code&gt;ci.collect&lt;/code&gt;
object to indicate where your static files are located. Lighthouse CI will
use its own server to serve these files while testing your site. If your
site is not static, add the &lt;code&gt;startServerCommand&lt;/code&gt; property to the
&lt;code&gt;ci.collect&lt;/code&gt; object to indicate the command that starts your server.
Lighthouse CI will start a new server process during testing and shut it
down after.&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;// Static site example&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token literal-property property&quot;&gt;collect&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;staticDistDir&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;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;// Dynamic site example&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token literal-property property&quot;&gt;collect&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;startServerCommand&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;npm run start&#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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the
&lt;a href=&quot;https://github.com/GoogleChrome/lighthouse-ci/blob/master/docs/configuration.md#url&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;url&lt;/code&gt;&lt;/a&gt;
property to the &lt;code&gt;ci.collect&lt;/code&gt; object to indicate URL(s) that Lighthouse CI
should run Lighthouse against. The value of the &lt;code&gt;url&lt;/code&gt; property should be
provided as an array of URLs; this array can contain one or more URLs. By
default, Lighthouse CI will run Lighthouse three times against each URL.&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 literal-property property&quot;&gt;collect&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 comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;http://localhost:8080&#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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Note: These URLs should be serveable by the server you configured in the
previous step. Thus, if you&#39;re running Lighthouse CI locally, these URLs should
probably include &lt;code&gt;localhost&lt;/code&gt; rather than your production host.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the
&lt;a href=&quot;https://github.com/GoogleChrome/lighthouse-ci/blob/master/docs/configuration.md#target&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;target&lt;/code&gt;&lt;/a&gt;
property to the &lt;code&gt;ci.upload&lt;/code&gt; object and set the value to
&lt;code&gt;&#39;temporary-public-storage&#39;&lt;/code&gt;. The Lighthouse report(s) collected by
Lighthouse CI will be uploaded to temporary public storage. The report will
remain there for seven days and then be automatically deleted. This setup
guide uses the &amp;quot;temporary public storage&amp;quot; upload option because it is quick
to setup. For information on other ways of storing Lighthouse reports, refer
to the
&lt;a href=&quot;https://github.com/GoogleChrome/lighthouse-ci/blob/master/docs/configuration.md#target&quot; rel=&quot;noopener&quot;&gt;documentation&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 literal-property property&quot;&gt;upload&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;target&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;temporary-public-storage&#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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The storage location of the report will be similar to this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;https://storage.googleapis.com/lighthouse-infrastructure.appspot.com/reports/1580152437799-46441.report.html&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;(This URL won&#39;t work because the report has already been deleted.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the Lighthouse CI CLI from the terminal using the &lt;code&gt;autorun&lt;/code&gt; command.
This will run Lighthouse three times and upload the median Lighthouse
report.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;lhci autorun&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If you&#39;ve correctly configured Lighthouse CI, running this command should
produce output similar to this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;✅  .lighthouseci/ directory writable&lt;br /&gt;✅  Configuration &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt; found&lt;br /&gt;✅  Chrome installation found&lt;br /&gt;⚠️   GitHub token not &lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt;&lt;br /&gt;Healthcheck passed&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Started a web server on port &lt;span class=&quot;token number&quot;&gt;65324&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;br /&gt;Running Lighthouse &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; time&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; on http://localhost:65324/index.html&lt;br /&gt;Run &lt;span class=&quot;token comment&quot;&gt;#1...done.&lt;/span&gt;&lt;br /&gt;Run &lt;span class=&quot;token comment&quot;&gt;#2...done.&lt;/span&gt;&lt;br /&gt;Run &lt;span class=&quot;token comment&quot;&gt;#3...done.&lt;/span&gt;&lt;br /&gt;Done running Lighthouse&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Uploading median LHR of http://localhost:65324/index.html&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.success&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;Open the report at https://storage.googleapis.com/lighthouse-infrastructure.appspot.com/reports/1591720514021-82403.report.html&lt;br /&gt;No GitHub token set, skipping GitHub status check.&lt;br /&gt;&lt;br /&gt;Done running autorun.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You can ignore the &lt;code&gt;GitHub token not set&lt;/code&gt; message in the console output. A
GitHub token is only necessary if you want to use Lighthouse CI with a GitHub
Action. How to setup a GitHub Action is explained later in this article.&lt;/p&gt;
&lt;p&gt;Clicking on the link in the output that begins with
&lt;code&gt;https://storage.googleapis.com...&lt;/code&gt; will take you to the Lighthouse report
corresponding to the median Lighthouse run.&lt;/p&gt;
&lt;p&gt;The defaults used by &lt;code&gt;autorun&lt;/code&gt; can be overridden via the command line or
&lt;code&gt;lighthouserc.js&lt;/code&gt;. For example, the &lt;code&gt;lighthouserc.js&lt;/code&gt; configuration below
indicates that five Lighthouse runs should be collected every time &lt;code&gt;autorun&lt;/code&gt;
executes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update &lt;code&gt;lighthouserc.js&lt;/code&gt; to use the &lt;code&gt;numberOfRuns&lt;/code&gt; property:&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;collect&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;numberOfRuns&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Re-run the &lt;code&gt;autorun&lt;/code&gt; command:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;lhci autorun&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The terminal output should show that Lighthouse has been run five times rather
than the default three:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;✅  .lighthouseci/ directory writable&lt;br /&gt;✅  Configuration &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt; found&lt;br /&gt;✅  Chrome installation found&lt;br /&gt;⚠️   GitHub token not &lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt;&lt;br /&gt;Healthcheck passed&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Automatically determined ./dist as &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;staticDistDir&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;&lt;br /&gt;Set it explicitly &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; lighthouserc.json &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; incorrect.&lt;br /&gt;&lt;br /&gt;Started a web server on port &lt;span class=&quot;token number&quot;&gt;64444&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;br /&gt;Running Lighthouse &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt; time&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; on http://localhost:64444/index.html&lt;br /&gt;Run &lt;span class=&quot;token comment&quot;&gt;#1...done.&lt;/span&gt;&lt;br /&gt;Run &lt;span class=&quot;token comment&quot;&gt;#2...done.&lt;/span&gt;&lt;br /&gt;Run &lt;span class=&quot;token comment&quot;&gt;#3...done.&lt;/span&gt;&lt;br /&gt;Run &lt;span class=&quot;token comment&quot;&gt;#4...done.&lt;/span&gt;&lt;br /&gt;Run &lt;span class=&quot;token comment&quot;&gt;#5...done.&lt;/span&gt;&lt;br /&gt;Done running Lighthouse&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Uploading median LHR of http://localhost:64444/index.html&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.success&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;br /&gt;Open the report at https://storage.googleapis.com/lighthouse-infrastructure.appspot.com/reports/1591716944028-6048.report.html&lt;br /&gt;No GitHub token set, skipping GitHub status check.&lt;br /&gt;&lt;br /&gt;Done running autorun.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;To learn about other configuration options, refer to the Lighthouse CI
&lt;a href=&quot;https://github.com/GoogleChrome/lighthouse-ci/blob/master/docs/configuration.md&quot; rel=&quot;noopener&quot;&gt;configuration
documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;ci-setup&quot;&gt;Setup your CI process to run Lighthouse CI &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lighthouse-ci/#ci-setup&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Lighthouse CI can be used with your favorite CI tool. The &lt;a href=&quot;https://github.com/GoogleChrome/lighthouse-ci/blob/master/docs/getting-started.md#configure-your-ci-provider&quot; rel=&quot;noopener&quot;&gt;Configure Your CI
Provider&lt;/a&gt;
section of the Lighthouse CI documentation contains code samples showing how to
incorporate Lighthouse CI into the configuration files of common CI tools.
Specifically, these code samples show how to run Lighthouse CI to collect
performance measurements during the CI process.&lt;/p&gt;
&lt;p&gt;Using Lighthouse CI to collect performance measurements is a good place to start
with performance monitoring. However, advanced users may want to go a step
further and use Lighthouse CI to fail builds if they don&#39;t meet pre-defined
criteria such as passing particular Lighthouse audits or meeting all performance
budgets. This behavior is configured through the
&lt;a href=&quot;https://github.com/GoogleChrome/lighthouse-ci/blob/master/docs/configuration.md#assert&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;assert&lt;/code&gt;&lt;/a&gt;
property of the &lt;code&gt;lighthouserc.js&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;Lighthouse CI supports three levels of assertions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;off&lt;/code&gt;: ignore assertions&lt;/li&gt;
&lt;li&gt;&lt;code&gt;warn&lt;/code&gt;: print failures to stderr&lt;/li&gt;
&lt;li&gt;&lt;code&gt;error&lt;/code&gt;: print failures to stderr and exit Lighthouse CI with a non-zero
&lt;a href=&quot;https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html#:~:text=A%20non%2Dzero%20exit%20status,N%20as%20the%20exit%20status.&quot; rel=&quot;noopener&quot;&gt;exit
code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Below is an example of a &lt;code&gt;lighthouserc.js&lt;/code&gt; configuration that includes
assertions. It sets assertions for the scores of Lighthouse&#39;s performance and
accessibility categories. To try this out, add the assertions shown below to
your &lt;code&gt;lighthouserc.js&lt;/code&gt; file, then rerun Lighthouse CI.&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 literal-property property&quot;&gt;ci&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;collect&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 comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;assert&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;assertions&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token string-property property&quot;&gt;&#39;categories:performance&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;warn&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;minScore&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token string-property property&quot;&gt;&#39;categories:accessibility&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;error&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;minScore&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;upload&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 comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;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 console output that it generates looks like this:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of a warning message generated by Lighthouse CI&quot; decoding=&quot;async&quot; height=&quot;431&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/ti9NuzxPKZCYVIzjjddc.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/ti9NuzxPKZCYVIzjjddc.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/ti9NuzxPKZCYVIzjjddc.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/ti9NuzxPKZCYVIzjjddc.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/ti9NuzxPKZCYVIzjjddc.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/ti9NuzxPKZCYVIzjjddc.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/ti9NuzxPKZCYVIzjjddc.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/ti9NuzxPKZCYVIzjjddc.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/ti9NuzxPKZCYVIzjjddc.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/ti9NuzxPKZCYVIzjjddc.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/ti9NuzxPKZCYVIzjjddc.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/ti9NuzxPKZCYVIzjjddc.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/ti9NuzxPKZCYVIzjjddc.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/ti9NuzxPKZCYVIzjjddc.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/ti9NuzxPKZCYVIzjjddc.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/ti9NuzxPKZCYVIzjjddc.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/ti9NuzxPKZCYVIzjjddc.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/ti9NuzxPKZCYVIzjjddc.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;For more information on Lighthouse CI assertions, refer to the
&lt;a href=&quot;https://github.com/GoogleChrome/lighthouse-ci/blob/master/docs/configuration.md#assert&quot; rel=&quot;noopener&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;github-actions&quot;&gt;Set up a GitHub Action to run Lighthouse CI &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lighthouse-ci/#github-actions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; This section assumes that you&#39;re familiar with git, GitHub, and GitHub Pull Requests. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;A &lt;a href=&quot;https://github.com/features/actions&quot; rel=&quot;noopener&quot;&gt;GitHub Action&lt;/a&gt; can be used to run
Lighthouse CI. This will generate a new Lighthouse report every time that a code
change is pushed to any branch of a GitHub repository. Use this in conjunction
with a &lt;a href=&quot;https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-status-checks&quot; rel=&quot;noopener&quot;&gt;status
check&lt;/a&gt;
to display these results on each pull request.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of a GitHub status check&quot; decoding=&quot;async&quot; height=&quot;297&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In the root of your repository, create a directory named
&lt;code&gt;.github/workflows&lt;/code&gt;. The
&lt;a href=&quot;https://help.github.com/en/actions/configuring-and-managing-workflows/configuring-a-workflow#about-workflows&quot; rel=&quot;noopener&quot;&gt;workflows&lt;/a&gt;
for your project will go in this directory. A workflow is a process that
runs at a predetermined time (for example, when code is pushed) and is
composed of one or more actions.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; .github&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; .github/workflows&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In &lt;code&gt;.github/workflows&lt;/code&gt; create a file named &lt;code&gt;lighthouse-ci.yaml&lt;/code&gt;. This file
will hold the configuration for a new workflow.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; lighthouse-ci.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following text to &lt;code&gt;lighthouse-ci.yaml&lt;/code&gt;.&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;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Build project and run Lighthouse CI&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;push&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;lhci&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Lighthouse CI&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;steps&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 key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v1&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Use Node.js 10.x&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/setup&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;node@v1&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token key atrule&quot;&gt;node-version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10.x&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&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;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;&lt;br /&gt;          npm install&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; run Lighthouse CI&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;&lt;br /&gt;          npm install -g @lhci/cli@0.3.x&lt;br /&gt;          lhci autorun --upload.target=temporary-public-storage || echo &quot;LHCI failed!&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This configuration sets up a workflow consisting of a single job that will run
whenever new code is pushed to the repository. This job has four steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Check out the repository that Lighthouse CI will be run against&lt;/li&gt;
&lt;li&gt;Install and configure Node&lt;/li&gt;
&lt;li&gt;Install required npm packages&lt;/li&gt;
&lt;li&gt;Run Lighthouse CI and upload the results to temporary public storage.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Commit these changes and push them to GitHub. If you&#39;ve correctly followed
the steps above, pushing code to GitHub will trigger running the workflow
you just added.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To confirm that Lighthouse CI has triggered and to view the report it
generated, go to the &lt;strong&gt;Actions&lt;/strong&gt; tab of your project. You should see the
&lt;strong&gt;Build project and Run Lighthouse CI&lt;/strong&gt; workflow listed under your most
recent commit.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of the GitHub &amp;#x27;Settings&amp;#x27; tab&quot; decoding=&quot;async&quot; height=&quot;216&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/ougavsYk6faiNidNxIGQ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/ougavsYk6faiNidNxIGQ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/ougavsYk6faiNidNxIGQ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/ougavsYk6faiNidNxIGQ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/ougavsYk6faiNidNxIGQ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/ougavsYk6faiNidNxIGQ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/ougavsYk6faiNidNxIGQ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/ougavsYk6faiNidNxIGQ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/ougavsYk6faiNidNxIGQ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/ougavsYk6faiNidNxIGQ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/ougavsYk6faiNidNxIGQ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/ougavsYk6faiNidNxIGQ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/ougavsYk6faiNidNxIGQ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/ougavsYk6faiNidNxIGQ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/ougavsYk6faiNidNxIGQ.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/ougavsYk6faiNidNxIGQ.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/ougavsYk6faiNidNxIGQ.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/ougavsYk6faiNidNxIGQ.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;You can navigate to the Lighthouse report corresponding to a particular commit
from the &lt;strong&gt;Actions&lt;/strong&gt; tab. Click on the commit, click on the &lt;strong&gt;Lighthouse CI&lt;/strong&gt;
workflow step, then expand the results of the &lt;strong&gt;run Lighthouse CI&lt;/strong&gt; step.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of the GitHub &amp;#x27;Settings&amp;#x27; tab&quot; decoding=&quot;async&quot; height=&quot;366&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/aJF6FVHGOPpGNxKB3LjY.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/aJF6FVHGOPpGNxKB3LjY.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/aJF6FVHGOPpGNxKB3LjY.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/aJF6FVHGOPpGNxKB3LjY.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/aJF6FVHGOPpGNxKB3LjY.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/aJF6FVHGOPpGNxKB3LjY.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/aJF6FVHGOPpGNxKB3LjY.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/aJF6FVHGOPpGNxKB3LjY.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/aJF6FVHGOPpGNxKB3LjY.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/aJF6FVHGOPpGNxKB3LjY.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/aJF6FVHGOPpGNxKB3LjY.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/aJF6FVHGOPpGNxKB3LjY.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/aJF6FVHGOPpGNxKB3LjY.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/aJF6FVHGOPpGNxKB3LjY.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/aJF6FVHGOPpGNxKB3LjY.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/aJF6FVHGOPpGNxKB3LjY.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/aJF6FVHGOPpGNxKB3LjY.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/aJF6FVHGOPpGNxKB3LjY.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;You&#39;ve just set up a GitHub Action to run Lighthouse CI. This will be most
useful when used in conjunction with a GitHub &lt;a href=&quot;https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-status-checks&quot; rel=&quot;noopener&quot;&gt;status
check&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;github-status-checks&quot;&gt;Set up a GitHub status check &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lighthouse-ci/#github-status-checks&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A status check, if configured, is a message that appears on every PR and
typically includes information such as the results of a test or the success of a
build.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of the GitHub &amp;#x27;Settings&amp;#x27; tab&quot; decoding=&quot;async&quot; height=&quot;297&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/RZIfiOAPrst9Cxtxi9AX.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;The steps below explain how to set up a status check for Lighthouse CI.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Go to the &lt;a href=&quot;https://github.com/apps/lighthouse-ci&quot; rel=&quot;noopener&quot;&gt;Lighthouse CI GitHub App
page&lt;/a&gt; and click &lt;strong&gt;Configure&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;(Optional) If you&#39;re part of multiple organizations on GitHub, choose the
organization that owns the repository for which you want to use Lighthouse
CI.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select &lt;strong&gt;All repositories&lt;/strong&gt; if you want to enable Lighthouse CI in all
repositories or select &lt;strong&gt;Only select repositories&lt;/strong&gt; if you only want to use
it in specific repositories, and then select the repositories. Then click
&lt;strong&gt;Install &amp;amp; Authorize&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Copy the token that is displayed. You&#39;ll use it in the next step.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To add the token, navigate to the &lt;strong&gt;Settings&lt;/strong&gt; page of your GitHub
repository, click &lt;strong&gt;Secrets&lt;/strong&gt;, then click &lt;strong&gt;Add a new secret&lt;/strong&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of the GitHub &amp;#x27;Settings&amp;#x27; tab&quot; decoding=&quot;async&quot; height=&quot;375&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/ZYH9cOHehImZLI6vov1r.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/ZYH9cOHehImZLI6vov1r.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/ZYH9cOHehImZLI6vov1r.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/ZYH9cOHehImZLI6vov1r.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/ZYH9cOHehImZLI6vov1r.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/ZYH9cOHehImZLI6vov1r.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/ZYH9cOHehImZLI6vov1r.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/ZYH9cOHehImZLI6vov1r.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/ZYH9cOHehImZLI6vov1r.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/ZYH9cOHehImZLI6vov1r.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/ZYH9cOHehImZLI6vov1r.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/ZYH9cOHehImZLI6vov1r.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/ZYH9cOHehImZLI6vov1r.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/ZYH9cOHehImZLI6vov1r.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/ZYH9cOHehImZLI6vov1r.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/ZYH9cOHehImZLI6vov1r.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/ZYH9cOHehImZLI6vov1r.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/ZYH9cOHehImZLI6vov1r.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set the &lt;strong&gt;Name&lt;/strong&gt; field to &lt;code&gt;LHCI_GITHUB_APP_TOKEN&lt;/code&gt; and set the &lt;strong&gt;Value&lt;/strong&gt;
field to the token that you copied in the last step and then click the &lt;strong&gt;Add
secret&lt;/strong&gt; button.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go to the &lt;code&gt;lighthouse-ci.yaml&lt;/code&gt; file and add the new environment secret to the &amp;quot;run Lighthouse CI&amp;quot; command.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;         - name: run Lighthouse CI&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;           run: |&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;             npm install -g @lhci/cli@0.3.x&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;             lhci autorun --upload.target=temporary-public-storage || echo &quot;LHCI failed!&quot;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;            env:&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;              LHCI_GITHUB_APP_TOKEN: $&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;ol start=&quot;8&quot;&gt;
&lt;li&gt;The status check is ready for use. To test it, &lt;a href=&quot;https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request&quot; rel=&quot;noopener&quot;&gt;create a new pull
request&lt;/a&gt;
or push a commit to an existing pull request.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;server-setup&quot;&gt;Set up the Lighthouse CI Server &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lighthouse-ci/#server-setup&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Lighthouse CI server provides a dashboard for exploring historical
Lighthouse reporting. It can also act as a private, long-term datastore for
Lighthouse reports.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of the Lighthouse CI Server dashboard&quot; decoding=&quot;async&quot; height=&quot;581&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/4xv6LLe6G48weVNl1CO1.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/4xv6LLe6G48weVNl1CO1.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/4xv6LLe6G48weVNl1CO1.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/4xv6LLe6G48weVNl1CO1.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/4xv6LLe6G48weVNl1CO1.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/4xv6LLe6G48weVNl1CO1.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/4xv6LLe6G48weVNl1CO1.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/4xv6LLe6G48weVNl1CO1.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/4xv6LLe6G48weVNl1CO1.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/4xv6LLe6G48weVNl1CO1.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/4xv6LLe6G48weVNl1CO1.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/4xv6LLe6G48weVNl1CO1.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/4xv6LLe6G48weVNl1CO1.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/4xv6LLe6G48weVNl1CO1.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/4xv6LLe6G48weVNl1CO1.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/4xv6LLe6G48weVNl1CO1.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/4xv6LLe6G48weVNl1CO1.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/4xv6LLe6G48weVNl1CO1.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of comparing two Lighthouse reports in Lighthouse CI Server&quot; decoding=&quot;async&quot; height=&quot;556&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/vp9hVBQGZk01fUMpIQ1Z.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/vp9hVBQGZk01fUMpIQ1Z.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/vp9hVBQGZk01fUMpIQ1Z.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/vp9hVBQGZk01fUMpIQ1Z.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/vp9hVBQGZk01fUMpIQ1Z.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/vp9hVBQGZk01fUMpIQ1Z.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/vp9hVBQGZk01fUMpIQ1Z.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/vp9hVBQGZk01fUMpIQ1Z.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/vp9hVBQGZk01fUMpIQ1Z.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/vp9hVBQGZk01fUMpIQ1Z.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/vp9hVBQGZk01fUMpIQ1Z.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/vp9hVBQGZk01fUMpIQ1Z.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/vp9hVBQGZk01fUMpIQ1Z.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/vp9hVBQGZk01fUMpIQ1Z.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/vp9hVBQGZk01fUMpIQ1Z.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/vp9hVBQGZk01fUMpIQ1Z.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/vp9hVBQGZk01fUMpIQ1Z.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/vp9hVBQGZk01fUMpIQ1Z.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;ol&gt;
&lt;li&gt;Choose which commits to compare.&lt;/li&gt;
&lt;li&gt;The amount that the Lighthouse score has changed between the two commits.&lt;/li&gt;
&lt;li&gt;This section only shows metrics that have changed between the two commits.&lt;/li&gt;
&lt;li&gt;Regressions are highlighted in pink.&lt;/li&gt;
&lt;li&gt;Improvements are highlighted in blue.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Lighthouse CI Server is best-suited to users who are comfortable deploying and
managing their own infrastructure.&lt;/p&gt;
&lt;p&gt;For information on setting up the Lighthouse CI server, including recipes for
using Heroku and Docker for deployment, refer to these
&lt;a href=&quot;https://github.com/GoogleChrome/lighthouse-ci/blob/master/docs/server.md&quot; rel=&quot;noopener&quot;&gt;instructions&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;find-out-more&quot;&gt;Find out more &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/lighthouse-ci/#find-out-more&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/GoogleChrome/lighthouse-ci&quot; rel=&quot;noopener&quot;&gt;Lighthouse CI GitHub repo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Getting started with measuring Web Vitals</title>
    <link href="https://web.dev/vitals-measurement-getting-started/"/>
    <updated>2020-05-27T00:00:00Z</updated>
    <id>https://web.dev/vitals-measurement-getting-started/</id>
    <content type="html" mode="escaped">&lt;p&gt;Collecting data on your site&#39;s Web Vitals is the first step towards improving them. A well-rounded analysis will collect performance data from both real-world and lab environments. Measuring Web Vitals requires minimal code changes and can be accomplished using free tooling.&lt;/p&gt;
&lt;h2 id=&quot;measuring-web-vitals-using-rum-data&quot;&gt;Measuring Web Vitals using RUM data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-measurement-getting-started/#measuring-web-vitals-using-rum-data&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Real_user_monitoring&quot; rel=&quot;noopener&quot;&gt;Real User Monitoring&lt;/a&gt; (RUM) data, also known as field data, captures the performance experienced by a site&#39;s actual users. RUM data is what Google uses to determine whether a site meets the &lt;a href=&quot;https://web.dev/vitals/&quot;&gt;recommended Core Web Vitals thresholds.&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;getting-started&quot;&gt;Getting started &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-measurement-getting-started/#getting-started&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you don&#39;t have a RUM setup, the following tools will quickly provide you with data about the real-world performance of your site. These tools are all based on the same underlying data set (the &lt;a href=&quot;https://developer.chrome.com/docs/crux/&quot; rel=&quot;noopener&quot;&gt;Chrome User Experience Report&lt;/a&gt;), but have slightly different use cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PageSpeed Insights (PSI)&lt;/strong&gt;: &lt;a href=&quot;https://pagespeed.web.dev/&quot; rel=&quot;noopener&quot;&gt;PageSpeed Insights&lt;/a&gt; reports on the aggregate page-level and origin-level performance over the past 28 days. In addition, it provides suggestions on how to improve performance. If you&#39;re looking for a single action to take in order to get started with measuring and improving your site&#39;s Web Vitals, we recommend using PSI to audit your site. PSI is available on the &lt;a href=&quot;https://pagespeed.web.dev/&quot; rel=&quot;noopener&quot;&gt;web&lt;/a&gt; and as an &lt;a href=&quot;https://developers.google.com/speed/docs/insights/v5/get-started&quot; rel=&quot;noopener&quot;&gt;API&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Search Console&lt;/strong&gt;: &lt;a href=&quot;https://search.google.com/search-console/welcome&quot; rel=&quot;noopener&quot;&gt;Search Console&lt;/a&gt; reports performance data on a per-page basis. This makes it well-suited for identifying specific pages that need improvement. Unlike PageSpeed Insights, Search Console reporting includes historical performance data. Search Console can only be used with sites that you own and have verified ownership of.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CrUX dashboard&lt;/strong&gt;: &lt;a href=&quot;https://developers.google.com/web/updates/2018/08/chrome-ux-report-dashboard&quot; rel=&quot;noopener&quot;&gt;CrUX dashboard&lt;/a&gt; is a pre-built dashboard that surfaces CrUX data for an origin of your choosing. It is built on top of Data Studio and the setup process takes about a minute. Compared to PageSpeed Insights and Search Console, CrUX dashboard reporting includes more dimensions - for example, data can be broken down by device and connection type.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&#39;s worth noting that although the tools listed above are well-suited for &amp;quot;getting started&amp;quot; with measuring Web Vitals, they can be useful in other contexts as well. In particular, both CrUX and PSI are available as an API and can be used to &lt;a href=&quot;https://dev.to/chromiumdev/a-step-by-step-guide-to-monitoring-the-competition-with-the-chrome-ux-report-4k1o&quot; rel=&quot;noopener&quot;&gt;build dashboards&lt;/a&gt; and other reporting.&lt;/p&gt;
&lt;h3 id=&quot;collecting-rum-data&quot;&gt;Collecting RUM data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-measurement-getting-started/#collecting-rum-data&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Although CrUX-based tools are a good starting point for investigating Web Vitals performance, we strongly recommend supplementing it with your own RUM. RUM data that you collect yourself can provide more detailed and immediate feedback on your site&#39;s performance. This makes it easier to identify issues and test possible solutions.&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; CrUX-based data sources report data using a granularity of approximately one month - however, the details of this vary slightly by tool. For example, PSI and Search Console report the performance observed over the past 28 days, whereas the CrUX dataset and dashboard is broken down by calendar month. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;You can collect your own RUM data by using a dedicated RUM provider, or, by setting up your own tooling.&lt;/p&gt;
&lt;p&gt;Dedicated RUM providers specialize in collecting and reporting RUM data. To use Core Web Vitals with these services, ask your RUM provider about enabling Core Web Vitals monitoring for your site.&lt;/p&gt;
&lt;p&gt;If you don&#39;t have a RUM provider, you may be able to augment your existing analytics setup to collect and report on these metrics by using 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;. This method is explained in more detail below.&lt;/p&gt;
&lt;h3 id=&quot;the-web-vitals-javascript-library&quot;&gt;The web-vitals JavaScript library &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-measurement-getting-started/#the-web-vitals-javascript-library&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you&#39;re implementing your own RUM setup for Web Vitals, the easiest way to collect Web Vitals measurements is to 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;&lt;/a&gt; JavaScript library. &lt;code&gt;web-vitals&lt;/code&gt; is a small, modular library (~1KB) that provides a convenient API for collecting and reporting each of the &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/#in-the-field&quot;&gt;field-measurable&lt;/a&gt; Web Vitals metrics.&lt;/p&gt;
&lt;p&gt;The metrics that make up Web Vitals are not all directly exposed by the browser&#39;s built-in performance APIs - but rather built on top of them. For example, &lt;a href=&quot;https://web.dev/cls/&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt; is implemented using the &lt;a href=&quot;https://wicg.github.io/layout-instability/&quot; rel=&quot;noopener&quot;&gt;Layout Instability API&lt;/a&gt;. By using &lt;code&gt;web-vitals&lt;/code&gt;, you don&#39;t need to worry about implementing these metrics yourself; it also ensures that the data you collect matches the methodology and best practices for each metric.&lt;/p&gt;
&lt;p&gt;For more information on implementing &lt;code&gt;web-vitals&lt;/code&gt;, refer to the &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals&quot; rel=&quot;noopener&quot;&gt;documentation&lt;/a&gt; and the &lt;a href=&quot;https://web.dev/vitals-field-measurement-best-practices/&quot;&gt;Best practices for measuring Web Vitals in the field&lt;/a&gt; guide.&lt;/p&gt;
&lt;h3 id=&quot;data-aggregation&quot;&gt;Data aggregation &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-measurement-getting-started/#data-aggregation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It is essential that you report the measurements collected by &lt;code&gt;web-vitals&lt;/code&gt;. If this data is measured but not reported, you&#39;ll never see it. The &lt;code&gt;web-vitals&lt;/code&gt; documentation includes examples showing how to send the data to &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals#send-the-results-to-an-analytics-endpoint&quot; rel=&quot;noopener&quot;&gt;a generic API endpoint&lt;/a&gt;, &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals#send-the-results-to-google-analytics&quot; rel=&quot;noopener&quot;&gt;Google Analytics&lt;/a&gt;, or &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals#send-the-results-to-google-tag-manager&quot; rel=&quot;noopener&quot;&gt;Google Tag Manager&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you already have a favorite reporting tool, consider using that. If not, Google Analytics is free and can be used for this purpose.&lt;/p&gt;
&lt;p&gt;When considering which tool to use, it is helpful to think about who will need to have access to the data. Businesses typically achieve the biggest performance wins when the whole company, rather than a single department, is interested in improving performance. See &lt;a href=&quot;https://web.dev/fixing-website-speed-cross-functionally/&quot;&gt;Fixing website speed cross-functionally&lt;/a&gt; to learn how to get buy-in from different departments.&lt;/p&gt;
&lt;h3 id=&quot;data-interpretation&quot;&gt;Data interpretation &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-measurement-getting-started/#data-interpretation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When analyzing performance data, it&#39;s important to pay attention to the tails of the distribution. RUM data often reveals that performance varies widely - some users have fast experiences, others have slow experiences. However, using the median to summarize data can easily mask this behavior.&lt;/p&gt;
&lt;p&gt;With regards to Web Vitals, Google uses the percentage of &amp;quot;good&amp;quot; experiences, rather than statistics like medians or averages, to determine whether a site or page meets the recommended thresholds. Specifically, for a site or page to be considered as meeting the Core Web Vitals thresholds, 75% of page visits should meet the &amp;quot;good&amp;quot; threshold for each metric.&lt;/p&gt;
&lt;h2 id=&quot;measuring-web-vitals-using-lab-data&quot;&gt;Measuring Web Vitals using lab data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-measurement-getting-started/#measuring-web-vitals-using-lab-data&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/user-centric-performance-metrics/#in-the-lab&quot;&gt;Lab data&lt;/a&gt;, also known as synthetic data, is collected from a controlled environment, rather than actual users. Unlike RUM data, lab data can be collected from pre-production environments and therefore incorporated into developer workflows and continuous integration processes. Examples of tools that collect synthetic data are Lighthouse and WebPageTest.&lt;/p&gt;
&lt;h3 id=&quot;considerations&quot;&gt;Considerations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-measurement-getting-started/#considerations&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There will always be discrepancies between RUM data and lab data - particularly if the network conditions, device type, or location of the lab environment differs significantly from that of users. However, when it comes to collecting lab data on Web Vitals metrics in particular, there are a couple specific considerations that are important to note:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cumulative Layout Shift (CLS):&lt;/strong&gt; The &lt;a href=&quot;https://web.dev/cls/&quot;&gt;Cumulative Layout Shift&lt;/a&gt; measured in lab environments can be artificially lower than the CLS observed in RUM data. CLS is defined as the &amp;quot;sum total of all individual layout shift scores for every unexpected layout shift that occurs &lt;em&gt;during the entire lifespan of the page&lt;/em&gt;.&amp;quot; However, the lifespan of a page is typically very different depending on whether it is being loaded by a real user or a synthetic performance measurement tool. Many lab tools only load the page - they don&#39;t interact with it. As a result, they only capture layout shifts that occur during initial page load. By contrast, the CLS measured by RUM tools captures &lt;a href=&quot;https://web.dev/cls/#expected-vs-unexpected-layout-shifts&quot;&gt;unexpected layout shifts&lt;/a&gt; that occur throughout the entire lifespan of the page.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;First Input Delay (FID):&lt;/strong&gt; &lt;a href=&quot;https://web.dev/fid/&quot;&gt;First Input Delay&lt;/a&gt; can&#39;t be measured in lab environments because it requires user interactions with the page. As a result, &lt;a href=&quot;https://web.dev/tbt/&quot;&gt;Total Blocking Time&lt;/a&gt; (TBT) is the recommended lab proxy for FID. TBT measures the &amp;quot;total amount of time between First Contentful Paint and Time to Interactive during which the page is blocked from responding to user input&amp;quot;. Although FID and TBT are calculated differently, they are both reflections of a blocked main thread during the bootstrap process. When the main thread is blocked, the browser is delayed in responding to user interactions. FID measures the delay, if any, that occurs the first time a user tries to interact with a page.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;tooling&quot;&gt;Tooling &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/vitals-measurement-getting-started/#tooling&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;These tools can be used to gather lab measurements of Web Vitals:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Web Vitals Chrome Extension:&lt;/strong&gt; The Web Vitals Chrome &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals-extension&quot; rel=&quot;noopener&quot;&gt;extension&lt;/a&gt; measures and reports the Core Web Vitals (LCP, FID, and CLS) for a given page. This tool is intended to provide developers with real-time performance feedback as they make code changes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lighthouse:&lt;/strong&gt; Lighthouse reports on LCP, CLS, and TBT, and also highlights possible performance improvements. Lighthouse is available in Chrome DevTools, as a Chrome Extension, and as an npm package. Lighthouse can also be incorporated into continuous integration workflows via &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;li&gt;&lt;strong&gt;WebPageTest:&lt;/strong&gt; &lt;a href=&quot;https://webpagetest.org/&quot; rel=&quot;noopener&quot;&gt;WebPageTest&lt;/a&gt; includes Web Vitals as a part of its standard reporting. WebPageTest is useful for gathering information on Web Vitals under particular device and network conditions.&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Fix an overloaded server</title>
    <link href="https://web.dev/overloaded-server/"/>
    <updated>2020-03-31T00:00:00Z</updated>
    <id>https://web.dev/overloaded-server/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;overview&quot;&gt;Overview &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#overview&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This guide shows you how to fix an overloaded server in 4 steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/overloaded-server/#assess&quot;&gt;Assess&lt;/a&gt;: Determine the server&#39;s bottleneck.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/overloaded-server/#stabilize&quot;&gt;Stabilize&lt;/a&gt;: Implement quick fixes to mitigate impact.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/overloaded-server/#improve&quot;&gt;Improve&lt;/a&gt;: Augment and optimize server capabilities.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/overloaded-server/#monitor&quot;&gt;Monitor&lt;/a&gt;: Use automated tools to help prevent future issues.&lt;/li&gt;
&lt;/ol&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt;  If you have questions or feedback on this guide, or you want to share your own tips and tricks, please leave a comment in &lt;a href=&quot;https://github.com/GoogleChrome/web.dev/pull/2479&quot;&gt;PR #2479&lt;/a&gt;.  &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;assess&quot;&gt;Assess &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#assess&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When traffic overloads a server, one or more of the following can become a bottleneck: CPU, network, memory, or disk I/O. Identifying which of these is the bottleneck makes it possible to focus efforts on the most impactful mitigations.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CPU: CPU usage that is consistently over 80% should be investigated and fixed. Server performance often degrades once CPU usage reaches ~80-90%, and becomes more pronounced as usuage gets closer to 100%. The CPU utilization of serving a single request is negligible, but doing this at the scale encountered during traffic spikes can sometimes overwhelm a server. Offloading serving to other infrastructure, reducing expensive operations, and limiting the quantity of requests will reduce CPU utilization.&lt;/li&gt;
&lt;li&gt;Network: During periods of high traffic, the network throughput required to fulfill user requests can exceed capacity. Some sites, depending on the hosting provider, may also hit caps regarding cumulative data transfer. Reducing the size and quantity of data transferred to and from the server will remove this bottleneck.&lt;/li&gt;
&lt;li&gt;Memory: When a system doesn&#39;t have enough memory, data has to be offloaded to disk for storage. Disk is considerably slower to access than memory, and this can slow down an entire application. If memory becomes completely exhausted, it can result in &lt;a href=&quot;https://en.wikipedia.org/wiki/Out_of_memory&quot; rel=&quot;noopener&quot;&gt;Out of Memory&lt;/a&gt; (OOM) errors. Adjusting memory allocation, fixing memory leaks, and upgrading memory can remove this bottleneck.&lt;/li&gt;
&lt;li&gt;Disk I/O: The rate at which data can be read or written from disk is constrained by the disk itself. If disk I/O is a bottleneck, increasing the amount of data cached in memory can alleviate this issue (at the cost of increased memory utilization). If this doesn&#39;t work, it may be necessary to upgrade your disks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The techniques in this guide focus on addressing CPU and network bottlenecks. For most sites, CPU and network will be the most relevant bottlenecks during a traffic spike.&lt;/p&gt;
&lt;p&gt;Running &lt;a href=&quot;https://linux.die.net/man/1/top&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;top&lt;/code&gt;&lt;/a&gt; on the affected server is a good starting place for investigating bottlenecks. If available, supplement this with historical data from your hosting provider or monitoring tooling.&lt;/p&gt;
&lt;h2 id=&quot;stabilize&quot;&gt;Stabilize &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#stabilize&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An overloaded server can quickly lead to &lt;a href=&quot;https://en.wikipedia.org/wiki/Cascading_failure&quot; rel=&quot;noopener&quot;&gt;cascading failures&lt;/a&gt; elsewhere in the system. Thus, it&#39;s important to stabilize the server before attempting to make more significant changes.&lt;/p&gt;
&lt;h3 id=&quot;rate-limiting&quot;&gt;Rate Limiting &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#rate-limiting&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Rate limiting protects infrastructure by limiting the number of incoming requests. This is increasingly important as server performance degrades: as response times increase, users tend to aggressively refresh the page - increasing the server load even further.&lt;/p&gt;
&lt;h4 id=&quot;fix&quot;&gt;Fix &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#fix&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Although rejecting a request is relatively inexpensive, the best way to protect your server is to handle rate limiting somewhere upstream from it - for instance, via a load balancer, reverse proxy, or CDN.&lt;/p&gt;
&lt;p&gt;Instructions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.nginx.com/blog/rate-limiting-nginx/&quot; rel=&quot;noopener&quot;&gt;NGINX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.haproxy.com/blog/four-examples-of-haproxy-rate-limiting/&quot; rel=&quot;noopener&quot;&gt;HAProxy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/iis/configuration/system.applicationhost/sites/site/limits&quot; rel=&quot;noopener&quot;&gt;Microsoft IIS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Further reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://cloud.google.com/solutions/rate-limiting-strategies-techniques&quot; rel=&quot;noopener&quot;&gt;Rate-limiting Strategies &amp;amp; Techniques&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;http-caching&quot;&gt;HTTP Caching &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#http-caching&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Look for ways to more aggressively cache content. If a resource can be served from an HTTP cache (whether it&#39;s the browser cache or a CDN), then it doesn&#39;t need to be requested from the origin server, which reduces server load.&lt;/p&gt;
&lt;p&gt;HTTP headers like &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Cache-Control&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Cache-Control&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Expires&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Expires&lt;/code&gt;&lt;/a&gt;, and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/ETag&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;ETag&lt;/code&gt;&lt;/a&gt; indicate how a resource should be cached by an HTTP cache. Auditing and fixing these headers will improve caching.&lt;/p&gt;
&lt;p&gt;Although &lt;a href=&quot;https://developer.chrome.com/docs/workbox/service-worker-overview/&quot; rel=&quot;noopener&quot;&gt;service workers&lt;/a&gt; can also be used for caching, they utilize a separate &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Cache&quot; rel=&quot;noopener&quot;&gt;cache&lt;/a&gt; and are a supplement, rather than a replacement, for proper HTTP caching. For this reason, when handling an overloaded server, efforts should be focused on optimizing HTTP caching.&lt;/p&gt;
&lt;h4 id=&quot;diagnose&quot;&gt;Diagnose &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#diagnose&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Run &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt; and look at the &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/uses-long-cache-ttl&quot; rel=&quot;noopener&quot;&gt;Serve static assets with an efficient cache policy&lt;/a&gt; audit to view a list of resources with a short to medium &lt;a href=&quot;https://en.wikipedia.org/wiki/Time_to_live&quot; rel=&quot;noopener&quot;&gt;time to live&lt;/a&gt; (TTL). For each listed resource, consider if the TTL should be increased. As a rough guideline:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Static resources should be cached with a long TTL (1 year).&lt;/li&gt;
&lt;li&gt;Dynamic resources should be cached with a short TTL (3 hours).&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;fix-2&quot;&gt;Fix &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#fix-2&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Set the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Cache-Control&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Cache-Control&lt;/code&gt;&lt;/a&gt; header&#39;s &lt;code&gt;max-age&lt;/code&gt; directive to the appropriate number of seconds.&lt;/p&gt;
&lt;p&gt;Instructions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_headers_module.html&quot; rel=&quot;noopener&quot;&gt;NGINX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://httpd.apache.org/docs/current/mod/mod_expires.html&quot; rel=&quot;noopener&quot;&gt;Apache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/iis/configuration/system.webserver/staticcontent/clientcache&quot; rel=&quot;noopener&quot;&gt;Microsoft&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note: The &lt;code&gt;max-age&lt;/code&gt; directive is just one of many caching directives. There are many other directives and headers that will affect the caching behavior of your application. For a more in-depth explanation of caching strategy it is highly recommended that you read &lt;a href=&quot;https://web.dev/http-cache/&quot;&gt;HTTP Caching&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;graceful-degradation&quot;&gt;Graceful Degradation &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#graceful-degradation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Graceful degradation is the strategy of temporarily reducing functionality in order to shed excess load from a system. This concept can be applied in many different ways: for example, serving a static text page instead of a full-featured application, disabling search or returning fewer search results, or disabling certain expensive or non-essential features. Emphasis should be placed on removing functionalities that can be safely and easily removed with minimal business impact.&lt;/p&gt;
&lt;h2 id=&quot;improve&quot;&gt;Improve &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#improve&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;use-a-content-delivery-network-cdn&quot;&gt;Use a content delivery network (CDN) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#use-a-content-delivery-network-cdn&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Serving static assets can be offloaded from your server to a content delivery network (CDN), thereby reducing the load.&lt;/p&gt;
&lt;p&gt;The primary function of a CDN is to deliver content to users quickly by providing a large network of servers that are located close to users. However, most CDNs also offer additional performance-related features like compression, load balancing, and media optimization.&lt;/p&gt;
&lt;h4 id=&quot;set-up-a-cdn&quot;&gt;Set up a CDN &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#set-up-a-cdn&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;CDNs benefit from scale, so operating your own CDN rarely makes sense. A basic CDN configuration is fairly quick to set up (~30 minutes) and consists of updating DNS records to point at the CDN.&lt;/p&gt;
&lt;h4 id=&quot;optimize-cdn-usage&quot;&gt;Optimize CDN Usage &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#optimize-cdn-usage&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;h4 id=&quot;diagnose-2&quot;&gt;Diagnose &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#diagnose-2&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Identify resources that are not being served from a CDN (but should be) by running &lt;a href=&quot;https://webpagetest.org/&quot; rel=&quot;noopener&quot;&gt;WebPageTest&lt;/a&gt;. On the results page, click on the square above &#39;Effective use of CDN&#39; to see the list of resources that should be served from a CDN.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Arrow pointing to the &amp;#x27;Effective use of CDN&amp;#x27; button&quot; decoding=&quot;async&quot; height=&quot;109&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 300px) 300px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/awCu4XpFI9IQ1bfhIaWJ.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/awCu4XpFI9IQ1bfhIaWJ.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/awCu4XpFI9IQ1bfhIaWJ.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/awCu4XpFI9IQ1bfhIaWJ.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/awCu4XpFI9IQ1bfhIaWJ.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/awCu4XpFI9IQ1bfhIaWJ.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/awCu4XpFI9IQ1bfhIaWJ.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/awCu4XpFI9IQ1bfhIaWJ.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/awCu4XpFI9IQ1bfhIaWJ.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/awCu4XpFI9IQ1bfhIaWJ.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/awCu4XpFI9IQ1bfhIaWJ.jpg?auto=format&amp;w=600 600w&quot; width=&quot;300&quot; /&gt;
  &lt;figcaption&gt;
   WebPageTest results
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h4 id=&quot;fix-3&quot;&gt;Fix &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#fix-3&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;If a resource is not being cached by the CDN, check that the following conditions are met:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It has a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Cache-Control#Cacheability&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Cache-Control: public&lt;/code&gt;&lt;/a&gt; header.&lt;/li&gt;
&lt;li&gt;It has a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Cache-Control#Expiration&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Cache-Control: s-maxage&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Cache-Control#Expiration&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Cache-Control: max-age&lt;/code&gt;&lt;/a&gt;, or &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Expires&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Expires&lt;/code&gt;&lt;/a&gt; header.&lt;/li&gt;
&lt;li&gt;It has a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Content-Length&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Content-Length&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Content-Range&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Content-Range&lt;/code&gt;&lt;/a&gt;, or &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Transfer-Encoding&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Transfer-Encoding header&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;scale-compute-resources&quot;&gt;Scale compute resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#scale-compute-resources&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The decision to scale compute resources should be made carefully. Although it is often necessary to scale compute resources, doing so prematurely can generate unnecessary architectural complexity and financial costs.&lt;/p&gt;
&lt;h4 id=&quot;diagnose-3&quot;&gt;Diagnose &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#diagnose-3&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A high &lt;a href=&quot;https://web.dev/ttfb/&quot;&gt;Time To First Byte&lt;/a&gt; (TTFB) can be a sign that a server is nearing its capacity. You can find this information in the Lighthouse &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/time-to-first-byte&quot; rel=&quot;noopener&quot;&gt;Reduce server response times (TTFB)&lt;/a&gt; audit.&lt;/p&gt;
&lt;p&gt;To investigate further, use a monitoring tool to assess CPU usage. If current or anticipated CPU usage exceeds 80% you should consider increasing your servers.&lt;/p&gt;
&lt;h4 id=&quot;fix-4&quot;&gt;Fix &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#fix-4&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Adding a load balancer makes it possible to distribute traffic across multiple servers. A load balancer sits in front of a pool of servers and routes traffic to the appropriate server. Cloud providers offer their own load balancers (&lt;a href=&quot;https://cloud.google.com/load-balancing&quot; rel=&quot;noopener&quot;&gt;GCP&lt;/a&gt;, &lt;a href=&quot;https://aws.amazon.com/elasticloadbalancing/&quot; rel=&quot;noopener&quot;&gt;AWS&lt;/a&gt;, &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/load-balancer/load-balancer-overview&quot; rel=&quot;noopener&quot;&gt;Azure&lt;/a&gt;) or you can set up your own using &lt;a href=&quot;https://www.digitalocean.com/community/tutorials/an-introduction-to-haproxy-and-load-balancing-concepts&quot; rel=&quot;noopener&quot;&gt;HAProxy&lt;/a&gt; or &lt;a href=&quot;http://nginx.org/en/docs/http/load_balancing.html&quot; rel=&quot;noopener&quot;&gt;NGINX&lt;/a&gt;. Once a load balancer is in place, additional servers can be added.&lt;/p&gt;
&lt;p&gt;In addition to load balancing, most cloud providers offer autoscaling (&lt;a href=&quot;https://cloud.google.com/compute/docs/load-balancing-and-autoscaling&quot; rel=&quot;noopener&quot;&gt;GCP&lt;/a&gt;, &lt;a href=&quot;https://docs.aws.amazon.com/ec2/index.html&quot; rel=&quot;noopener&quot;&gt;AWS&lt;/a&gt;, &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-monitor/platform/autoscale-overview&quot; rel=&quot;noopener&quot;&gt;Azure&lt;/a&gt;). Autoscaling works in conjunction with load balancing - autoscaling automatically scales compute resources up and down given demand at a given time. That being said, autoscaling is not magical - it takes time for new instances to come online and it requires significant configuration. Because of the additional complexity that autoscaling entails, a simpler load balancer-based setup should be considered first.&lt;/p&gt;
&lt;h3 id=&quot;enable-compression&quot;&gt;Enable compression &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#enable-compression&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Text-based resources should be compressed using gzip or brotli. Gzip can reduce the transfer size of these resources by ~70%.&lt;/p&gt;
&lt;h4 id=&quot;diagnose-4&quot;&gt;Diagnose &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#diagnose-4&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Use the Lighthouse &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/uses-text-compression&quot; rel=&quot;noopener&quot;&gt;Enable text compression&lt;/a&gt; audit to identify resources that should be compressed.&lt;/p&gt;
&lt;h4 id=&quot;fix-5&quot;&gt;Fix &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#fix-5&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Enable compression by updating your server configuration. Instructions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_gzip_module.html&quot; rel=&quot;noopener&quot;&gt;NGINX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://httpd.apache.org/docs/trunk/mod/mod_deflate.html&quot; rel=&quot;noopener&quot;&gt;Apache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/iis/extensions/iis-compression/iis-compression-overview&quot; rel=&quot;noopener&quot;&gt;Microsoft&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;optimize-images-and-media&quot;&gt;Optimize images and media &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#optimize-images-and-media&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://images.guide/#introduction&quot; rel=&quot;noopener&quot;&gt;Images make up the majority of the file size of most websites&lt;/a&gt;; optimizing images can quickly and significantly reduce the size of a site.&lt;/p&gt;
&lt;h4 id=&quot;diagnose-5&quot;&gt;Diagnose &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#diagnose-5&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Lighthouse has a variety of audits that flag potential image optimizations. Alternatively, another strategy is to use DevTools to identify the largest image files - these images will likely be good candidates for optimization.&lt;/p&gt;
&lt;p&gt;Relevant Lighthouse audits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/uses-responsive-images&quot; rel=&quot;noopener&quot;&gt;Properly size images&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/offscreen-images/&quot; rel=&quot;noopener&quot;&gt;Defer offscreen images&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/uses-optimized-images/&quot; rel=&quot;noopener&quot;&gt;Efficiently encode images&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/uses-webp-images&quot; rel=&quot;noopener&quot;&gt;Serve images in next-gen formats&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/efficient-animated-content/&quot; rel=&quot;noopener&quot;&gt;Use video formats for animated content&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Chrome DevTools workflow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/devtools/network/#load&quot; rel=&quot;noopener&quot;&gt;Log network activity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Img&lt;/strong&gt; to &lt;a href=&quot;https://developer.chrome.com/docs/devtools/network/reference/#filter-by-type&quot; rel=&quot;noopener&quot;&gt;filter out non-image resources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Size&lt;/strong&gt; column to sort the image files by size&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;fix-6&quot;&gt;Fix &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#fix-6&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;em&gt;If you have limited time…&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Focus your time on Identifying large and frequently loaded images and manually optimizing them with a tool like &lt;a href=&quot;https://squoosh.app/&quot; rel=&quot;noopener&quot;&gt;Squoosh&lt;/a&gt;. Hero images are often good candidates for optimization.&lt;/p&gt;
&lt;p&gt;Things to keep in mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Size: Images should be no larger than necessary.&lt;/li&gt;
&lt;li&gt;Compression: Generally speaking, a quality level of 80-85 will have a minimal effect on image quality while yielding a 30-40% reduction in file size.&lt;/li&gt;
&lt;li&gt;Format: Use JPEGs for photos rather than PNG; use MP4 for &lt;a href=&quot;https://web.dev/replace-gifs-with-videos/&quot;&gt;animated content&lt;/a&gt; rather than GIF.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;If you have more time…&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Consider setting up an image CDN if images make up a substantial portion of your site. Image CDNs are designed for serving and optimizing images and they will offload image serving from the origin server. Setting up an image CDN is straightforward but requires updating existing image URLs to point at the image CDN.&lt;/p&gt;
&lt;p&gt;Further reading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/image-cdns/#optimize-your-images&quot;&gt;Use image CDNs to optimize images&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://images.guide/&quot; rel=&quot;noopener&quot;&gt;images.guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;minify-js-and-css&quot;&gt;Minify JS and CSS &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#minify-js-and-css&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Minification removes unnecessary characters from JavaScript and CSS.&lt;/p&gt;
&lt;h4 id=&quot;diagnose-6&quot;&gt;Diagnose &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#diagnose-6&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Use the &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/unminified-css/&quot; rel=&quot;noopener&quot;&gt;Minify CSS&lt;/a&gt; and &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/unminified-javascript/&quot; rel=&quot;noopener&quot;&gt;Minify JavaScript&lt;/a&gt; Lighthouse audits to identify resources that are in need of minification.&lt;/p&gt;
&lt;h4 id=&quot;fix-7&quot;&gt;Fix &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#fix-7&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;If you have limited time, focus on minifying your JavaScript. Most sites have more JavaScript than CSS, so this will be more impactful.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/reduce-network-payloads-using-text-compression/&quot;&gt;Minify JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/minify-css/&quot;&gt;Minify CSS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;monitor&quot;&gt;Monitor &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#monitor&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Server monitoring tools provide data collection, dashboards, and alerting regarding server performance. Their usage can help prevent and mitigate future server performance issues.&lt;/p&gt;
&lt;p&gt;A monitoring setup should be kept as simple as possible. Excessive data collection and alerting has its costs: the greater the scope or frequency of data collection, the more expensive it is to collect and store; excessive alerting inevitably leads to ignored pages.&lt;/p&gt;
&lt;p&gt;Alerting should use metrics that consistently and accurately detect issues. Server response time (latency) is a metric that works particularly well for this: it catches a wide variety of issues and correlates directly with user experience. Alerting based on lower-level metrics like CPU usage can be a useful supplement but will catch a smaller subset of issues. In addition, alerting should be based on the performance observed at the tail (in the other words the 95th or 99th percentiles), rather than averages. Otherwise, averages can easily obscure issues that don&#39;t affect all users.&lt;/p&gt;
&lt;h3 id=&quot;fix-8&quot;&gt;Fix &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/overloaded-server/#fix-8&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;All major cloud providers offer their own monitoring tooling (&lt;a href=&quot;https://codelabs.developers.google.com/codelabs/cloud-monitoring-alerting/index.html?index=..%2F..index&quot; rel=&quot;noopener&quot;&gt;GCP&lt;/a&gt;, &lt;a href=&quot;https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/monitoring-system-instance-status-check.html&quot; rel=&quot;noopener&quot;&gt;AWS&lt;/a&gt;, &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-monitor/&quot; rel=&quot;noopener&quot;&gt;Azure&lt;/a&gt;). In addition, &lt;a href=&quot;https://github.com/topics/monitoring&quot; rel=&quot;noopener&quot;&gt;Netdata&lt;/a&gt; is an excellent free and open-source alternative. Regardless of which tool you choose, you will need to install the tool&#39;s monitoring agent on each server that you want to monitor. Once complete, make sure to set up alerting.&lt;/p&gt;
&lt;p&gt;Instructions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://cloud.google.com/monitoring/alerts&quot; rel=&quot;noopener&quot;&gt;GCP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-system-instance-status-check.html&quot; rel=&quot;noopener&quot;&gt;AWS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/azure-monitor/app/alerts&quot; rel=&quot;noopener&quot;&gt;Azure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.netdata.cloud/health/&quot; rel=&quot;noopener&quot;&gt;Netdata&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>How to set up Signed Exchanges using Web Packager</title>
    <link href="https://web.dev/signed-exchanges-webpackager/"/>
    <updated>2020-02-19T00:00:00Z</updated>
    <id>https://web.dev/signed-exchanges-webpackager/</id>
    <content type="html" mode="escaped">&lt;p&gt;A &lt;a href=&quot;https://web.dev/signed-exchanges&quot;&gt;signed exchange (SXG)&lt;/a&gt; is a delivery mechanism that makes it
possible to authenticate the origin of a resource independently of how it was delivered.
The following instructions explain how to set up Signed Exchanges using
&lt;a href=&quot;https://github.com/google/webpackager&quot; rel=&quot;noopener&quot;&gt;Web Packager&lt;/a&gt;. Instructions are included for
both self-signed and &lt;code&gt;CanSignHttpExchanges&lt;/code&gt; certificates.&lt;/p&gt;
&lt;h2 id=&quot;serve-sxgs-using-a-self-signed-certificate&quot;&gt;Serve SXGs using a self-signed certificate &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges-webpackager/#serve-sxgs-using-a-self-signed-certificate&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Using a self-signed certificate to serve SXGs is primarily used for
demonstration and testing purposes. SXGs signed with a self-signed certificate
will generate error messages in the browser when used outside of testing
environments and should not be served to crawlers.&lt;/p&gt;
&lt;h3 id=&quot;prerequisites&quot;&gt;Prerequisites &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges-webpackager/#prerequisites&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To follow these instructions you will need to have
&lt;a href=&quot;https://github.com/openssl/openssl#build-and-install&quot; rel=&quot;noopener&quot;&gt;openssl&lt;/a&gt; and
&lt;a href=&quot;https://golang.org/doc/install&quot; rel=&quot;noopener&quot;&gt;Go&lt;/a&gt; installed in your development environment.&lt;/p&gt;
&lt;h3 id=&quot;generate-a-self-signed-certificate&quot;&gt;Generate a self-signed certificate &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges-webpackager/#generate-a-self-signed-certificate&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This section explains how to generate a self-signed certificate that can be
used with signed exchanges.&lt;/p&gt;
&lt;h4 id=&quot;instructions&quot;&gt;Instructions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges-webpackager/#instructions&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Generate a private key.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;openssl ecparam -out priv.key -name prime256v1 -genkey&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The private key will be saved as a file named &lt;code&gt;priv.key&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a &lt;a href=&quot;https://en.wikipedia.org/wiki/Certificate_signing_request&quot; rel=&quot;noopener&quot;&gt;certificate signing
request&lt;/a&gt; (CSR).&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;openssl req -new -sha256 -key priv.key -out cert.csr -subj &lt;span class=&quot;token string&quot;&gt;&#39;/O=Web Packager Demo/CN=example.com&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;A certificate signing request is a block of encoded text that conveys the
information necessary to request a certificate from a &lt;a href=&quot;https://en.wikipedia.org/wiki/Certificate_authority&quot; rel=&quot;noopener&quot;&gt;certificate authority(CA)&lt;/a&gt;. Although you will not be requesting a certificate from a
CA, it is still necessary to create a certificate signing request.&lt;/p&gt;
&lt;p&gt;The command above creates a certificate signing request for an organization
named &lt;code&gt;Web Packager Demo&lt;/code&gt; that has the &lt;a href=&quot;https://knowledge.digicert.com/solution/SO7239.html&quot; rel=&quot;noopener&quot;&gt;common
name&lt;/a&gt; &lt;code&gt;example.com&lt;/code&gt;. The
common name should be the fully qualified domain name of the site that contains
the content that you want to package as SXG.&lt;/p&gt;
&lt;p&gt;In a production SXG setup, this would be a site that you own. However, in a
testing environment like the one described in these instructions, it can be any
site.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a certificate that has the &lt;code&gt;CanSignHttpExchanges&lt;/code&gt; extension.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;openssl x509 -req -days &lt;span class=&quot;token number&quot;&gt;90&lt;/span&gt; -in cert.csr -signkey priv.key -out cert.pem -extfile &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; -e &lt;span class=&quot;token string&quot;&gt;&quot;1.3.6.1.4.1.11129.2.1.22 = ASN1:NULL&lt;span class=&quot;token entity&quot; title=&quot;\n&quot;&gt;\n&lt;/span&gt;subjectAltName=DNS:example.com&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;This command uses the private key and the CSR created in steps 1 and 2 to create the
certificate file &lt;code&gt;cert.pem&lt;/code&gt;. The &lt;code&gt;-extfile&lt;/code&gt; flag associates the certificate with
the &lt;code&gt;CanSignHttpExchanges&lt;/code&gt; certificate extension (&lt;code&gt;1.3.6.1.4.1.11129.2.1.22&lt;/code&gt; is
the &lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_certificate_system/9/html/administration_guide/standard_x.509_v3_certificate_extensions&quot; rel=&quot;noopener&quot;&gt;object
identifier&lt;/a&gt;
for the &lt;code&gt;CanSignHttpExchanges&lt;/code&gt; extension). In addition, the &lt;code&gt;-extfile&lt;/code&gt; flag also
defines &lt;code&gt;example.com&lt;/code&gt; as a &lt;a href=&quot;https://en.wikipedia.org/wiki/Subject_Alternative_Name&quot; rel=&quot;noopener&quot;&gt;Subject Alternative
Name&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you are curious about the contents of &lt;code&gt;cert.pem&lt;/code&gt;, you can view them using the
following command:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;openssl x509 -in cert.pem -noout -text&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You are done creating private keys and certificates. You will need the
&lt;code&gt;priv.key&lt;/code&gt; and &lt;code&gt;cert.pem&lt;/code&gt; files in the next section.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;setup-the-web-packager-server-for-testing&quot;&gt;Setup the Web Packager server for testing &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges-webpackager/#setup-the-web-packager-server-for-testing&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;prerequisites-2&quot;&gt;Prerequisites &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges-webpackager/#prerequisites-2&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;a href=&quot;https://github.com/google/webpackager&quot; rel=&quot;noopener&quot;&gt;Web Packager&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; clone https://github.com/google/webpackager.git&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Build &lt;code&gt;webpkgserver&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; webpackager/cmd/webpkgserver&lt;br /&gt;go build &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;webpkgserver&lt;/code&gt; is a specific binary within the Web Packager project.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify that &lt;code&gt;webpkgserver&lt;/code&gt; has been installed correctly.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;./webpkgserver --help&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This command should return information about the usage of &lt;code&gt;webpkgserver&lt;/code&gt;. If
this does not work, a good first troubleshooting step is to verify that your
&lt;a href=&quot;https://golang.org/doc/gopath_code.html#GOPATH&quot; rel=&quot;noopener&quot;&gt;GOPATH&lt;/a&gt; is configured
correctly.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;instructions-2&quot;&gt;Instructions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges-webpackager/#instructions-2&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the &lt;code&gt;webpkgserver&lt;/code&gt; directory (you might already be in this
directory).&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; /path/to/cmd/webpkgserver&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a &lt;code&gt;webpkgsever.toml&lt;/code&gt; file by copying the example.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;cp&lt;/span&gt; ./webpkgserver.example.toml ./webpkgserver.toml&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This file contains the configuration options for &lt;code&gt;webpkgserver&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open &lt;code&gt;webpkgserver.toml&lt;/code&gt; with an editor of your choice and make the following
changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Change the line &lt;code&gt;#AllowTestCert = false&lt;/code&gt; to &lt;code&gt;AllowTestCert = true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Change the line &lt;code&gt;PEMFile = &#39;path/to/your.pem&#39;&lt;/code&gt; to reflect the path to
the PEM certificate, &lt;code&gt;cert.pem&lt;/code&gt;, that you created. Do not change the
line mentioning &lt;code&gt;TLS.PEMFile&lt;/code&gt;—this is a different configuration option.&lt;/li&gt;
&lt;li&gt;Change the line &lt;code&gt;KeyFile = &#39;priv.key&#39;&lt;/code&gt; to reflect the path of the
private key, &lt;code&gt;priv.key&lt;/code&gt;, that you created. Do not change the line
mentioning &lt;code&gt;TLS.KeyFile&lt;/code&gt;—this is a different configuration option.&lt;/li&gt;
&lt;li&gt;Change the line &lt;code&gt;#CertURLBase = &#39;/webpkg/cert&#39;&lt;/code&gt; to &lt;code&gt;CertURLBase = &#39;data:&#39;&lt;/code&gt;. &lt;code&gt;CertURLBase&lt;/code&gt; indicates the serving location of the SXG
certificate. This information is used to set the &lt;code&gt;cert-url&lt;/code&gt; parameter in
the
&lt;a href=&quot;https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#name-the-signature-header&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Signature&lt;/code&gt;&lt;/a&gt;
header of the SXG. In production environments, &lt;code&gt;CertURLBase&lt;/code&gt; is used
like this: &lt;code&gt;CertURLBase = &#39;https://mysite.com/&#39;&lt;/code&gt;. However, for local
testing, &lt;code&gt;CertURLBase = &#39;data:&#39;&lt;/code&gt; can be used to instruct &lt;code&gt;webpkgserver&lt;/code&gt;
to use a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/Data_URIs&quot; rel=&quot;noopener&quot;&gt;data
URL&lt;/a&gt;
to inline the certificate in the &lt;code&gt;cert-url&lt;/code&gt; field. For local testing,
this is the most conveninent way to serve the SXG certificate.&lt;/li&gt;
&lt;li&gt;Change the line &lt;code&gt;Domain = &#39;example.org&#39;&lt;/code&gt; to reflect the domain that you
created a certificate for. If you have followed the instructions in this
article verbatim, this should be changed to &lt;code&gt;example.com&lt;/code&gt;.
&lt;code&gt;webpkgserver&lt;/code&gt; will only fetch content from the domain indicated by
&lt;code&gt;webpkgserver.toml&lt;/code&gt;. If you try to fetch pages from a different domain
without updating &lt;code&gt;webpkgserver.toml&lt;/code&gt;, the &lt;code&gt;webpkgserver&lt;/code&gt; logs will show
the error message &lt;code&gt;URL doesn&#39;t match the fetch targets&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Optional&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you want to enable or disable &lt;a href=&quot;https://github.com/WICG/webpackage/blob/main/explainers/signed-exchange-subresource-substitution.md&quot; rel=&quot;noopener&quot;&gt;subresource
preloading&lt;/a&gt;,
the following &lt;code&gt;webpkgserver.toml&lt;/code&gt; configuration options are relevant:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;To have &lt;code&gt;webpkgserver&lt;/code&gt; insert directives for preloading stylesheet
and script subresources as SXGs, change the line &lt;code&gt;#PreloadCSS = false&lt;/code&gt;
to &lt;code&gt;PreloadCSS = true&lt;/code&gt;. In addition, change the line &lt;code&gt;#PreloadJS = false&lt;/code&gt; to &lt;code&gt;PreloadJS = true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As an alternative to using this configuration option, you can manually
add &lt;code&gt;Link: rel=&amp;quot;preload&amp;quot;&lt;/code&gt; headers and &lt;code&gt;&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&lt;/code&gt; tags to a
page&#39;s HTML.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;By default, &lt;code&gt;webpkgserver&lt;/code&gt; replaces existing &lt;code&gt;&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&lt;/code&gt; tags
with the equivalent &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags necessary for fetching this content as
SXG. In doing so, &lt;code&gt;webpkgserver&lt;/code&gt; will set the
&lt;a href=&quot;https://github.com/WICG/webpackage/blob/main/explainers/signed-exchange-subresource-substitution.md#use-cases&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;allowed-alt-sxg&lt;/code&gt;&lt;/a&gt;
and
&lt;a href=&quot;https://github.com/WICG/webpackage/blob/main/explainers/signed-exchange-subresource-substitution.md#use-cases&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;header-integrity&lt;/code&gt;&lt;/a&gt;
directives as needed—HTML authors do not need to add these by hand. To
override this behavior and keep existing non-SXG preloads, change
&lt;code&gt;#KeepNonSXGPreloads (default = false)&lt;/code&gt; to &lt;code&gt;KeepNonSXGPreloads = true&lt;/code&gt;.
Keep in mind that enabling this option may make the SXG ineligible for
the Google SXG cache per these
&lt;a href=&quot;https://github.com/google/webpackager/blob/main/docs/cache_requirements.md&quot; rel=&quot;noopener&quot;&gt;requirements&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Start &lt;code&gt;webpkgserver&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;./webpkgserver&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If the server has started successfully, you should see the following log messages:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;Listening at &lt;span class=&quot;token number&quot;&gt;127.0&lt;/span&gt;.0.1:8080&lt;br /&gt;Successfully retrieved valid OCSP.&lt;br /&gt;Writing to cache &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; /private/tmp/webpkg&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Your log messages may look slightly different. In particular, the directory
that &lt;code&gt;webpkgserver&lt;/code&gt; uses to cache certificates varies by operating system.&lt;/p&gt;
&lt;p&gt;If you do not see these messages, a good first troubleshooting
step is to double-check &lt;code&gt;webpkgserver.toml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you update &lt;code&gt;webpkgserver.toml&lt;/code&gt; you should restart &lt;code&gt;webpkgserver&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Launch Chrome using the following command:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;/Applications/Google&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt; Chrome.app/Contents/MacOS/Google&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt; Chrome &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;--user-data-dir&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;/tmp/udd &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;br /&gt;--ignore-certificate-errors-spki-list&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;openssl x509 -noout -pubkey -in cert.pem &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; openssl pkey -pubin -outform der &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; openssl dgst -sha256 -binary &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; base64&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This command instructs Chrome to ignore the certificate errors associated
with &lt;code&gt;cert.pem&lt;/code&gt;. This makes it possible to test SXGs using a test
certificate. If Chrome is launched without this command, inspecting the SXG
in DevTools will display the error &lt;code&gt;Certificate verification error:  ERR_CERT_INVALID&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You may need to adjust this command to reflect the location of Chrome on
your machine, as well as the location of &lt;code&gt;cert.pem&lt;/code&gt;. If you&#39;ve done this
correctly, you should see a warning displayed below the address bar. The
warning should be similar to this: &lt;code&gt;You are using an unsupported  command-line flag:  --ignore-certificate-errors-spki-list=9uxADcgc6/ho0mJLRMBcOjfBaN21k0sOInoMchr9CMY=.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If the warning does not include a hash string, you have not correctly
indicated the location of the SXG certificate.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the DevTools &lt;strong&gt;Network&lt;/strong&gt; tab, then visit the following URL:
&lt;code&gt;http://localhost:8080/priv/doc/https://example.com&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This makes a request to the &lt;code&gt;webpackager&lt;/code&gt; instance running at
&lt;code&gt;http://localhost:8080&lt;/code&gt; for a SXG containing the contents of
&lt;code&gt;https://example.com&lt;/code&gt;. &lt;code&gt;/priv/doc/&lt;/code&gt; is the default API endpoint used by
&lt;code&gt;webpackager&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
  &lt;img alt=&quot;Screenshot of the DevTools Network tab showing a SXG and its certificate.&quot; decoding=&quot;async&quot; height=&quot;236&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/DPw05KcHLdWOgHYiDsgf.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/DPw05KcHLdWOgHYiDsgf.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/DPw05KcHLdWOgHYiDsgf.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/DPw05KcHLdWOgHYiDsgf.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/DPw05KcHLdWOgHYiDsgf.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/DPw05KcHLdWOgHYiDsgf.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/DPw05KcHLdWOgHYiDsgf.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/DPw05KcHLdWOgHYiDsgf.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/DPw05KcHLdWOgHYiDsgf.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/DPw05KcHLdWOgHYiDsgf.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/DPw05KcHLdWOgHYiDsgf.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/DPw05KcHLdWOgHYiDsgf.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/DPw05KcHLdWOgHYiDsgf.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/DPw05KcHLdWOgHYiDsgf.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/DPw05KcHLdWOgHYiDsgf.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/DPw05KcHLdWOgHYiDsgf.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/DPw05KcHLdWOgHYiDsgf.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/DPw05KcHLdWOgHYiDsgf.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;The following resources are listed in the &lt;strong&gt;Network&lt;/strong&gt; tab:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A resource with the type &lt;code&gt;signed-exchange&lt;/code&gt;. This is the SXG.&lt;/li&gt;
&lt;li&gt;A resource with the type &lt;code&gt;cert-chain+cbor&lt;/code&gt;. This the SXG certificate. SXG certificates must use the &lt;code&gt;application/cert-chain+cbor&lt;/code&gt; format.&lt;/li&gt;
&lt;li&gt;A resource with the type &lt;code&gt;document&lt;/code&gt;. This is the content that has been delivered via SXG.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you don&#39;t see these resources, try clearing the browser cache, then
reloading &lt;code&gt;http://localhost:8080/priv/doc/https://example.com&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Click on the &lt;strong&gt;Preview&lt;/strong&gt; tab to see more information about the Signed Exchange
and its signature.&lt;/p&gt;
  &lt;img alt=&quot;Screenshot of the Preview tab showing a SXG&quot; decoding=&quot;async&quot; height=&quot;541&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/x9hfT6ZJ8OuB4M1ImWic.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/x9hfT6ZJ8OuB4M1ImWic.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/x9hfT6ZJ8OuB4M1ImWic.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/x9hfT6ZJ8OuB4M1ImWic.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/x9hfT6ZJ8OuB4M1ImWic.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/x9hfT6ZJ8OuB4M1ImWic.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/x9hfT6ZJ8OuB4M1ImWic.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/x9hfT6ZJ8OuB4M1ImWic.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/x9hfT6ZJ8OuB4M1ImWic.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/x9hfT6ZJ8OuB4M1ImWic.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/x9hfT6ZJ8OuB4M1ImWic.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/x9hfT6ZJ8OuB4M1ImWic.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/x9hfT6ZJ8OuB4M1ImWic.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/x9hfT6ZJ8OuB4M1ImWic.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/x9hfT6ZJ8OuB4M1ImWic.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/x9hfT6ZJ8OuB4M1ImWic.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/x9hfT6ZJ8OuB4M1ImWic.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/j2RDdG43oidUy6AL6LovThjeX9c2/x9hfT6ZJ8OuB4M1ImWic.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h2 id=&quot;serve-signed-exchanges-using-a-cansignhttpexchanges-certificate&quot;&gt;Serve signed exchanges using a &lt;code&gt;CanSignHttpExchanges&lt;/code&gt; certificate &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges-webpackager/#serve-signed-exchanges-using-a-cansignhttpexchanges-certificate&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The instructions in this section explain how to serve SXGs using a
&lt;code&gt;CanSignHttpExchanges&lt;/code&gt; certificate. Production use of SXGs requires a
&lt;code&gt;CanSignHttpExchanges&lt;/code&gt; certificate.&lt;/p&gt;
&lt;p&gt;For the sake of brevity, these instructions are written with the assumption
that you understand the concepts discussed in the &lt;a href=&quot;https://web.dev/signed-exchanges-webpackager/#serve-sxgs-using-a-self-signed-certificate&quot;&gt;Setup Signed Exchanges
using a self-signed
certificate&lt;/a&gt;
section.&lt;/p&gt;
&lt;h3 id=&quot;prerequisites-3&quot;&gt;Prerequisites &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges-webpackager/#prerequisites-3&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;You have a &lt;code&gt;CanSignHttpExchanges&lt;/code&gt; certificate. This
&lt;a href=&quot;https://github.com/google/webpackager/wiki/Certificate-Authorities&quot; rel=&quot;noopener&quot;&gt;page&lt;/a&gt;
lists the CAs that offer this type of certificate.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you don&#39;t have a certificate, then you can configure your webpkgserver to
automatically retrieve certificates from your CA. You can follow the
directions for what goes in &lt;code&gt;webpkgserver.toml&lt;/code&gt; in this
&lt;a href=&quot;https://github.com/google/webpackager/blob/main/cmd/webpkgserver/README.md#configuration&quot; rel=&quot;noopener&quot;&gt;page&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Although not a requirement, it is strongly recommended that you run
&lt;code&gt;webpkgserver&lt;/code&gt; behind an edge server. If you do not use an edge server, you
will need to configure the &lt;code&gt;TLS.PEMFile&lt;/code&gt; and &lt;code&gt;TLS.KeyFile&lt;/code&gt; options in
&lt;code&gt;webpkgserver.toml&lt;/code&gt;. By default, &lt;code&gt;webpkgserver&lt;/code&gt; runs over HTTP. However, SXG
certificates must be served over HTTPS to be considered valid by the browser.
Configuring &lt;code&gt;TLS.PEMFile&lt;/code&gt; and &lt;code&gt;TLS.KeyFile&lt;/code&gt; allows &lt;code&gt;webpkgserver&lt;/code&gt; to use
HTTPS and therefore serve the SXG certificate directly to the browser.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;instructions-3&quot;&gt;Instructions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/signed-exchanges-webpackager/#instructions-3&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a PEM file by concatenating your site&#39;s SXG certificate followed by
your site&#39;s CA certificate. More instructions on this can be found
&lt;a href=&quot;https://www.digicert.com/kb/ssl-support/pem-ssl-creation.htm&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/X.509#Certificate_filename_extensions&quot; rel=&quot;noopener&quot;&gt;PEM&lt;/a&gt; is
a file format that is commonly used as a &amp;quot;container&amp;quot; for storing multiple
certificates.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a fresh &lt;code&gt;webpkgsever.toml&lt;/code&gt; file by copying the example.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;cp&lt;/span&gt; ./webpkgserver.example.toml ./webpkgserver.toml&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open &lt;code&gt;webpkgserver.toml&lt;/code&gt; with the editor of your choice and make the
following changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Change the line &lt;code&gt;PEMFile = cert.pem&lt;/code&gt; to reflect the location of the PEM
file containing your full certificate chain.&lt;/li&gt;
&lt;li&gt;Change the line &lt;code&gt;KeyFile = &#39;priv.key&#39;&lt;/code&gt; to reflect the location of the
private key corresponding to your PEM File.&lt;/li&gt;
&lt;li&gt;Change the line &lt;code&gt;Domain = &#39;example.org&#39;&lt;/code&gt; to reflect your site.&lt;/li&gt;
&lt;li&gt;(Optional) To have &lt;code&gt;webpkgserver&lt;/code&gt; auto-renew the SXG certificate every
90 days (45 days for Google), configure the options in the &lt;code&gt;[SXG.ACME]&lt;/code&gt; section of
&lt;code&gt;webpkgserver.toml&lt;/code&gt;. This option only applies to sites with a DigiCert
or Google ACME account setup.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Configure your edge server to forward traffic to the &lt;code&gt;webpkgserver&lt;/code&gt;
instance.&lt;/p&gt;
&lt;p&gt;There are two primary types of requests handled by &lt;code&gt;webpkgserver&lt;/code&gt;: requests
for SXGs (which are served by the &lt;code&gt;/priv/doc/&lt;/code&gt; endpoint) and requests for
the SXG certificate (which are served by the &lt;code&gt;/webpkg/cert/&lt;/code&gt; endpoint). The
URL rewriting rules for each of these request types varies slightly. For
more information, see &lt;a href=&quot;https://github.com/google/webpackager/blob/main/cmd/webpkgserver/README.md#running-behind-front-end-edge-server&quot; rel=&quot;noopener&quot;&gt;Running behind front end edge
server&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;By default, &lt;code&gt;webpkgserver&lt;/code&gt; serves the SXG certificate at
&lt;code&gt;/webpkg/cert/$CERT_HASH&lt;/code&gt;—for example,
&lt;code&gt;/webpkg/cert/-0QmE0gvoedn92gtwI3s7On9zPevJGm5pn2RYhpZxgY&lt;/code&gt;.
To generate &lt;code&gt;$CERT_HASH&lt;/code&gt;, run the following command:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;openssl base64 -in cert.pem -d &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; openssl dgst -sha256 -binary &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; base64 &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;tr&lt;/span&gt; /+ _- &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;tr&lt;/span&gt; -d &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Optimize images with Thumbor</title>
    <link href="https://web.dev/use-thumbor/"/>
    <updated>2019-09-23T00:00:00Z</updated>
    <id>https://web.dev/use-thumbor/</id>
    <content type="html" mode="escaped">&lt;p&gt;&lt;a href=&quot;http://thumbor.org/&quot; rel=&quot;noopener&quot;&gt;Thumbor&lt;/a&gt; is a free, open source image CDN that makes it easy to compress, resize, and transform images. This post lets you try out Thumbor firsthand without needing to install anything. We&#39;ve set up a sandbox Thumbor server for you to try out at &lt;code&gt;http://34.67.235.246:8888&lt;/code&gt;. The image that you&#39;re going to experiment with is available at &lt;a href=&quot;http://34.67.235.246:8888/unsafe/https://web.dev/backdrop-filter/hero.jpg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;http://34.67.235.246:8888/unsafe/https://web.dev/backdrop-filter/hero.jpg&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;prequisites&quot;&gt;Prequisites &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-thumbor/#prequisites&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This post assumes that you understand how image CDNs can improve your load performance. If not, check out &lt;a href=&quot;https://web.dev/image-cdns&quot;&gt;Use image CDNs to optimize images&lt;/a&gt;. It also assumes that you&#39;ve built basic websites before.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt;  If you would like to install Thumbor on your own server and then follow along with this post, check out &lt;a href=&quot;https://web.dev/install-thumbor&quot;&gt;How to install the Thumbor image CDN&lt;/a&gt;. Whenever you see &lt;code&gt;http://34.67.235.246:8888&lt;/code&gt; in this post you&#39;ll need to replace that origin with your Thumbor instance&#39;s origin.  &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;thumbor-url-format&quot;&gt;Thumbor URL Format &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-thumbor/#thumbor-url-format&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As mentioned in &lt;a href=&quot;https://web.dev/image-cdns&quot;&gt;Use Image CDNs to Optimize Images&lt;/a&gt;, each image CDN uses a slightly different URL format for images. Figure 1 represents Thumbor&#39;s format.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A Thumbor URL has the following components: origin, security key, size, filters and image.&quot; decoding=&quot;async&quot; height=&quot;89&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/lo1hS8qn53XCztrlgvl7.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/lo1hS8qn53XCztrlgvl7.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/lo1hS8qn53XCztrlgvl7.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/lo1hS8qn53XCztrlgvl7.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/lo1hS8qn53XCztrlgvl7.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/lo1hS8qn53XCztrlgvl7.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/lo1hS8qn53XCztrlgvl7.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/lo1hS8qn53XCztrlgvl7.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/lo1hS8qn53XCztrlgvl7.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/lo1hS8qn53XCztrlgvl7.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/lo1hS8qn53XCztrlgvl7.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/lo1hS8qn53XCztrlgvl7.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/lo1hS8qn53XCztrlgvl7.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/lo1hS8qn53XCztrlgvl7.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/lo1hS8qn53XCztrlgvl7.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/lo1hS8qn53XCztrlgvl7.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/lo1hS8qn53XCztrlgvl7.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/lo1hS8qn53XCztrlgvl7.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Thumbor&#39;s URL format&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;origin&quot;&gt;Origin &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-thumbor/#origin&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Like all &lt;a href=&quot;https://html.spec.whatwg.org/multipage/origin.html#concept-origin&quot; rel=&quot;noopener&quot;&gt;origins&lt;/a&gt;, the origin of a Thumbor URL is composed of three parts: a &lt;a href=&quot;https://en.wikipedia.org/wiki/Uniform_Resource_Identifier#Definition&quot; rel=&quot;noopener&quot;&gt;scheme&lt;/a&gt; (which is almost always &lt;code&gt;http&lt;/code&gt; or &lt;code&gt;https&lt;/code&gt;), a host, and a port. In this example, the host is identified using an IP address, but if you&#39;re using a DNS server it might look like &lt;code&gt;thumbor-server.my-site.com&lt;/code&gt;. By default, Thumbor uses port &lt;code&gt;8888&lt;/code&gt; to serve images.&lt;/p&gt;
&lt;h3 id=&quot;security-key&quot;&gt;Security Key &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-thumbor/#security-key&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;unsafe&lt;/code&gt; part of the URL indicates that you&#39;re using Thumbor without a security key. A security key prevents a user from making unauthorized changes to your image URLs. By changing the image URL, a user could use your server (and your hosting bill) to resize their images, or, more maliciously, to overload your server. This guide won&#39;t cover setting up &lt;a href=&quot;https://github.com/thumbor/thumbor/wiki/security&quot; rel=&quot;noopener&quot;&gt;Thumbor&#39;s security key feature&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;size&quot;&gt;Size &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-thumbor/#size&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This part of the URL specifies the desired size of the output image. This can be omitted if you don&#39;t want to change the size of the image. Thumbor will use different approaches like cropping or scaling to achieve the desired size depending on the other URL parameters. The next section of this post explains how to resize images in more detail.&lt;/p&gt;
&lt;p&gt;Try it now:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Click the following URL to view the image served at its original size in a new tab: &lt;a href=&quot;http://34.67.235.246:8888/unsafe/https://web.dev/backdrop-filter/hero.jpg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;http://34.67.235.246:8888/unsafe/https://web.dev/backdrop-filter/hero.jpg&lt;/a&gt;&lt;/p&gt;
 &lt;figure&gt;
   &lt;img alt=&quot;Image at original size&quot; decoding=&quot;async&quot; height=&quot;500&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/R2Xp5XxJi4CFGjXlPx4X.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/R2Xp5XxJi4CFGjXlPx4X.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/R2Xp5XxJi4CFGjXlPx4X.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/R2Xp5XxJi4CFGjXlPx4X.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/R2Xp5XxJi4CFGjXlPx4X.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/R2Xp5XxJi4CFGjXlPx4X.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/R2Xp5XxJi4CFGjXlPx4X.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/R2Xp5XxJi4CFGjXlPx4X.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/R2Xp5XxJi4CFGjXlPx4X.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/R2Xp5XxJi4CFGjXlPx4X.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/R2Xp5XxJi4CFGjXlPx4X.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/R2Xp5XxJi4CFGjXlPx4X.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/R2Xp5XxJi4CFGjXlPx4X.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/R2Xp5XxJi4CFGjXlPx4X.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/R2Xp5XxJi4CFGjXlPx4X.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/R2Xp5XxJi4CFGjXlPx4X.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/R2Xp5XxJi4CFGjXlPx4X.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/R2Xp5XxJi4CFGjXlPx4X.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
   &lt;figcaption&gt;Original image&lt;/figcaption&gt;
 &lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Resize the image to 100x100 pixels: &lt;a href=&quot;http://34.67.235.246:8888/unsafe/100x100/https://web.dev/backdrop-filter/hero.jpg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;http://34.67.235.246:8888/unsafe/100x100/https://web.dev/backdrop-filter/hero.jpg&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Image at 100x100 pixels&quot; decoding=&quot;async&quot; height=&quot;505&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/QXf1r4Ov6gXDtbrcmLWZ.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/QXf1r4Ov6gXDtbrcmLWZ.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/QXf1r4Ov6gXDtbrcmLWZ.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/QXf1r4Ov6gXDtbrcmLWZ.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/QXf1r4Ov6gXDtbrcmLWZ.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/QXf1r4Ov6gXDtbrcmLWZ.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/QXf1r4Ov6gXDtbrcmLWZ.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/QXf1r4Ov6gXDtbrcmLWZ.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/QXf1r4Ov6gXDtbrcmLWZ.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/QXf1r4Ov6gXDtbrcmLWZ.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/QXf1r4Ov6gXDtbrcmLWZ.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/QXf1r4Ov6gXDtbrcmLWZ.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/QXf1r4Ov6gXDtbrcmLWZ.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/QXf1r4Ov6gXDtbrcmLWZ.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/QXf1r4Ov6gXDtbrcmLWZ.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/QXf1r4Ov6gXDtbrcmLWZ.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/QXf1r4Ov6gXDtbrcmLWZ.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/QXf1r4Ov6gXDtbrcmLWZ.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Image resized to 100x100 pixels&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;filters&quot;&gt;Filters &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-thumbor/#filters&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Filters transform an image. The filters part of the URL segment starts with &lt;code&gt;filters:&lt;/code&gt; followed by a colon-separated list of filters; this can be omitted if you are not using any filters. The syntax for individual filters resembles a function call (for example &lt;code&gt;grayscale()&lt;/code&gt;) containing zero or more arguments.&lt;/p&gt;
&lt;p&gt;Try it now:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Apply a single filter: a Gaussian &lt;a href=&quot;https://thumbor.readthedocs.io/en/latest/blur.html&quot; rel=&quot;noopener&quot;&gt;blur&lt;/a&gt; effect with a radius of 25 pixels: &lt;a href=&quot;http://34.67.235.246:8888/unsafe/filters:blur(25)/https://web.dev/backdrop-filter/hero.jpg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;http://34.67.235.246:8888/unsafe/filters:blur(25)/https://web.dev/backdrop-filter/hero.jpg&lt;/a&gt;&lt;/p&gt;
 &lt;figure&gt;
   &lt;img alt=&quot;Blurred image&quot; decoding=&quot;async&quot; height=&quot;505&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/e5zG6ghl8IADjEKMGBzf.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/e5zG6ghl8IADjEKMGBzf.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/e5zG6ghl8IADjEKMGBzf.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/e5zG6ghl8IADjEKMGBzf.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/e5zG6ghl8IADjEKMGBzf.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/e5zG6ghl8IADjEKMGBzf.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/e5zG6ghl8IADjEKMGBzf.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/e5zG6ghl8IADjEKMGBzf.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/e5zG6ghl8IADjEKMGBzf.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/e5zG6ghl8IADjEKMGBzf.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/e5zG6ghl8IADjEKMGBzf.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/e5zG6ghl8IADjEKMGBzf.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/e5zG6ghl8IADjEKMGBzf.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/e5zG6ghl8IADjEKMGBzf.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/e5zG6ghl8IADjEKMGBzf.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/e5zG6ghl8IADjEKMGBzf.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/e5zG6ghl8IADjEKMGBzf.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/e5zG6ghl8IADjEKMGBzf.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
   &lt;figcaption&gt;Blurred image&lt;/figcaption&gt;
 &lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Apply multiple filter. Convert to &lt;a href=&quot;https://thumbor.readthedocs.io/en/latest/grayscale.html&quot; rel=&quot;noopener&quot;&gt;grayscale&lt;/a&gt; and &lt;a href=&quot;https://thumbor.readthedocs.io/en/latest/rotate.html&quot; rel=&quot;noopener&quot;&gt;rotate&lt;/a&gt; the image 90 degrees: &lt;a href=&quot;http://34.67.235.246:8888/unsafe/filters:grayscale():blur(90)/https://web.dev/backdrop-filter/hero.jpg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;http://34.67.235.246:8888/unsafe/filters:grayscale():blur(90)/https://web.dev/backdrop-filter/hero.jpg&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Grayscale image that has been rotated 90 degrees&quot; decoding=&quot;async&quot; height=&quot;505&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/U9atnYPla5L93UmVx9di.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/U9atnYPla5L93UmVx9di.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/U9atnYPla5L93UmVx9di.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/U9atnYPla5L93UmVx9di.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/U9atnYPla5L93UmVx9di.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/U9atnYPla5L93UmVx9di.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/U9atnYPla5L93UmVx9di.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/U9atnYPla5L93UmVx9di.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/U9atnYPla5L93UmVx9di.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/U9atnYPla5L93UmVx9di.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/U9atnYPla5L93UmVx9di.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/U9atnYPla5L93UmVx9di.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/U9atnYPla5L93UmVx9di.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/U9atnYPla5L93UmVx9di.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/U9atnYPla5L93UmVx9di.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/U9atnYPla5L93UmVx9di.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/U9atnYPla5L93UmVx9di.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/U9atnYPla5L93UmVx9di.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Grayscale, rotated image&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;transforming-images&quot;&gt;Transforming Images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-thumbor/#transforming-images&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This section focuses on the Thumbor functionalities most relevant to performance: compression, resizing, and conversion between file formats.&lt;/p&gt;
&lt;h3 id=&quot;compression&quot;&gt;Compression &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-thumbor/#compression&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://thumbor.readthedocs.io/en/latest/quality.html&quot; rel=&quot;noopener&quot;&gt;quality&lt;/a&gt; filter compresses JPEG images to the desired image quality level (1-100). If no quality level is provided, Thumbor compresses the image to a quality level of 80. This is a good default: quality levels 80-85 typically have little noticeable effect on image quality, but usually decrease image size by 30-40%.&lt;/p&gt;
&lt;p&gt;Try it now:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Compress the image to a quality of 1 (very bad): &lt;a href=&quot;http://34.67.235.246:8888/unsafe/filters:quality(1)/https://web.dev/backdrop-filter/hero.jpg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;http://34.67.235.246:8888/unsafe/filters:quality(1)/https://web.dev/backdrop-filter/hero.jpg&lt;/a&gt;&lt;/p&gt;
 &lt;figure&gt;
   &lt;img alt=&quot;Low-quality image&quot; decoding=&quot;async&quot; height=&quot;505&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/DyC3mcwd1vn0Xnv7GUco.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/DyC3mcwd1vn0Xnv7GUco.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/DyC3mcwd1vn0Xnv7GUco.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/DyC3mcwd1vn0Xnv7GUco.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/DyC3mcwd1vn0Xnv7GUco.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/DyC3mcwd1vn0Xnv7GUco.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/DyC3mcwd1vn0Xnv7GUco.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/DyC3mcwd1vn0Xnv7GUco.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/DyC3mcwd1vn0Xnv7GUco.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/DyC3mcwd1vn0Xnv7GUco.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/DyC3mcwd1vn0Xnv7GUco.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/DyC3mcwd1vn0Xnv7GUco.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/DyC3mcwd1vn0Xnv7GUco.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/DyC3mcwd1vn0Xnv7GUco.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/DyC3mcwd1vn0Xnv7GUco.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/DyC3mcwd1vn0Xnv7GUco.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/DyC3mcwd1vn0Xnv7GUco.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/DyC3mcwd1vn0Xnv7GUco.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
   &lt;figcaption&gt;Low-quality image&lt;/figcaption&gt;
 &lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Compress the image using Thumbor&#39;s default compression settings: &lt;a href=&quot;http://34.67.235.246:8888/unsafe/filters:quality()/https://web.dev/backdrop-filter/hero.jpg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;http://34.67.235.246:8888/unsafe/filters:quality()/https://web.dev/backdrop-filter/hero.jpg&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Compressed image with no noticible quality issues&quot; decoding=&quot;async&quot; height=&quot;505&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/vOZpDiHEPMTQOEZ3YG7e.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/vOZpDiHEPMTQOEZ3YG7e.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/vOZpDiHEPMTQOEZ3YG7e.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/vOZpDiHEPMTQOEZ3YG7e.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/vOZpDiHEPMTQOEZ3YG7e.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/vOZpDiHEPMTQOEZ3YG7e.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/vOZpDiHEPMTQOEZ3YG7e.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/vOZpDiHEPMTQOEZ3YG7e.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/vOZpDiHEPMTQOEZ3YG7e.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/vOZpDiHEPMTQOEZ3YG7e.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/vOZpDiHEPMTQOEZ3YG7e.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/vOZpDiHEPMTQOEZ3YG7e.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/vOZpDiHEPMTQOEZ3YG7e.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/vOZpDiHEPMTQOEZ3YG7e.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/vOZpDiHEPMTQOEZ3YG7e.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/vOZpDiHEPMTQOEZ3YG7e.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/vOZpDiHEPMTQOEZ3YG7e.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/vOZpDiHEPMTQOEZ3YG7e.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Compressed image&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;resizing&quot;&gt;Resizing &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-thumbor/#resizing&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To resize an image while maintaining its original proportions use the format &lt;code&gt;$WIDTHx0&lt;/code&gt; or &lt;code&gt;0x$HEIGHT&lt;/code&gt; within the &lt;code&gt;size&lt;/code&gt; portion of the URL string.&lt;/p&gt;
&lt;p&gt;Try it now:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Resize the image to a width of 200 pixels while maintaining original proportions: &lt;a href=&quot;http://34.67.235.246:8888/unsafe/200x0/https://web.dev/backdrop-filter/hero.jpg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;http://34.67.235.246:8888/unsafe/200x0/https://web.dev/backdrop-filter/hero.jpg&lt;/a&gt;&lt;/p&gt;
 &lt;!-- lint disable code-block-style --&gt;
 &lt;figure&gt;
   &lt;img alt=&quot;Image that is 200 pixels wide&quot; decoding=&quot;async&quot; height=&quot;505&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/afo1UErx1tzpBz5mO0nQ.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/afo1UErx1tzpBz5mO0nQ.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/afo1UErx1tzpBz5mO0nQ.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/afo1UErx1tzpBz5mO0nQ.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/afo1UErx1tzpBz5mO0nQ.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/afo1UErx1tzpBz5mO0nQ.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/afo1UErx1tzpBz5mO0nQ.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/afo1UErx1tzpBz5mO0nQ.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/afo1UErx1tzpBz5mO0nQ.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/afo1UErx1tzpBz5mO0nQ.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/afo1UErx1tzpBz5mO0nQ.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/afo1UErx1tzpBz5mO0nQ.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/afo1UErx1tzpBz5mO0nQ.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/afo1UErx1tzpBz5mO0nQ.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/afo1UErx1tzpBz5mO0nQ.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/afo1UErx1tzpBz5mO0nQ.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/afo1UErx1tzpBz5mO0nQ.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/afo1UErx1tzpBz5mO0nQ.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
   &lt;figcaption&gt;Image resized to a width of 200 pixels&lt;/figcaption&gt;
 &lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Resize the image to a height of 500 pixels while maintaining original proportion: &lt;a href=&quot;http://34.67.235.246:8888/unsafe/0x500/https://web.dev/backdrop-filter/hero.jpg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;http://34.67.235.246:8888/unsafe/0x500/https://web.dev/backdrop-filter/hero.jpg&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Image that is 500 pixels tall&quot; decoding=&quot;async&quot; height=&quot;505&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/ln4jTuQjlK8DDsutTH9i.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/ln4jTuQjlK8DDsutTH9i.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/ln4jTuQjlK8DDsutTH9i.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/ln4jTuQjlK8DDsutTH9i.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/ln4jTuQjlK8DDsutTH9i.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/ln4jTuQjlK8DDsutTH9i.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/ln4jTuQjlK8DDsutTH9i.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/ln4jTuQjlK8DDsutTH9i.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/ln4jTuQjlK8DDsutTH9i.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/ln4jTuQjlK8DDsutTH9i.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/ln4jTuQjlK8DDsutTH9i.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/ln4jTuQjlK8DDsutTH9i.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/ln4jTuQjlK8DDsutTH9i.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/ln4jTuQjlK8DDsutTH9i.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/ln4jTuQjlK8DDsutTH9i.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/ln4jTuQjlK8DDsutTH9i.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/ln4jTuQjlK8DDsutTH9i.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/ln4jTuQjlK8DDsutTH9i.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Image resized to a height of 500 pixels&lt;figcaption&gt;
&lt;/figcaption&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;You can also resize images to a percentage of the original by using the &lt;a href=&quot;https://thumbor.readthedocs.io/en/latest/proportion.html&quot; rel=&quot;noopener&quot;&gt;proportion&lt;/a&gt; filter. If size is specified in conjunction with the proportion filter, the image will be resized, and then the proportion filter will be applied.&lt;/p&gt;
&lt;p&gt;Try it now:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Resize the image to 50% of the original: &lt;a href=&quot;http://34.67.235.246:8888/unsafe/filters:proportion(.5)/https://web.dev/backdrop-filter/hero.jpg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;http://34.67.235.246:8888/unsafe/filters:proportion(.5)/https://web.dev/backdrop-filter/hero.jpg&lt;/a&gt;&lt;/p&gt;
 &lt;figure&gt;
   &lt;img alt=&quot;Image that is 50% the size of the original&quot; decoding=&quot;async&quot; height=&quot;505&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/KmAi5ht9IUiFPkyu6zjA.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/KmAi5ht9IUiFPkyu6zjA.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/KmAi5ht9IUiFPkyu6zjA.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/KmAi5ht9IUiFPkyu6zjA.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/KmAi5ht9IUiFPkyu6zjA.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/KmAi5ht9IUiFPkyu6zjA.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/KmAi5ht9IUiFPkyu6zjA.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/KmAi5ht9IUiFPkyu6zjA.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/KmAi5ht9IUiFPkyu6zjA.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/KmAi5ht9IUiFPkyu6zjA.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/KmAi5ht9IUiFPkyu6zjA.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/KmAi5ht9IUiFPkyu6zjA.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/KmAi5ht9IUiFPkyu6zjA.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/KmAi5ht9IUiFPkyu6zjA.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/KmAi5ht9IUiFPkyu6zjA.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/KmAi5ht9IUiFPkyu6zjA.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/KmAi5ht9IUiFPkyu6zjA.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/KmAi5ht9IUiFPkyu6zjA.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
   &lt;figcaption&gt;Image resized to 50% the size of the original&lt;/figcaption&gt;
 &lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Resize the image to a width of 1000 pixels, then resize the image to 10% of its current size: &lt;a href=&quot;http://34.67.235.246:8888/unsafe/1000x/filters:proportion(.1)/https://web.dev/backdrop-filter/hero.jpg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;http://34.67.235.246:8888/unsafe/1000x/filters:proportion(.1)/https://web.dev/backdrop-filter/hero.jpg&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Image that is 100 pixels wide&quot; decoding=&quot;async&quot; height=&quot;505&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/F4jHvji47nFA7RiVdsAF.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/F4jHvji47nFA7RiVdsAF.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/F4jHvji47nFA7RiVdsAF.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/F4jHvji47nFA7RiVdsAF.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/F4jHvji47nFA7RiVdsAF.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/F4jHvji47nFA7RiVdsAF.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/F4jHvji47nFA7RiVdsAF.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/F4jHvji47nFA7RiVdsAF.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/F4jHvji47nFA7RiVdsAF.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/F4jHvji47nFA7RiVdsAF.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/F4jHvji47nFA7RiVdsAF.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/F4jHvji47nFA7RiVdsAF.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/F4jHvji47nFA7RiVdsAF.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/F4jHvji47nFA7RiVdsAF.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/F4jHvji47nFA7RiVdsAF.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/F4jHvji47nFA7RiVdsAF.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/F4jHvji47nFA7RiVdsAF.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/F4jHvji47nFA7RiVdsAF.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Image resized to a width of 100 pixels&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;These methods are just a few of Thumbor&#39;s many cropping and resizing options. To read about other options, check out &lt;a href=&quot;https://github.com/thumbor/thumbor/wiki/Usage&quot; rel=&quot;noopener&quot;&gt;Usage&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;file-formats&quot;&gt;File Formats &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-thumbor/#file-formats&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://thumbor.readthedocs.io/en/latest/format.html&quot; rel=&quot;noopener&quot;&gt;format&lt;/a&gt; filter converts images to &lt;code&gt;jpeg&lt;/code&gt;, &lt;code&gt;webp&lt;/code&gt;, &lt;code&gt;gif&lt;/code&gt;, or &lt;code&gt;png&lt;/code&gt;. Keep in mind that if you&#39;re optimizing for performance you should &lt;a href=&quot;https://web.dev/fast/#optimize-your-images&quot;&gt;use either JPEG or WebP&lt;/a&gt; as PNG and GIF files tend to be significantly larger and do not compress as well.&lt;/p&gt;
&lt;p&gt;Try it now:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Convert the image to WebP. If you open the &lt;strong&gt;Network&lt;/strong&gt; panel of DevTools the document&#39;s &lt;strong&gt;Content-Type response header&lt;/strong&gt; shows that the server returned a WebP image: &lt;a href=&quot;http://34.67.235.246:8888/unsafe/filters:format(webp)/https://web.dev/backdrop-filter/hero.jpg&quot; target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;http://34.67.235.246:8888/unsafe/filters:format(webp)/https://web.dev/backdrop-filter/hero.jpg&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
  &lt;img alt=&quot;DevTools screenshot showing the content-type (WebP) of an image&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/78Jp9l7N0gUQtiuxbNSn.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/78Jp9l7N0gUQtiuxbNSn.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/78Jp9l7N0gUQtiuxbNSn.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/78Jp9l7N0gUQtiuxbNSn.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/78Jp9l7N0gUQtiuxbNSn.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/78Jp9l7N0gUQtiuxbNSn.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/78Jp9l7N0gUQtiuxbNSn.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/78Jp9l7N0gUQtiuxbNSn.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/78Jp9l7N0gUQtiuxbNSn.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/78Jp9l7N0gUQtiuxbNSn.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/78Jp9l7N0gUQtiuxbNSn.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/78Jp9l7N0gUQtiuxbNSn.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/78Jp9l7N0gUQtiuxbNSn.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/78Jp9l7N0gUQtiuxbNSn.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/78Jp9l7N0gUQtiuxbNSn.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/78Jp9l7N0gUQtiuxbNSn.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/78Jp9l7N0gUQtiuxbNSn.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/78Jp9l7N0gUQtiuxbNSn.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;The &lt;code&gt;content-type&lt;/code&gt; response header shown in DevTools&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/use-thumbor/#next-steps&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Try out other &lt;a href=&quot;https://thumbor.readthedocs.io/en/latest/filters.html&quot; rel=&quot;noopener&quot;&gt;filters&lt;/a&gt; and transformations on the &lt;code&gt;hero.jpg&lt;/code&gt; image.&lt;/p&gt;
&lt;p&gt;If you&#39;re following along using your own Thumbor installation, check out the appendix below that explains how and why to use the &lt;code&gt;thumbor.conf&lt;/code&gt; file.&lt;/p&gt;
&lt;h2 id=&quot;appendix-thumborconf&quot;&gt;Appendix: &lt;code&gt;thumbor.conf&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-thumbor/#appendix-thumborconf&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Many of the configuration options discussed in this post, plus many others, can be established as defaults by setting up and using a &lt;code&gt;thumbor.conf&lt;/code&gt; configuration file. Settings in the &lt;code&gt;thumbor.conf&lt;/code&gt; file will be applied to all images unless overridden by the URL string parameters.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Run the &lt;code&gt;thumbor-config&lt;/code&gt; command to create a new &lt;code&gt;thumbor.conf&lt;/code&gt; file.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;thumbor-config &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; ./thumbor.conf&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open your new &lt;code&gt;thumbor.conf&lt;/code&gt; file. The &lt;code&gt;thumbor-config&lt;/code&gt; command generated a file that lists and explains all Thumbor configuration options.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Configure settings by uncommenting lines and changing the default values.
You may find it useful to set the following settings:&lt;/p&gt;
 &lt;!-- lint disable no-inline-padding --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;QUALITY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AUTO_WEBP&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MAX_WIDTH&lt;/code&gt; and &lt;code&gt;MAX_HEIGHT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ALLOW_ANIMATED_GIFS&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run Thumbor with the &lt;code&gt;--conf&lt;/code&gt; flag to use your &lt;code&gt;thumbor.conf&lt;/code&gt; settings.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;thumbor --conf /path/to/thumbor.conf&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Use image CDNs to optimize images</title>
    <link href="https://web.dev/image-cdns/"/>
    <updated>2019-08-14T00:00:00Z</updated>
    <id>https://web.dev/image-cdns/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;why-use-an-image-cdn&quot;&gt;Why use an image CDN? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#why-use-an-image-cdn&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Image content delivery networks (CDNs) are excellent at optimizing images. Switching to an image CDN can yield a &lt;a href=&quot;https://www.youtube.com/watch?v=YJGCZCaIZkQ&amp;amp;t=1010s&quot; rel=&quot;noopener&quot;&gt;40–80% savings&lt;/a&gt; in image file size. In theory, it&#39;s possible to achieve the same results using only build scripts, but it&#39;s rare in practice.&lt;/p&gt;
&lt;h2 id=&quot;whats-an-image-cdn&quot;&gt;What&#39;s an image CDN? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#whats-an-image-cdn&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Image CDNs specialize in the transformation, optimization, and delivery of images. You can also think of them as APIs for accessing and manipulating the images used on your site. For images loaded from an image CDN, an image URL indicates not only which image to load, but also parameters like size, format, and quality. This makes it easy to create variations of an image for different use cases.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Shows the request/response flow between the image CDN and the client. Parameters like size and format are used to request variations of the same image.&quot; decoding=&quot;async&quot; height=&quot;408&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/OIF2VcXp8P6O7tQvw53B.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Examples of transformations image CDNs can perform based on parameters in image URLs.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Image CDNs are different from build-time image optimization scripts in that they create new versions of images as they&#39;re needed. As a result, CDNs are generally better suited to creating images that are heavily customized for each individual client than build scripts are.&lt;/p&gt;
&lt;h2 id=&quot;how-image-cdns-use-urls-to-indicate-optimization-options&quot;&gt;How image CDNs use URLs to indicate optimization options &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#how-image-cdns-use-urls-to-indicate-optimization-options&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Image URLs used by image CDNs convey important information about an image and the transformations and optimizations that should be applied to it. URL formats will vary depending on image CDN, but at a high-level, they all have similar features. Let&#39;s walk through some of the most common features.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Image URLs typically consist of the following components: origin, image, security key, and transformations.&quot; decoding=&quot;async&quot; height=&quot;127&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/GA4udXeYUEjHSY4N0Qew.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;origin&quot;&gt;Origin &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#origin&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;An image CDN can live on your own domain or the domain of your image CDN. Third-party image CDNs typically offer the option of using a custom domain for a fee. Using your own domain makes it easier to switch image CDNs at a later date because no URL changes will be needed.&lt;/p&gt;
&lt;p&gt;The example above uses the image CDN&#39;s domain (&amp;quot;example-cdn.com&amp;quot;) with a personalized subdomain, rather than a custom domain.&lt;/p&gt;
&lt;h3 id=&quot;image&quot;&gt;Image &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#image&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Image CDNs can usually be configured to automatically retrieve images from their existing locations when they&#39;re needed. This capability is often achieved by including the complete URL of the &lt;em&gt;existing image&lt;/em&gt; within the URL for the image generated by the image CDN. For example, you might see a URL that looks like this: &lt;code&gt;https://my-site.example-cdn.com/https://flowers.com/daisy.jpg/quality=auto&lt;/code&gt;. This URL would retrieve and optimize the image that exists at &lt;code&gt;https://flowers.com/daisy.jpg&lt;/code&gt;.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; As the above example shows, the file exension requested (&lt;code&gt;.jpg&lt;/code&gt; is this case) may not be the same as the image file format returned (WebP in this example). The &lt;code&gt;content-type&lt;/code&gt; HTTP Header will inform the browser which format the URL is in so it can process it appropriately. However, this may cause some confusion if the file is saved to disk and used by another program that expects the file format to match the file extension. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Another widely supported way of uploading images to an image CDN is to send them via an HTTP POST request to the image CDN&#39;s API.&lt;/p&gt;
&lt;h3 id=&quot;security-key&quot;&gt;Security key &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#security-key&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A security key prevents other people from creating new versions of your images. If this feature is enabled, each new version of an image requires a unique security key. If someone tries to change the parameters of the image URL but doesn&#39;t provide a valid security key, they won&#39;t be able to create a new version. Your image CDN will take care of the details of generating and tracking security keys for you.&lt;/p&gt;
&lt;h3 id=&quot;transformations&quot;&gt;Transformations &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#transformations&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Image CDNs offer tens, and in some cases hundreds, of different image transformations. These transformations are specified via the URL string, and there are no restrictions on using multiple transformations at the same time. In the context of web performance, the most important image transformations are size, pixel density, format, and compression. These transformations are the reason why switching to an image CDN typically results in a significant reduction in image size.&lt;/p&gt;
&lt;p&gt;There tends to be an objectively best setting for performance transformations, so some image CDNs support an &amp;quot;auto&amp;quot; mode for these transformations. For example, instead of specifying that images be transformed to the WebP format, you could allow the CDN to automatically select and serve the optimal format. Signals that an image CDN can use to determine the best way to transform an image include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/blog/automating-resource-selection-with-client-hints/&quot; rel=&quot;noopener&quot;&gt;Client hints&lt;/a&gt; (for example, viewport width, DPR, and image width)&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Save-Data&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Save-Data&lt;/code&gt;&lt;/a&gt; header&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/User-Agent&quot; rel=&quot;noopener&quot;&gt;User-Agent&lt;/a&gt; request header&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Network_Information_API&quot; rel=&quot;noopener&quot;&gt;Network Information API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, the image CDN might serve AVIF to a Chrome browser, WebP to an Edge browser, and JPEG to a very old browser. Auto settings are popular because they allow you to take advantage of image CDNs&#39; significant expertise in optimizing images without the need for code changes to adopt new technologies once they&#39;re supported by the image CDN.&lt;/p&gt;
&lt;h2 id=&quot;types-of-image-cdns&quot;&gt;Types of Image CDNs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#types-of-image-cdns&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Image CDNs can be broken down into two categories: self-managed and third-party-managed.&lt;/p&gt;
&lt;h3 id=&quot;self-managed-image-cdns&quot;&gt;Self-managed image CDNs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#self-managed-image-cdns&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Self-managed CDNs can be a good choice for sites with engineering staff who are comfortable maintaining their own infrastructure.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/thumbor/thumbor&quot; rel=&quot;noopener&quot;&gt;Thumbor&lt;/a&gt; is the most popular self-managed image CDN. While it is open-source and free to use, it generally has fewer features than most commercial CDNs, and its documentation is somewhat limited. &lt;a href=&quot;https://wikitech.wikimedia.org/wiki/Thumbor&quot; rel=&quot;noopener&quot;&gt;Wikipedia&lt;/a&gt;, &lt;a href=&quot;https://medium.com/square-corner-blog/dynamic-images-with-thumbor-a430a1cfcd87&quot; rel=&quot;noopener&quot;&gt;Square&lt;/a&gt;, and &lt;a href=&quot;https://99designs.com/tech-blog/blog/2013/07/01/thumbnailing-with-thumbor/&quot; rel=&quot;noopener&quot;&gt;99designs&lt;/a&gt; are three sites that use Thumbor. See the &lt;a href=&quot;https://web.dev/install-thumbor&quot;&gt;How to install the Thumbor image CDN&lt;/a&gt; post for instructions on setting it up.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/h2non/imaginary&quot; rel=&quot;noopener&quot;&gt;Imaginary&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/cshum/imagor&quot; rel=&quot;noopener&quot;&gt;Imagor&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;third-party-image-cdns&quot;&gt;Third-party image CDNs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#third-party-image-cdns&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Third-party image CDNs provide image CDNs as a service. Just as cloud providers provide servers and other infrastructure for a fee; image CDNs provide image optimization and delivery for a fee. Because third-party image CDNs maintain the underlying technology, getting started is fairly simple and can usually be accomplished in 10-15 minutes, although a complete migration for a large site might take far longer. Third-party image CDNs are typically priced based on usage tiers, with most image CDNs providing either a free tier or a free trial to give you an opportunity to try out their product.&lt;/p&gt;
&lt;h2 id=&quot;choosing-an-image-cdn&quot;&gt;Choosing an image CDN &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#choosing-an-image-cdn&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are many good options for image CDNs. Some will have more features than others, but all of them will probably help you save bytes on your images and therefore load your pages faster. Besides feature sets, other factors to consider when choosing an image CDN are cost, support, documentation, and ease of setup or migration.&lt;/p&gt;
&lt;p&gt;Trying them out yourself before making a decision can also be helpful. Below you can find codelabs with instructions on how to quickly get started with several image CDNs.&lt;/p&gt;
&lt;h2 id=&quot;effects-on-largest-contentful-paint-lcp&quot;&gt;Effects on Largest Contentful Paint (LCP) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/image-cdns/#effects-on-largest-contentful-paint-lcp&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Images are a vital part of the user experience on many websites, and thus factor into how well sites do when it comes to &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint&lt;/a&gt;. Here are a few things to keep in mind if you decide to use an image CDN:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Images served from CDNs may come from a cross-origin server, which involves extra connection setup time. When possible, try to use an image CDN that proxies through the primary origin so that you&#39;re not adding extra origins for the browser to connect to. This has the same effect as self-hosting images on the primary origin.&lt;/li&gt;
&lt;li&gt;Consider using a &lt;a href=&quot;https://web.dev/fetch-priority/#summary&quot;&gt;&lt;code&gt;fetchpriority&lt;/code&gt; attribute value of &lt;code&gt;&amp;quot;high&amp;quot;&lt;/code&gt;&lt;/a&gt; on the LCP image element so that the browser can begin loading that image as soon as possible.&lt;/li&gt;
&lt;li&gt;If an image is not immediately discoverable in the initial HTML, consider using a &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/uses-rel-preload/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;rel=preload&lt;/code&gt;&lt;/a&gt; hint for your LCP candidate image so that the browser can load that image ahead of time.&lt;/li&gt;
&lt;li&gt;If proxying through your origin is not possible, and the exact image to be loaded will not be known until later during page load, &lt;a href=&quot;https://web.dev/preconnect-and-dns-prefetch/&quot;&gt;you should set up a connection to the cross-origin image CDN as early as possible&lt;/a&gt; to shorten the resource loading phase of what could be your page&#39;s LCP candidate image.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Image CDNs are indispensable tools that eliminate the toil of manually optimizing images, and should be considered. As always, though, there are trade-offs to consider, and keeping an eye on your website&#39;s LCP candidate images and adding hints as appropriate can mitigate any added latency to those key requests.&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author><author>
      <name>Jeremy Wagner</name>
    </author><author>
      <name>Barry Pollard</name>
    </author>
  </entry>
  
  <entry>
    <title>How to install the Thumbor image CDN</title>
    <link href="https://web.dev/install-thumbor/"/>
    <updated>2019-08-14T00:00:00Z</updated>
    <id>https://web.dev/install-thumbor/</id>
    <content type="html" mode="escaped">&lt;p&gt;Image CDNs make it easy to dynamically optimize the aesthetics and performance of your images. Unlike most image CDNs, &lt;a href=&quot;http://thumbor.org/&quot; rel=&quot;noopener&quot;&gt;Thumbor&lt;/a&gt; is open-source and can be used for free to resize, compress, and transform images. It&#39;s suitable for production use; &lt;a href=&quot;https://wikitech.wikimedia.org/wiki/Thumbor&quot; rel=&quot;noopener&quot;&gt;Wikipedia&lt;/a&gt; and &lt;a href=&quot;https://medium.com/square-corner-blog/dynamic-images-with-thumbor-a430a1cfcd87&quot; rel=&quot;noopener&quot;&gt;Square&lt;/a&gt; both use Thumbor.&lt;/p&gt;
&lt;p&gt;This guide explains how to install Thumbor on your own server. Once installed, you&#39;ll be able to use Thumbor as an API for transforming your images.&lt;/p&gt;
&lt;h2 id=&quot;intro&quot;&gt;Intro &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/install-thumbor/#intro&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You&#39;ll be installing Thumbor on a VM running Ubuntu 16.04. Ubuntu 16.04 is a very common image and these instructions are intended to work on any cloud provider. Creating a VM might sound like more work than installing Thumbor on your local machine, but the minutes that you take to create a VM will probably save you hours or days of frustration trying to get Thumbor to properly install on your local machine. Although easy to use, Thumbor is notoriously difficult to install but these instructions simplify the process. If dependencies download quickly, the installation can be completed in 5 to 10 minutes.&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/install-thumbor/#prerequisites&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This post assumes that you know how to create a Ubuntu 16.04 LTS VM on a cloud platform like &lt;a href=&quot;https://cloud.google.com/compute/docs/instances/create-start-instance&quot; rel=&quot;noopener&quot;&gt;Google Cloud&lt;/a&gt;, &lt;a href=&quot;https://aws.amazon.com/getting-started/tutorials/launch-a-virtual-machine/&quot; rel=&quot;noopener&quot;&gt;AWS,&lt;/a&gt; or &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/virtual-machines/linux/quick-create-portal?toc=%2Fazure%2Fvirtual-machines%2Flinux%2Ftoc.json&quot; rel=&quot;noopener&quot;&gt;Azure&lt;/a&gt; and how to use command line tools to set up the VM.&lt;/p&gt;
&lt;h2 id=&quot;install-thumbor-dependencies&quot;&gt;Install Thumbor Dependencies &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/install-thumbor/#install-thumbor-dependencies&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Update and upgrade Ubuntu&#39;s already-installed packages:&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;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; update -y &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; upgrade -y&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Install &lt;code&gt;pip&lt;/code&gt;, the package manager for Python. Later you&#39;ll install Thumbor with &lt;code&gt;pip&lt;/code&gt;.&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;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; -y python-pip&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Install Thumbor&#39;s dependencies. Thumbor&#39;s documentation does not explicitly mention these dependencies, but Thumbor will not install successfully without them.&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 comment&quot;&gt;# ssl packages&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; -y libcurl4-openssl-dev libssl-dev&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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 comment&quot;&gt;# computer vision packages&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; -y python-opencv libopencv-dev&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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 comment&quot;&gt;# image format packages&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; -y libjpeg-dev libpng-dev libwebp-dev webp&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;install-thumbor&quot;&gt;Install Thumbor &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/install-thumbor/#install-thumbor&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Install Thumbor using pip.&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;sudo&lt;/span&gt; pip &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; thumbor&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Note: Many Python developers use &lt;a href=&quot;https://pypi.org/project/virtualenv/&quot; rel=&quot;noopener&quot;&gt;virtualenv&lt;/a&gt; to manage their packages. For the sake of simplicity, these instructions do not use &lt;code&gt;virtualenv&lt;/code&gt;. If you are installing Thumbor in a standalone environment, &lt;code&gt;virtualenv&lt;/code&gt; is not necessary. If you choose to use &lt;code&gt;virtualenv&lt;/code&gt;, note that Thumbor requires Python 2.7 and will not work with newer versions of &lt;code&gt;pip&lt;/code&gt; (e.g., these instructions use &lt;code&gt;pip&lt;/code&gt; 8.1.1).&lt;/p&gt;
&lt;p&gt;If you&#39;ve successfully installed Thumbor, this should work:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;thumbor --help&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;run-thumbor&quot;&gt;Run Thumbor &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/install-thumbor/#run-thumbor&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run Thumbor. Debug logging is optional but can be helpful when you&#39;re getting started.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;thumbor --log-level debug&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Thumbor is now running.&lt;/p&gt;
&lt;h2 id=&quot;open-firewall-port&quot;&gt;Open Firewall Port &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/install-thumbor/#open-firewall-port&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By default, Thumbor runs on port 8888. If your VM&#39;s IP address is &lt;code&gt;12.123.12.122&lt;/code&gt;, then you would access Thumbor from the web browser at &lt;code&gt;http://12.123.12.123:8888/.../$IMAGE&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;However, this probably won&#39;t work for you (yet) because cloud providers usually require that you explicitly open firewall ports before they will accept incoming traffic.&lt;/p&gt;
&lt;p&gt;Update the firewall to expose port 8888. Here&#39;s more information on how to do this for: &lt;a href=&quot;https://cloud.google.com/vpc/docs/using-firewalls&quot; rel=&quot;noopener&quot;&gt;Google Cloud&lt;/a&gt;, &lt;a href=&quot;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/authorizing-access-to-an-instance.html&quot; rel=&quot;noopener&quot;&gt;AWS&lt;/a&gt;, and &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/virtual-machines/windows/nsg-quickstart-portal&quot; rel=&quot;noopener&quot;&gt;Azure&lt;/a&gt;. Note that for Google Cloud you need to first &lt;a href=&quot;https://cloud.google.com/compute/docs/ip-addresses/reserve-static-external-ip-address&quot; rel=&quot;noopener&quot;&gt;assign a static IP address to your VM&lt;/a&gt; and then &lt;a href=&quot;https://cloud.google.com/vpc/docs/special-configurations#externalhttpconnection&quot; rel=&quot;noopener&quot;&gt;allow an external HTTP connection&lt;/a&gt;.&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/install-thumbor/#try-it-out&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Thumbor is now accessible and ready for use. Try it out by visiting the following URL:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;http://YOUR_VIRTUAL_MACHINE:8888/unsafe/100x100/https://web.dev/install-thumbor/hero.jpg&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Note that this URL uses HTTP. Thumbor uses HTTP by default but can be &lt;a href=&quot;https://thumbor.readthedocs.io/en/latest/image_loader.html&quot; rel=&quot;noopener&quot;&gt;configured&lt;/a&gt; to use HTTPS.&lt;/p&gt;
&lt;p&gt;You should see an image that is 100 pixels wide by 100 pixels tall. Thumbor has taken the image &lt;code&gt;hero.jpg&lt;/code&gt; and size specified in the URL string and served the result. You can replace the image in the URL string (i.e., &lt;code&gt;https://web.dev/install-thumbor/hero.jpg&lt;/code&gt;) with any other image (e.g., &lt;code&gt;https://your-site.com/cat.jpg&lt;/code&gt;) and Thumbor will resize that image too.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://web.dev/use-thumbor/#thumbor-url-format&quot;&gt;Optimize images with Thumbor&lt;/a&gt; article has more information on using the Thumbor API. In particular, you may be interested in &lt;a href=&quot;https://web.dev/use-thumbor/#appendix-thumborconf&quot;&gt;setting up a Thumbor configuration file&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;appendix-configuring-systemd&quot;&gt;Appendix: Configuring Systemd &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/install-thumbor/#appendix-configuring-systemd&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This step explains how to make sure that the Thumbor process keeps running, even after the VM has been restarted. This step is important for production sites, but optional if you&#39;re just playing around with Thumbor.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.freedesktop.org/software/systemd/man/systemd.html&quot; rel=&quot;noopener&quot;&gt;Systemd&lt;/a&gt; is the &amp;quot;system and service manager&amp;quot; for Linux operating systems. &lt;code&gt;systemd&lt;/code&gt; makes it easy to configure when services (processes) run.&lt;/p&gt;
&lt;p&gt;You will be configuring &lt;code&gt;systemd&lt;/code&gt; to automatically start Thumbor on VM boot. If the VM is restarted, the Thumbor process will automatically restart as well. This is much more reliable than relying on user intervention to start Thumbor.&lt;/p&gt;
&lt;p&gt;Navigate to the &lt;code&gt;/lib/systemd/system&lt;/code&gt; directory. This directory contains the service files for &lt;code&gt;systemd&lt;/code&gt;.&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 builtin class-name&quot;&gt;cd&lt;/span&gt; /lib/systemd/system&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;As superuser, create a &lt;code&gt;thumbor.service&lt;/code&gt; file.&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;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; thumbor.service&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Using your favorite text editor (vim and nano come pre-installed on Ubuntu or you can install another editor), add the following configuration to &lt;code&gt;thumbor.service&lt;/code&gt;. This configuration will run &lt;code&gt;/usr/local/bin/thumbor&lt;/code&gt; (i.e. the Thumbor binary) once networking is available and will restart Thumbor on &lt;a href=&quot;https://www.freedesktop.org/software/systemd/man/systemd.service.html#Restart=&quot; rel=&quot;noopener&quot;&gt;failure&lt;/a&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[Unit]&lt;br /&gt;&lt;br /&gt;Description=Service for Thumbor image CDN&lt;br /&gt;&lt;br /&gt;Documentation=https://thumbor.readthedocs.io/en/latest/&lt;br /&gt;&lt;br /&gt;After=network.target&lt;br /&gt;&lt;br /&gt;[Service]&lt;br /&gt;&lt;br /&gt;ExecStart=/usr/local/bin/thumbor&lt;br /&gt;&lt;br /&gt;Restart=on-failure&lt;br /&gt;&lt;br /&gt;[Install]&lt;br /&gt;&lt;br /&gt;WantedBy=multi-user.target&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;systemctl&lt;/code&gt; is the utility used to manage &lt;code&gt;systemd&lt;/code&gt;. Use the &lt;code&gt;start&lt;/code&gt; command to start Thumbor.&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;sudo&lt;/span&gt; systemctl start thumbor.service&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Note: If Thumbor is currently running, you should stop it before attempting to start Thumbor using &lt;code&gt;systemctl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, &amp;quot;enable&amp;quot; Thumbor. This means that Thumbor will automatically start on boot.&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;sudo&lt;/span&gt; systemctl &lt;span class=&quot;token builtin class-name&quot;&gt;enable&lt;/span&gt; thumbor.service&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Verify that you&#39;ve successfully configured &lt;code&gt;systemd&lt;/code&gt; by running the &lt;code&gt;status&lt;/code&gt; command.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;systemctl status thumbor.service&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If you&#39;ve successfully set up thumbor.service to use &lt;code&gt;systemd&lt;/code&gt;, the &lt;a href=&quot;https://www.freedesktop.org/software/systemd/man/systemctl.html#status%20PATTERN%E2%80%A6%7CPID%E2%80%A6%5D&quot; rel=&quot;noopener&quot;&gt;status&lt;/a&gt; should show that it is enabled and active.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Systemctl displaying the status of Thumbor&quot; decoding=&quot;async&quot; height=&quot;164&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 466px) 466px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/e04pxe6uE090ewJ3WWPX.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/e04pxe6uE090ewJ3WWPX.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/e04pxe6uE090ewJ3WWPX.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/e04pxe6uE090ewJ3WWPX.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/e04pxe6uE090ewJ3WWPX.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/e04pxe6uE090ewJ3WWPX.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/e04pxe6uE090ewJ3WWPX.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/e04pxe6uE090ewJ3WWPX.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/e04pxe6uE090ewJ3WWPX.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/e04pxe6uE090ewJ3WWPX.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/e04pxe6uE090ewJ3WWPX.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/e04pxe6uE090ewJ3WWPX.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/e04pxe6uE090ewJ3WWPX.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/e04pxe6uE090ewJ3WWPX.jpg?auto=format&amp;w=932 932w&quot; width=&quot;466&quot; /&gt;
&lt;/figure&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Third-party contributors</title>
    <link href="https://web.dev/handbook/third-party-contributions/"/>
    <updated>2019-07-28T00:00:00Z</updated>
    <id>https://web.dev/handbook/third-party-contributions/</id>
    <content type="html" mode="escaped">&lt;p&gt;First, thanks for contributing to web.dev! We really appreciate your help in making this community better.&lt;/p&gt;
&lt;p&gt;If you&#39;re not a Googler, we ask that you provide a point of contact who can respond to GitHub issues related to your content.  Ideally, your point of contact will be able to respond within a week of when the issue is submitted. The web.dev team will tag the point of contact in relevant issues so that they can respond.&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Tooling and libraries</title>
    <link href="https://web.dev/handbook/tooling-and-libraries/"/>
    <updated>2019-07-21T00:00:00Z</updated>
    <id>https://web.dev/handbook/tooling-and-libraries/</id>
    <content type="html" mode="escaped">&lt;p&gt;Choosing tooling and libraries can be one of the most overwhelming and time consuming aspects of web development. To help readers feel confident in their choices, provide instructions for well-known, well-established tools rather than an exhaustive list of options. Strike a balance between what&#39;s most commonly used and what&#39;s best practice, in terms of compatibility, accessibility, and functionality.&lt;/p&gt;
&lt;p&gt;When appropriate, provide multiple paths, frameworks, or tools to achieve a goal. Including a codelab for each path is often a good way to achieve that goal. To see an example, check out the codelab callout at the end of the &lt;a href=&quot;https://web.dev/use-imagemin-to-compress-images/#imagemin-npm-module&quot;&gt;Use Imagemin to compress images&lt;/a&gt; post.&lt;/p&gt;
&lt;h2 id=&quot;use-open-source-software&quot;&gt;Use open-source software &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/handbook/tooling-and-libraries/#use-open-source-software&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This keeps web.dev accessible to as many developers as possible.&lt;/p&gt;
&lt;h2 id=&quot;ensure-tooling-is-well-established&quot;&gt;Ensure tooling is well-established &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/handbook/tooling-and-libraries/#ensure-tooling-is-well-established&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you&#39;re planning to include tooling in a post or codelab, make sure it meets at least one of these criteria:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://httparchive.org/&quot; rel=&quot;noopener&quot;&gt;HTTP Archive&lt;/a&gt; data:&lt;/strong&gt; Tooling is used by ≥ 10K domains.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.npmjs.com/&quot; rel=&quot;noopener&quot;&gt;npm&lt;/a&gt; weekly download statistics:&lt;/strong&gt; Tooling consistently has ≥ 50K weekly downloads.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Include a link to the relevant data source in your &lt;a href=&quot;https://web.dev/handbook/quick-start/#planning&quot;&gt;content proposal&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Sometimes it&#39;s impossible to use these data sources to verify the popularity of a given tool. (For example, if you&#39;re writing an article about servers, neither data source can be used to verify the usage of &lt;a href=&quot;https://www.nginx.com/&quot; rel=&quot;noopener&quot;&gt;NGINX&lt;/a&gt;.) In such cases you can demonstrate the popularity of a tool in your content proposal by listing 5–10 well-known sites that have self-identified as users.&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Use Lighthouse for performance budgets</title>
    <link href="https://web.dev/use-lighthouse-for-performance-budgets/"/>
    <updated>2019-06-14T00:00:00Z</updated>
    <id>https://web.dev/use-lighthouse-for-performance-budgets/</id>
    <content type="html" mode="escaped">&lt;p&gt;&lt;a href=&quot;https://github.com/GoogleChrome/lighthouse&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt; now supports performance budgets. This feature, known as LightWallet, can be set up in under five minutes and provides feedback on performance metrics and the size and quantity of page resources.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Important&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; While Lighthouse is an excellent tool for identifying performance improvement opportunities, it can&#39;t be stressed enough that Lighthouse is a tool that relies on &lt;a href=&quot;https://web.dev/lab-and-field-data-differences/#lab-data&quot;&gt;lab data&lt;/a&gt;. While lab data is an integral part of any performance improvement effort, comparing and contrasting that data with &lt;a href=&quot;https://web.dev/lab-and-field-data-differences/#field-data&quot;&gt;field data&lt;/a&gt; collected from your website&#39;s actual users is key. Doing so will help you understand how the changes you make to your website affect real users. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;install-lighthouse&quot;&gt;Install Lighthouse &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-lighthouse-for-performance-budgets/#install-lighthouse&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;LightWallet is available in the command line version of Lighthouse v5+.&lt;/p&gt;
&lt;p&gt;To get started, install Lighthouse:&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 lighthouse&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;create-a-budget&quot;&gt;Create a Budget &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-lighthouse-for-performance-budgets/#create-a-budget&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Create a file named &lt;code&gt;budget.json&lt;/code&gt;. In this file add the following JSON:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token 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;/*&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;timings&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;metric&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;interactive&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;budget&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;metric&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;first-meaningful-paint&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;budget&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&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;br /&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;resourceSizes&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;resourceType&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;script&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;budget&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;125&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;resourceType&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;total&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;budget&quot;&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;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;br /&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;resourceCounts&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;resourceType&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;third-party&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;budget&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This example &lt;code&gt;budget.json&lt;/code&gt; file sets five separate budgets:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A budget of 3000ms for Time to Interactive.&lt;/li&gt;
&lt;li&gt;A budget of 1000ms for First Meaningful Paint&lt;/li&gt;
&lt;li&gt;A budget of 125 KB for the total amount of JavaScript on the page.&lt;/li&gt;
&lt;li&gt;A budget of 300 KB for the overall size of the page.&lt;/li&gt;
&lt;li&gt;A budget of 10 requests for the number of requests made to third-party origins.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a complete list of supported performance metrics and resource types, refer to the &lt;a href=&quot;https://github.com/GoogleChrome/lighthouse/blob/master/docs/performance-budgets.md&quot; rel=&quot;noopener&quot;&gt;Performance Budgets&lt;/a&gt; section of the Lighthouse docs.&lt;/p&gt;
&lt;h2 id=&quot;run-lighthouse&quot;&gt;Run Lighthouse &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-lighthouse-for-performance-budgets/#run-lighthouse&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run Lighthouse using the &lt;code&gt;--budget-path&lt;/code&gt; flag. This flag tells Lighthouse the location of your budget file.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;lighthouse https://example.com --budget-path&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;./budget.json&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Note&lt;/strong&gt;: A budget file does not have to be named &lt;code&gt;budget.json&lt;/code&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;view-the-results&quot;&gt;View the Results &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-lighthouse-for-performance-budgets/#view-the-results&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If LightWallet has been configured correctly, the Lighthouse report will contain a &lt;strong&gt;Budgets&lt;/strong&gt; section within the &lt;strong&gt;Performance&lt;/strong&gt; category.&lt;/p&gt;
&lt;img alt=&quot;&amp;#x27;Budgets&amp;#x27; section of the Lighthouse report&quot; decoding=&quot;async&quot; height=&quot;289&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/FdUeI8rKZtJB3Ol624S3.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/FdUeI8rKZtJB3Ol624S3.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/FdUeI8rKZtJB3Ol624S3.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/FdUeI8rKZtJB3Ol624S3.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/FdUeI8rKZtJB3Ol624S3.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/FdUeI8rKZtJB3Ol624S3.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/FdUeI8rKZtJB3Ol624S3.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/FdUeI8rKZtJB3Ol624S3.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/FdUeI8rKZtJB3Ol624S3.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/FdUeI8rKZtJB3Ol624S3.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/FdUeI8rKZtJB3Ol624S3.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/FdUeI8rKZtJB3Ol624S3.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/FdUeI8rKZtJB3Ol624S3.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/FdUeI8rKZtJB3Ol624S3.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/FdUeI8rKZtJB3Ol624S3.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/FdUeI8rKZtJB3Ol624S3.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/FdUeI8rKZtJB3Ol624S3.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/FdUeI8rKZtJB3Ol624S3.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;In the JSON version of the Lighthouse report, Lightwallet results can be found within the audit findings for the &lt;code&gt;performance-budget&lt;/code&gt; audit.&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Speed at scale: what&#39;s new in web performance?</title>
    <link href="https://web.dev/speed-at-scale/"/>
    <updated>2019-05-24T00:00:00Z</updated>
    <id>https://web.dev/speed-at-scale/</id>
    <content type="html" mode="escaped">&lt;p&gt;During the &lt;a href=&quot;https://www.youtube.com/watch?v=YJGCZCaIZkQ&amp;amp;feature=youtu.be&quot; rel=&quot;noopener&quot;&gt;&amp;quot;Speed at Scale&amp;quot;
talk&lt;/a&gt; at Google
I/O 2019, we announced three things that we hope will improve web performance
over the coming year.&lt;/p&gt;
&lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;YJGCZCaIZkQ&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
&lt;h2 id=&quot;lighthouse-now-supports-performance-budgeting&quot;&gt;Lighthouse now supports Performance Budgeting &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-at-scale/#lighthouse-now-supports-performance-budgeting&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/use-lighthouse-for-performance-budgets/&quot;&gt;LightWallet&lt;/a&gt;
is a new feature in Lighthouse that adds support for &lt;a href=&quot;https://web.dev/fast#set-performance-budgets&quot;&gt;performance
budgets&lt;/a&gt;. Performance budgets establish
standards for the performance of your site. More importantly, they make it is
easy to identify and fix performance regressions before they ship.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A LightWallet report showing which assets are over the file size budget.&quot; decoding=&quot;async&quot; height=&quot;607&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/b1hw1bMU0dCIqS45XdfW.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/b1hw1bMU0dCIqS45XdfW.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/b1hw1bMU0dCIqS45XdfW.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/b1hw1bMU0dCIqS45XdfW.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/b1hw1bMU0dCIqS45XdfW.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/b1hw1bMU0dCIqS45XdfW.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/b1hw1bMU0dCIqS45XdfW.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/b1hw1bMU0dCIqS45XdfW.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/b1hw1bMU0dCIqS45XdfW.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/b1hw1bMU0dCIqS45XdfW.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/b1hw1bMU0dCIqS45XdfW.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/b1hw1bMU0dCIqS45XdfW.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/b1hw1bMU0dCIqS45XdfW.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/b1hw1bMU0dCIqS45XdfW.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/b1hw1bMU0dCIqS45XdfW.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/b1hw1bMU0dCIqS45XdfW.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/b1hw1bMU0dCIqS45XdfW.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/b1hw1bMU0dCIqS45XdfW.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;LightWallet is available in the newest version of the Lighthouse CLI and only
takes a couple minutes to set up. These&lt;a href=&quot;https://web.dev/use-lighthouse-for-performance-budgets/&quot;&gt;
instructions&lt;/a&gt;
provide more information.&lt;/p&gt;
&lt;p&gt;Unsure what your budgets should be? Try our experimental &lt;a href=&quot;https://bit.ly/perf-budget-calculator&quot; rel=&quot;noopener&quot;&gt;Performance Budget
Calculator&lt;/a&gt; which can generate a
LightWallet compatible budget configuration.&lt;/p&gt;
&lt;h2 id=&quot;browser-level-image-and-iframe-lazy-loading-comes-to-the-web&quot;&gt;Browser-level image and iframe lazy loading comes to the web &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-at-scale/#browser-level-image-and-iframe-lazy-loading-comes-to-the-web&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Web pages often contain a large number of images, which contribute to
data-usage, &lt;a href=&quot;https://httparchive.org/reports/state-of-images&quot; rel=&quot;noopener&quot;&gt;page-bloat&lt;/a&gt; and
slower page loads. Many of these images are offscreen, requiring a user to
scroll in order to view them.&lt;/p&gt;
&lt;p&gt;Until now, you&#39;ve needed to solve lazy loading images using a JavaScript
library but that may soon change. This summer, Chrome will be launching support
for the &lt;a href=&quot;https://addyosmani.com/blog/lazy-loading/&quot; rel=&quot;noopener&quot;&gt;loading&lt;/a&gt; attribute which
brings standardized &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; lazy loading to the web.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Browser-level lazy-loading highlighting offscreen content being loaded on-demand&quot; decoding=&quot;async&quot; height=&quot;450&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/5W51sHaRUB0NEuN0MaFh.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/5W51sHaRUB0NEuN0MaFh.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/5W51sHaRUB0NEuN0MaFh.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/5W51sHaRUB0NEuN0MaFh.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/5W51sHaRUB0NEuN0MaFh.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/5W51sHaRUB0NEuN0MaFh.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/5W51sHaRUB0NEuN0MaFh.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/5W51sHaRUB0NEuN0MaFh.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/5W51sHaRUB0NEuN0MaFh.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/5W51sHaRUB0NEuN0MaFh.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/5W51sHaRUB0NEuN0MaFh.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/5W51sHaRUB0NEuN0MaFh.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/5W51sHaRUB0NEuN0MaFh.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/5W51sHaRUB0NEuN0MaFh.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/5W51sHaRUB0NEuN0MaFh.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/5W51sHaRUB0NEuN0MaFh.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/5W51sHaRUB0NEuN0MaFh.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/5W51sHaRUB0NEuN0MaFh.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;The &lt;code&gt;loading&lt;/code&gt; attribute allows a browser to defer loading offscreen images and
iframes until users scroll near them. &lt;code&gt;loading&lt;/code&gt; supports three values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;lazy&lt;/code&gt;: is a good candidate for lazy loading.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;eager&lt;/code&gt;: is not a good candidate for lazy loading. Load right away.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;auto&lt;/code&gt;: browser will determine whether or not to lazily load.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;io2019.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;...&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token 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;iframe&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;video-player.html&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;loading&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazy&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;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;The exact heuristics for &amp;quot;when the user scrolls near&amp;quot; is left up to the
browser. In general, our hope is that browsers will start fetching deferred
images and iframe content a little before it comes into the viewport.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;loading&lt;/code&gt; attribute is implemented behind flags in Chrome Canary. You can
try out &lt;a href=&quot;https://mathiasbynens.be/demo/img-loading-lazy&quot; rel=&quot;noopener&quot;&gt;this demo&lt;/a&gt; in Chrome
75+ with the &lt;code&gt;about://flags/#enable-lazy-image-loading&lt;/code&gt; and
&lt;code&gt;about://flags/#enable-lazy-frame-loading&lt;/code&gt; flags turned on.&lt;/p&gt;
&lt;p&gt;A &lt;a href=&quot;https://addyosmani.com/blog/lazy-loading/&quot; rel=&quot;noopener&quot;&gt;write-up&lt;/a&gt; on the
lazy loading feature is available with more details.&lt;/p&gt;
&lt;h2 id=&quot;google-fonts-now-supports-font-display-as-a-query-parameter&quot;&gt;Google Fonts now supports font-display as a query parameter &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-at-scale/#google-fonts-now-supports-font-display-as-a-query-parameter&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We announced support for &lt;a href=&quot;https://font-display.glitch.me/&quot; rel=&quot;noopener&quot;&gt;font-display&lt;/a&gt; is now available in production for all &lt;a href=&quot;https://fonts.google.com/&quot; rel=&quot;noopener&quot;&gt;Google Fonts&lt;/a&gt; via the &lt;a href=&quot;https://developers.google.com/fonts/docs/getting_started#use_font-display&quot; rel=&quot;noopener&quot;&gt;display query-string parameter&lt;/a&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;https://fonts.googleapis.com/css?family=Lobster&amp;amp;display=swap&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The &lt;code&gt;font-display&lt;/code&gt; descriptor lets you decide how your web fonts will render or
fallback, depending on how long it takes for them to load. It supports a number
of values including &lt;code&gt;auto&lt;/code&gt;, &lt;code&gt;block&lt;/code&gt;, &lt;code&gt;swap&lt;/code&gt;, &lt;code&gt;fallback&lt;/code&gt; and &lt;code&gt;optional&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Previously, the only way to specify &lt;code&gt;font-display&lt;/code&gt; for web fonts from Google Fonts was to self-host them but this change removes the need to do so.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://developers.google.com/fonts/docs/getting_started#use_font-display&quot; rel=&quot;noopener&quot;&gt;Google Fonts
documentation&lt;/a&gt;
has been updated to include &lt;code&gt;font-display&lt;/code&gt; in the default code embeds (as seem
below). We hope this will encourage more developers to try out this exciting
addition.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Google Fonts embed code with font-display included in the URL as a query-parameter&quot; decoding=&quot;async&quot; height=&quot;528&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/aJqdPp1xobaYRDNx4aJd.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/aJqdPp1xobaYRDNx4aJd.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/aJqdPp1xobaYRDNx4aJd.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/aJqdPp1xobaYRDNx4aJd.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/aJqdPp1xobaYRDNx4aJd.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/aJqdPp1xobaYRDNx4aJd.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/aJqdPp1xobaYRDNx4aJd.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/aJqdPp1xobaYRDNx4aJd.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/aJqdPp1xobaYRDNx4aJd.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/aJqdPp1xobaYRDNx4aJd.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/aJqdPp1xobaYRDNx4aJd.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/aJqdPp1xobaYRDNx4aJd.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/aJqdPp1xobaYRDNx4aJd.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/aJqdPp1xobaYRDNx4aJd.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/aJqdPp1xobaYRDNx4aJd.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/aJqdPp1xobaYRDNx4aJd.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/aJqdPp1xobaYRDNx4aJd.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/aJqdPp1xobaYRDNx4aJd.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Here&#39;s a &lt;a href=&quot;https://glitch.com/~truth-bookcase&quot; rel=&quot;noopener&quot;&gt;demo&lt;/a&gt; on Glitch of using display
with multiple font families. Try it out with &lt;a href=&quot;https://developer.chrome.com/docs/devtools/network/#throttle&quot; rel=&quot;noopener&quot;&gt;DevTools network
emulation&lt;/a&gt;
to see the impact of &lt;code&gt;font-display: swap&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;watch-for-more&quot;&gt;Watch for more &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/speed-at-scale/#watch-for-more&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Our talk also covered several production case studies of using advanced
performance patterns to improve user-experience. These included sites
leveraging image CDNs, &lt;a href=&quot;https://web.dev/fast/reduce-network-payloads-using-text-compression/codelab-text-compression-brotli&quot;&gt;Brotli
compression&lt;/a&gt;,
smart JavaScript serving and prefetching to speed up their pages. &lt;a href=&quot;https://www.youtube.com/watch?v=YJGCZCaIZkQ&amp;amp;feature=youtu.be&quot; rel=&quot;noopener&quot;&gt;Watch the
talk&lt;/a&gt; to learn
more :)&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author><author>
      <name>Addy Osmani</name>
    </author>
  </entry>
  
  <entry>
    <title>Serve responsive images</title>
    <link href="https://web.dev/serve-responsive-images/"/>
    <updated>2018-11-05T00:00:00Z</updated>
    <id>https://web.dev/serve-responsive-images/</id>
    <content type="html" mode="escaped">&lt;p&gt;Serving desktop-sized images to mobile devices can use 2–4x more data than
needed. Instead of a &amp;quot;one-size-fits-all&amp;quot; approach to images, serve different
image sizes to different devices.&lt;/p&gt;
&lt;h2 id=&quot;responsive-images-and-core-web-vitals&quot;&gt;Responsive images and Core Web Vitals &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-responsive-images/#responsive-images-and-core-web-vitals&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you serve responsive images, you&#39;re evaluating the display capabilities of
the user&#39;s device and choosing one of a set of image candidates that are optimal
for display based on those criteria. The result—as stated previously—is
rather than delivering too much image data to devices that won&#39;t benefit from it,
you&#39;re serving an appropriately sized image for the device. For smaller devices such
as phones and tablets, this equates to reduced data usage as compared to devices
with larger screens, such as laptops.&lt;/p&gt;
&lt;p&gt;The effects of faster image loading can also extend to your page&#39;s &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt;.
For example, if your page&#39;s &lt;a href=&quot;https://web.dev/lcp/#what-elements-are-considered&quot;&gt;LCP element&lt;/a&gt; is an image,
you&#39;re reducing that LCP candidate&#39;s &lt;a href=&quot;https://web.dev/optimize-lcp/#3-reduce-resource-load-time&quot;&gt;resource load time&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Lower resource load times will lower the load time for an LCP image, which will improve
the page&#39;s LCP score. A lower LCP means users will perceive that your site is loading faster,
particularly the largest piece of content visible in the viewport during page load. Serving
responsive images can also reduce bandwidth contention for other resources on the page,
which can improve how fast your page loads in general.&lt;/p&gt;
&lt;h2 id=&quot;resize-images&quot;&gt;Resize images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-responsive-images/#resize-images&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Two of the most popular image resizing tools are the &lt;a href=&quot;https://www.npmjs.com/package/sharp&quot; rel=&quot;noopener&quot;&gt;sharp npm
package&lt;/a&gt; and the &lt;a href=&quot;https://www.imagemagick.org/script/index.php&quot; rel=&quot;noopener&quot;&gt;ImageMagick CLI
tool&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The sharp package is a good choice for automating image resizing (for example,
generating multiple sizes of thumbnails for all the videos on your website). It
is fast and easily integrated with build scripts and tools. On the other hand,
ImageMagick is convenient for one-off image resizing because it is used entirely
from the command line.&lt;/p&gt;
&lt;h3 id=&quot;sharp&quot;&gt;sharp &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-responsive-images/#sharp&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To use sharp as a Node script, save this code as a separate script in your project,
and then run it to convert your images:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sharp &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;sharp&#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; fs &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;fs&#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; directory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./images&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readdirSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;directory&lt;span class=&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;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;sharp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;directory&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;file&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// width, height&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;directory&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;file&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-small.jpg&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;imagemagick&quot;&gt;ImageMagick &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-responsive-images/#imagemagick&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To resize an image to 33% of its original size, run the following command in
your terminal:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;convert -resize &lt;span class=&quot;token number&quot;&gt;33&lt;/span&gt;% flower.jpg flower-small.jpg&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;To resize an image to fit within 300px wide by 200px high, 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 comment&quot;&gt;# macOS/Linux&lt;/span&gt;&lt;br /&gt;convert flower.jpg -resize 300x200 flower-small.jpg&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Windows&lt;/span&gt;&lt;br /&gt;magick convert flower.jpg -resize 300x200 flower-small.jpg&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;how-many-image-versions-should-you-create&quot;&gt;How many image versions should you create? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-responsive-images/#how-many-image-versions-should-you-create&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There is no single &amp;quot;correct&amp;quot; answer to this question. However, it&#39;s common to
serve 3-5 different sizes of an image. Serving more image sizes is better for
performance, but will take up more space on your servers and require writing a
tiny bit more HTML.&lt;/p&gt;
&lt;h3 id=&quot;other-options&quot;&gt;Other options &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-responsive-images/#other-options&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Image services like &lt;a href=&quot;https://github.com/thumbor/thumbor&quot; rel=&quot;noopener&quot;&gt;Thumbor&lt;/a&gt; (open-source)
and &lt;a href=&quot;https://cloudinary.com/&quot; rel=&quot;noopener&quot;&gt;Cloudinary&lt;/a&gt; are also worth checking out. Image
services provide responsive images (and image manipulation) on-demand. Thumbor
is setup by installing it on a server; Cloudinary takes care of these details
for you and requires no server setup. Both are easy ways to create responsive
images.&lt;/p&gt;
&lt;h2 id=&quot;serve-multiple-image-versions&quot;&gt;Serve multiple image versions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-responsive-images/#serve-multiple-image-versions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Specify multiple image versions and the browser will choose the best one to
use:&lt;/p&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;&lt;strong&gt;Before&lt;/strong&gt;&lt;/th&gt;
        &lt;th&gt;&lt;strong&gt;After&lt;/strong&gt;&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;
          &amp;lt;img src=&quot;flower-large.jpg&quot;&amp;gt;
        &lt;/td&gt;
        &lt;td&gt;
          &amp;lt;img src=&quot;flower-large.jpg&quot; srcset=&quot;flower-small.jpg 480w,
          flower-large.jpg 1080w&quot; sizes=&quot;50vw&quot;&amp;gt;
        &lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag&#39;s
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/img#attr-src&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;src&lt;/code&gt;&lt;/a&gt;,
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/img#attr-srcset&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;srcset&lt;/code&gt;&lt;/a&gt;,
and
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/img#attr-sizes&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;sizes&lt;/code&gt;&lt;/a&gt;
attributes all interact to achieve this end result.&lt;/p&gt;
&lt;h3 id=&quot;the-src-attribute&quot;&gt;The &amp;quot;src&amp;quot; attribute &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-responsive-images/#the-src-attribute&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The src attribute makes this code work for browsers that don&#39;t
&lt;a href=&quot;https://caniuse.com/#search=srcset&quot; rel=&quot;noopener&quot;&gt;support&lt;/a&gt; the &lt;code&gt;srcset&lt;/code&gt; and &lt;code&gt;sizes&lt;/code&gt;
attributes. If a browser does not support these attributes, it will fall back to
loading the resource specified by the &lt;code&gt;src&lt;/code&gt; attribute.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Gotchas&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; The resource specified by the &lt;code&gt;src&lt;/code&gt; attribute should be large enough to work well on all device sizes. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;the-srcset-attribute&quot;&gt;The &amp;quot;srcset&amp;quot; attribute &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-responsive-images/#the-srcset-attribute&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;srcset&lt;/code&gt; attribute is a comma-separated list of image filenames and their
width or density descriptors.&lt;/p&gt;
&lt;p&gt;This example uses
&lt;a href=&quot;https://www.w3.org/TR/html5/semantics-embedded-content.html#width-descriptor&quot; rel=&quot;noopener&quot;&gt;width descriptors&lt;/a&gt;.
&lt;code&gt;480w&lt;/code&gt; is a width descriptor tells the browser that &lt;code&gt;flower-small.jpg&lt;/code&gt; is
480px wide; &lt;code&gt;1080w&lt;/code&gt; is a width descriptor tells the browser that
&lt;code&gt;flower-large.jpg&lt;/code&gt; is 1080px wide.&lt;/p&gt;
&lt;p&gt;&amp;quot;Width descriptor&amp;quot; sounds fancy but is just a way to tell the browser the width
of an image. This saves the browser from needing to download the image to
determine its size.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Gotchas&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Use the &lt;code&gt;w&lt;/code&gt; unit (instead of &lt;code&gt;px&lt;/code&gt;) to write width descriptors. For example, a 1024px wide image would be written as &lt;code&gt;1024w&lt;/code&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;strong&gt;Extra Credit:&lt;/strong&gt;
You don&#39;t need to know about density descriptors to serve different image sizes.
However, if you&#39;re curious about how density descriptors work, check out the
&lt;a href=&quot;https://web.dev/codelab-density-descriptors&quot;&gt;Resolution Switching code lab&lt;/a&gt;.
Density descriptors are used to serve different images based on the device&#39;s
&lt;a href=&quot;https://en.wikipedia.org/wiki/Pixel_density&quot; rel=&quot;noopener&quot;&gt;pixel density&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;the-sizes-attribute&quot;&gt;The &amp;quot;sizes&amp;quot; attribute &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-responsive-images/#the-sizes-attribute&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The sizes attribute tells the browser how wide the image will be when it is
displayed. However, the sizes attribute has no effect on display size; you
still need CSS for that.&lt;/p&gt;
&lt;p&gt;The browser uses this information, along with what it knows about the user&#39;s
device (i.e. its dimensions and pixel density), to determine which image to
load.&lt;/p&gt;
&lt;p&gt;If a browser does not recognize the &amp;quot;&lt;code&gt;sizes&lt;/code&gt;&amp;quot; attribute, it will fallback to
loading the image specified by the &amp;quot;&lt;code&gt;src&lt;/code&gt;&amp;quot; attribute. (Browsers shipped support
for the &amp;quot;&lt;code&gt;sizes&lt;/code&gt;&amp;quot; and &amp;quot;&lt;code&gt;srcset&lt;/code&gt;&amp;quot; attributes at the same time, so a browser will
either support both attributes or neither.)&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Gotchas&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Slot width can be specified using a variety of units. The following are all valid sizes:  - &lt;code&gt;100px&lt;/code&gt; - &lt;code&gt;33vw&lt;/code&gt; - &lt;code&gt;20em&lt;/code&gt; - &lt;code&gt;calc(50vw-10px)&lt;/code&gt;  The following is not a valid size:  +  &lt;code&gt;25%&lt;/code&gt; (percentages cannot be used with the sizes attribute) &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;strong&gt;Extra Credit:&lt;/strong&gt;
If you want to be fancy, you can also use the sizes attribute to specify
multiple slot sizes. This accommodates websites that use different layouts for
different viewport sizes. Check out this &lt;a href=&quot;https://web.dev/codelab-specifying-multiple-slot-widths&quot;&gt;multiple slot code sample&lt;/a&gt;
to learn how to do this.&lt;/p&gt;
&lt;h3 id=&quot;even-more-extra-credit&quot;&gt;(Even more) Extra Credit &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-responsive-images/#even-more-extra-credit&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In addition to all the extra credit already listed (images are complex!), you
can also use these same concepts for
&lt;a href=&quot;https://developer.mozilla.org/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images#Art_direction&quot; rel=&quot;noopener&quot;&gt;art direction&lt;/a&gt;.
Art direction is the practice of serving completely different looking images
(rather than different versions of the same image) to different viewports. You
can learn more in the &lt;a href=&quot;https://web.dev/codelab-art-direction&quot;&gt;Art Direction code lab&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;verify&quot;&gt;Verify &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-responsive-images/#verify&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once you&#39;ve implemented responsive images, you can use Lighthouse to make sure
that you didn&#39;t miss any images. Run the Lighthouse Performance Audit
(&lt;strong&gt;Lighthouse &amp;gt; Options &amp;gt; Performance&lt;/strong&gt;) and look for the results of the
&lt;strong&gt;Properly size images&lt;/strong&gt; audit. These results will list the images that need to
be resized.&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Serve images with correct dimensions</title>
    <link href="https://web.dev/serve-images-with-correct-dimensions/"/>
    <updated>2018-11-05T00:00:00Z</updated>
    <id>https://web.dev/serve-images-with-correct-dimensions/</id>
    <content type="html" mode="escaped">&lt;p&gt;We&#39;ve all been there: you forgot to scale down an image before adding it to the
page. The image looks fine, but it is wasting users&#39; data and hurting page
performance.&lt;/p&gt;
&lt;h2 id=&quot;identify-incorrectly-sized-images&quot;&gt;Identify incorrectly sized images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-images-with-correct-dimensions/#identify-incorrectly-sized-images&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Lighthouse makes it easy to identify incorrectly-sized images. Run the
Performance Audit (&lt;strong&gt;Lighthouse &amp;gt; Options &amp;gt; Performance&lt;/strong&gt;) and look for the
results of the &lt;strong&gt;Properly size images&lt;/strong&gt; audit. The audit lists any images that
need to be resized.&lt;/p&gt;
&lt;h2 id=&quot;determine-the-correct-image-size&quot;&gt;Determine the correct image size &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-images-with-correct-dimensions/#determine-the-correct-image-size&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Image sizing can be deceptively complicated. For this reason, we&#39;ve provided two
approaches: the &amp;quot;good&amp;quot; and the &amp;quot;better.&amp;quot; Both will improve performance, but the
&amp;quot;better&amp;quot; approach may take a bit longer to understand and implement. However, it
will also reward you with bigger performance improvements. The best choice for
you is the one that you feel comfortable implementing.&lt;/p&gt;
&lt;h3 id=&quot;a-quick-note-on-css-units&quot;&gt;A quick note on CSS units &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-images-with-correct-dimensions/#a-quick-note-on-css-units&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are two types of CSS units for specifying the size of HTML elements,
including images:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Absolute units: Elements styled using absolute units will always be
displayed at the same size, regardless of device. Examples of valid,
absolute CSS units: px, cm, mm, in.&lt;/li&gt;
&lt;li&gt;Relative units: Elements styled using relative units will be displayed at
varying sizes, depending on the relative length specified. Examples of
valid, relative CSS units: %, vw (1vw = 1% of the width of the viewport),
em (1.5 em = 1.5 times font size).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-good-approach&quot;&gt;The &amp;quot;Good&amp;quot; Approach &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-images-with-correct-dimensions/#the-good-approach&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For images with sizing based on…&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Relative units&lt;/strong&gt;: Resize the image to a size that will work across all devices.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You may find it helpful to check your analytics data (e.g. Google
Analytics) to see which display sizes are commonly used by your users.
Alternatively, &lt;a href=&quot;http://screensiz.es/&quot; rel=&quot;noopener&quot;&gt;screensiz.es&lt;/a&gt;
provides information about the displays of many common devices.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Absolute units&lt;/strong&gt;: Resize the image to match the size that it is displayed at.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The DevTools Elements panel can be used to determine what size an image is
displayed at.&lt;/p&gt;
&lt;img alt=&quot;DevTools element&amp;#x27;s panel&quot; decoding=&quot;async&quot; height=&quot;364&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/pKQa0Huu0KGInOekdz6M.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/pKQa0Huu0KGInOekdz6M.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/pKQa0Huu0KGInOekdz6M.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/pKQa0Huu0KGInOekdz6M.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/pKQa0Huu0KGInOekdz6M.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/pKQa0Huu0KGInOekdz6M.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/pKQa0Huu0KGInOekdz6M.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/pKQa0Huu0KGInOekdz6M.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/pKQa0Huu0KGInOekdz6M.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/pKQa0Huu0KGInOekdz6M.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/pKQa0Huu0KGInOekdz6M.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/pKQa0Huu0KGInOekdz6M.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/pKQa0Huu0KGInOekdz6M.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/pKQa0Huu0KGInOekdz6M.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/pKQa0Huu0KGInOekdz6M.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/pKQa0Huu0KGInOekdz6M.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/pKQa0Huu0KGInOekdz6M.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/pKQa0Huu0KGInOekdz6M.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h3 id=&quot;the-better-approach&quot;&gt;The &amp;quot;Better&amp;quot; approach &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-images-with-correct-dimensions/#the-better-approach&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For images with both absolute and relative sizing, use &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/source#attr-srcset&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;srcset&lt;/code&gt;&lt;/a&gt;
and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/source#attr-sizes&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;sizes&lt;/code&gt;&lt;/a&gt;
attributes to serve different images to different display densities.
Read the &lt;a href=&quot;https://web.dev/serve-responsive-images&quot;&gt;guide on responsive images&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&amp;quot;Display density&amp;quot; refers to the fact that different displays have different
densities of pixels. All other things being equal, a high pixel density
display will look sharper than a low pixel density display.&lt;/p&gt;
&lt;p&gt;As a result, multiple image versions are necessary if you want users to
experience the crispest possible images, regardless of the pixel density of
their device.&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; Some sites find that this difference in image quality matters, some find that it does not. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Responsive image techniques make this possible by allowing you to list
multiple image versions and for the device to choose the image that works
best for it.&lt;/p&gt;
&lt;p&gt;An image that works across all devices will be unnecessarily large for
smaller devices. Responsive image techniques, specifically &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/source#attr-srcset&quot; rel=&quot;noopener&quot;&gt;srcset&lt;/a&gt;
and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/source#attr-sizes&quot; rel=&quot;noopener&quot;&gt;sizes&lt;/a&gt;, allow you to specify multiple image versions and for the device to choose
the size that works best for it.&lt;/p&gt;
&lt;h2 id=&quot;resize-images&quot;&gt;Resize images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-images-with-correct-dimensions/#resize-images&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Regardless of the approach that you choose, you may find it helpful to use
ImageMagick to resize your images.
&lt;a href=&quot;https://www.imagemagick.org/script/index.php&quot; rel=&quot;noopener&quot;&gt;ImageMagick&lt;/a&gt; is the most popular
command line tool for creating and editing images. Most people can resize images
far more quickly when using the CLI than a GUI-based image editor.&lt;/p&gt;
&lt;p&gt;Resize image to 25% the size of the original:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;convert flower.jpg -resize &lt;span class=&quot;token number&quot;&gt;25&lt;/span&gt;% flower_small.jpg&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Scale image to fit within &amp;quot;200px wide by 100px tall&amp;quot;:&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 comment&quot;&gt;# macOS/Linux&lt;/span&gt;&lt;br /&gt;convert flower.jpg -resize 200x100 flower_small.jpg&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Windows&lt;/span&gt;&lt;br /&gt;magick convert flower.jpg -resize 200x100 flower_small.jpg&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If you&#39;ll be resizing many images, you may find it more convenient to use a
script or service to automate the process. You can learn more about this in the
Responsive Images guide.&lt;/p&gt;
&lt;h2 id=&quot;avoid-layout-shifts-by-specifying-dimensions&quot;&gt;Avoid layout shifts by specifying dimensions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-images-with-correct-dimensions/#avoid-layout-shifts-by-specifying-dimensions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While this guide discusses image dimensions in the context of reducing the amount of unnecessary bytes downloaded, it&#39;s important to note that reserving the correct space for images in the layout is another crucial component of minimizing a page&#39;s &lt;a href=&quot;https://web.dev/cls/&quot;&gt;Cumulative Layout Shift&lt;/a&gt; metric. When serving images in HTML, be sure to use proper &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes so that the browser knows how much space to allocate in the layout for the image:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;flower.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;640&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;480&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;A picture of a siberian iris.&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;Without these attributes, or the equivalent CSS sizing, the browser has no idea how much space the image will take up until it has loaded. This will cause layout shifts in the document, which can be frustrating to users when content moves after they have started consuming it. This can result in users losing their place when reading, or to &amp;quot;miss&amp;quot; their intended hit target and end up clicking on something else they didn&#39;t intend to during page load.&lt;/p&gt;
&lt;p&gt;An alternative to providing the width and height explicitly, is to use the  CSS &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/aspect-ratio&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;aspect-ratio&lt;/code&gt; property&lt;/a&gt; on the image. This has a similar effect on an element&#39;s size that &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes do in the sense that the container will maintain a consistent aspect ratio. However, the difference is that this may result in a different aspect-ratio being used than the image is provided in, so you will likely want to use an &lt;code&gt;object-fit&lt;/code&gt; setting to ensure the image is not distorted in this explicit 16/9 view:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;aspect-ratio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 16 / 9&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;object-fit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cover&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;verify&quot;&gt;Verify &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-images-with-correct-dimensions/#verify&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once you&#39;ve resized all your images, re-run Lighthouse to verify that you didn&#39;t
miss anything.&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Use WebP images</title>
    <link href="https://web.dev/serve-images-webp/"/>
    <updated>2018-11-05T00:00:00Z</updated>
    <id>https://web.dev/serve-images-webp/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;why-should-you-care&quot;&gt;Why should you care? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-images-webp/#why-should-you-care&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;WebP images are smaller than their JPEG and PNG counterparts—usually on the
magnitude of a 25–35% reduction in filesize. This decreases page sizes and
improves performance.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;YouTube found that switching to WebP thumbnails resulted in &lt;a href=&quot;https://www.youtube.com/watch?v=rqXMwLbYEE4&quot; rel=&quot;noopener&quot;&gt;10%
faster page loads&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Facebook
&lt;a href=&quot;https://code.fb.com/android/improving-facebook-on-android/&quot; rel=&quot;noopener&quot;&gt;experienced&lt;/a&gt; a
25-35% filesize savings for JPEGs and an 80% filesize savings for PNGs when
they switched to using WebP.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;WebP is an excellent replacement for JPEG, PNG, and GIF images. In addition,
WebP offers both lossless and lossy compression. In lossless compression no data
is lost. Lossy compression reduces file size, but at the expense of possibly
reducing image quality.&lt;/p&gt;
&lt;h2 id=&quot;convert-images-to-webp&quot;&gt;Convert images to WebP &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-images-webp/#convert-images-to-webp&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;People generally use one of the following approaches for converting their images
to WebP: the
&lt;a href=&quot;https://developers.google.com/speed/webp/docs/using&quot; rel=&quot;noopener&quot;&gt;cwebp command-line tool&lt;/a&gt;
or the &lt;a href=&quot;https://github.com/imagemin/imagemin-webp&quot; rel=&quot;noopener&quot;&gt;Imagemin WebP plugin&lt;/a&gt; (npm
package).
The Imagemin WebP plugin is generally the best choice if your project uses build
scripts or build tools (e.g. Webpack or Gulp), whereas the CLI is a good choice
for simple projects or if you&#39;ll only need to convert images once.&lt;/p&gt;
&lt;p&gt;When you convert images to WebP, you have the option to set a wide variety of
compression settings—but for most people the only thing you&#39;ll ever need to
care about is the quality setting. You can specify a quality level from 0
(worst) to 100 (best). It&#39;s worth playing around with this, find
which level is the right tradeoff between image quality and filesize for your
needs.&lt;/p&gt;
&lt;h3 id=&quot;use-cwebp&quot;&gt;Use cwebp &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-images-webp/#use-cwebp&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Convert a single file, using cwebp&#39;s default compression settings:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;cwebp images/flower.jpg -o images/flower.webp&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Convert a single file, using a quality level of &lt;code&gt;50&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;cwebp -q &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt; images/flower.jpg -o images/flower.webp&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Convert all files in a directory:&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 keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token for-or-select variable&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; images/*&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; cwebp &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$file&lt;/span&gt;&quot;&lt;/span&gt; -o &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${file&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;.*}&lt;/span&gt;.webp&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;done&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;use-imagemin&quot;&gt;Use Imagemin &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-images-webp/#use-imagemin&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Imagemin WebP plugin can be used by itself or with your favorite build tool
(Webpack/Gulp/Grunt/etc.). This usually involves adding ~10 lines of code to a
build script or the configuration file for your build tool.
Here are examples of how to do that for
&lt;a href=&quot;https://glitch.com/~webp-webpack&quot; rel=&quot;noopener&quot;&gt;Webpack&lt;/a&gt;,
&lt;a href=&quot;https://glitch.com/~webp-gulp&quot; rel=&quot;noopener&quot;&gt;Gulp&lt;/a&gt;, and
&lt;a href=&quot;https://glitch.com/~webp-grunt&quot; rel=&quot;noopener&quot;&gt;Grunt&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you are not using one of those build tools, you can use Imagemin by itself as
a Node script. This script will convert the files in the &lt;code&gt;images&lt;/code&gt; directory and
save them in the &lt;code&gt;compressed_images&lt;/code&gt; directory.&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; imagemin &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;imagemin&#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; imageminWebp &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;imagemin-webp&#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 function&quot;&gt;imagemin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;images/*&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;compressed_images&#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;plugins&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 function&quot;&gt;imageminWebp&lt;/span&gt;&lt;span class=&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;quality&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token 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;Done!&#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 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;serve-webp-images&quot;&gt;Serve WebP images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-images-webp/#serve-webp-images&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If your site only supports WebP compatible
&lt;a href=&quot;https://caniuse.com/#search=webp&quot; rel=&quot;noopener&quot;&gt;browsers&lt;/a&gt;, you can stop reading. Otherwise,
serve WebP to newer browsers and a fallback image to older browsers:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&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;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;flower.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;After:&lt;/strong&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;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image/webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;flower.webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image/jpeg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;flower.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;flower.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/picture&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt;&lt;/a&gt;,
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/source&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt;&lt;/a&gt;,
and &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags, including how they are ordered relative to each other, all
interact to achieve this end result.&lt;/p&gt;
&lt;h3 id=&quot;lesspicturegreater&quot;&gt;&lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-images-webp/#lesspicturegreater&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; tag provides a wrapper for zero or more &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; tags and one &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;
&lt;h3 id=&quot;lesssourcegreater&quot;&gt;&lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-images-webp/#lesssourcegreater&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; tag specifies a media resource.&lt;/p&gt;
&lt;p&gt;The browser uses the first listed source that&#39;s in a format it supports. If the browser does not support any of the formats listed in the &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; tags, it falls back to loading the image specified by the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Gotchas&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt;  - The &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; tag for the &amp;quot;preferred&amp;quot; image format (in this case that is WebP) should be listed first, before other &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; tags.  - The value of the &lt;code&gt;type&lt;/code&gt; attribute should be the MIME type corresponding to the image format. An image&#39;s &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Complete_list_of_MIME_types&quot;&gt;MIME type&lt;/a&gt; and its file extension are often similar, but they aren&#39;t necessarily the same thing (e.g. &lt;code&gt;.jpg&lt;/code&gt; vs. &lt;code&gt;image/jpeg&lt;/code&gt;).  &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;lessimggreater&quot;&gt;&lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-images-webp/#lessimggreater&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag is what makes this code work on browsers
that don&#39;t support the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; tag.
If a browser does not support the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; tag, it will
ignore the tags it doesn&#39;t support. Thus, it only &amp;quot;sees&amp;quot; the
&lt;code&gt;&amp;lt;img src=&amp;quot;flower.jpg&amp;quot; alt=&amp;quot;&amp;quot;&amp;gt;&lt;/code&gt; tag and loads that image.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Gotchas&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;ul&gt; &lt;li&gt;The &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag should always be included, and it should always be listed last, after all &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; tags.&lt;/li&gt; &lt;li&gt;The resource specified by the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag should be in a universally supported format (e.g. JPEG), so it can be used as a fallback.&lt;/li&gt; &lt;/ul&gt; &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;reading-the-http-accept-header&quot;&gt;Reading the HTTP &lt;code&gt;Accept&lt;/code&gt; header &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-images-webp/#reading-the-http-accept-header&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you have an application back end or web server that allows you to rewrite requests, you can read the value of the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Accept&quot; rel=&quot;noopener&quot;&gt;HTTP &lt;code&gt;Accept&lt;/code&gt; header&lt;/a&gt;, which will advertise what alternative image formats are supported:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Accept&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;image/webp,image/svg+xml,image/*,*/*;q=0.8&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Reading this request header and rewriting the response based on its contents has the benefit of simplifying your image markup. &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; markup can get rather long with many sources. Below is an Apache &lt;code&gt;mod_rewrite&lt;/code&gt; rule that can serve WebP alternates:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-apacheconf&quot;&gt;&lt;code class=&quot;language-apacheconf&quot;&gt;&lt;span class=&quot;token directive-inline property&quot;&gt;RewriteEngine&lt;/span&gt; On&lt;br /&gt;&lt;span class=&quot;token directive-inline property&quot;&gt;RewriteCond&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;%{HTTP:Accept}&lt;/span&gt; image/webp &lt;span class=&quot;token directive-flags keyword&quot;&gt;[NC]&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token directive-inline property&quot;&gt;RewriteCond&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;%{HTTP:Content-Disposition}&lt;/span&gt; !attachment &lt;span class=&quot;token directive-flags keyword&quot;&gt;[NC]&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token directive-inline property&quot;&gt;RewriteCond&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;%{DOCUMENT_ROOT}&lt;/span&gt;/&lt;span class=&quot;token variable&quot;&gt;$1.webp&lt;/span&gt; -f &lt;span class=&quot;token directive-flags keyword&quot;&gt;[NC]&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token directive-inline property&quot;&gt;RewriteRule&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt; (.+)\.(png|jpe?g)$&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$1.webp&lt;/span&gt; [T=image/webp,L]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If you go this route, you&#39;ll need to set the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Vary&quot; rel=&quot;noopener&quot;&gt;HTTP &lt;code&gt;Vary&lt;/code&gt; response header&lt;/a&gt; to ensure caches will understand that the image may be served with varying content types:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-apacheconf&quot;&gt;&lt;code class=&quot;language-apacheconf&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;FilesMatch&lt;/span&gt;&lt;span class=&quot;token directive-block-parameter attr-value&quot;&gt; &lt;span class=&quot;token string&quot;&gt;&quot;.(jpe?g|png)$&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 directive-block tag&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;IfModule&lt;/span&gt;&lt;span class=&quot;token directive-block-parameter attr-value&quot;&gt; mod_headers.c&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 directive-inline property&quot;&gt;Header&lt;/span&gt; set Vary &lt;span class=&quot;token string&quot;&gt;&quot;Content-Type&quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;IfModule&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 directive-block tag&quot;&gt;&lt;span class=&quot;token directive-block tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;FilesMatch&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 rewrite rule above will look for a WebP version of any requested JPEG or PNG image. If a WebP alternate is found, it will be served with the proper &lt;code&gt;Content-Type&lt;/code&gt;  header. This will allow you to use image markup similar to the following with automatic WebP support:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;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;flower-320w.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;flower-320w.jpg 320w, flower-640w.jpg 640w, flower-960w.jpg 960w&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;verify-webp-usage&quot;&gt;Verify WebP usage &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/serve-images-webp/#verify-webp-usage&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Lighthouse can be used to verify that all images on your site are being served
using WebP. Run the Lighthouse Performance Audit (&lt;strong&gt;Lighthouse &amp;gt; Options &amp;gt;
Performance&lt;/strong&gt;) and look for the results of the &lt;strong&gt;Serve images in next-gen
formats&lt;/strong&gt; audit. Lighthouse will list any images that are not being served in
WebP.&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Use Imagemin to compress images</title>
    <link href="https://web.dev/use-imagemin-to-compress-images/"/>
    <updated>2018-11-05T00:00:00Z</updated>
    <id>https://web.dev/use-imagemin-to-compress-images/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;why-should-you-care&quot;&gt;Why should you care? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-imagemin-to-compress-images/#why-should-you-care&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Uncompressed images bloat your pages with unnecessary bytes. Because images can be &lt;a href=&quot;https://web.dev/lcp/#what-elements-are-considered&quot;&gt;candidates for Largest Contentful Paint (LCP)&lt;/a&gt;, those unnecessary bytes can add unnecessary &lt;a href=&quot;https://web.dev/optimize-lcp/#3-reduce-resource-load-time&quot;&gt;resource load time&lt;/a&gt;, which can result in longer LCP times.&lt;/p&gt;
&lt;p&gt;The photo on the right is 40% smaller than the one on the left, yet would probably look identical to the average user.&lt;/p&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;
          &lt;p&gt;&lt;img alt=&quot;&quot; decoding=&quot;async&quot; height=&quot;250&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 376px) 376px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/LRE2JJAuShXTjQF5ZSaR.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/LRE2JJAuShXTjQF5ZSaR.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/LRE2JJAuShXTjQF5ZSaR.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/LRE2JJAuShXTjQF5ZSaR.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/LRE2JJAuShXTjQF5ZSaR.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/LRE2JJAuShXTjQF5ZSaR.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/LRE2JJAuShXTjQF5ZSaR.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/LRE2JJAuShXTjQF5ZSaR.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/LRE2JJAuShXTjQF5ZSaR.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/LRE2JJAuShXTjQF5ZSaR.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/LRE2JJAuShXTjQF5ZSaR.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/LRE2JJAuShXTjQF5ZSaR.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/LRE2JJAuShXTjQF5ZSaR.jpg?auto=format&amp;w=752 752w&quot; width=&quot;376&quot; /&gt;&lt;/p&gt;
          20 KB
        &lt;/th&gt;
        &lt;th&gt;
          &lt;p&gt;&lt;img alt=&quot;&quot; decoding=&quot;async&quot; height=&quot;250&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 376px) 376px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/u9hncwN4TsT7zw2ObU10.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/u9hncwN4TsT7zw2ObU10.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/u9hncwN4TsT7zw2ObU10.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/u9hncwN4TsT7zw2ObU10.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/u9hncwN4TsT7zw2ObU10.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/u9hncwN4TsT7zw2ObU10.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/u9hncwN4TsT7zw2ObU10.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/u9hncwN4TsT7zw2ObU10.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/u9hncwN4TsT7zw2ObU10.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/u9hncwN4TsT7zw2ObU10.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/u9hncwN4TsT7zw2ObU10.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/u9hncwN4TsT7zw2ObU10.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/u9hncwN4TsT7zw2ObU10.jpg?auto=format&amp;w=752 752w&quot; width=&quot;376&quot; /&gt;&lt;/p&gt;
          12 KB
        &lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;&lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;h2 id=&quot;measure&quot;&gt;Measure &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-imagemin-to-compress-images/#measure&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run Lighthouse to check for opportunities to improve page load by compressing images.
These opportunities are listed under &amp;quot;Efficiently encode images&amp;quot;:&lt;/p&gt;
&lt;img alt=&quot;image&quot; decoding=&quot;async&quot; height=&quot;552&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/LnIukPEZHuVJwBtuJ7mc.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/LnIukPEZHuVJwBtuJ7mc.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/LnIukPEZHuVJwBtuJ7mc.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/LnIukPEZHuVJwBtuJ7mc.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/LnIukPEZHuVJwBtuJ7mc.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/LnIukPEZHuVJwBtuJ7mc.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/LnIukPEZHuVJwBtuJ7mc.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/LnIukPEZHuVJwBtuJ7mc.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/LnIukPEZHuVJwBtuJ7mc.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/LnIukPEZHuVJwBtuJ7mc.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/LnIukPEZHuVJwBtuJ7mc.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/LnIukPEZHuVJwBtuJ7mc.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/LnIukPEZHuVJwBtuJ7mc.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/LnIukPEZHuVJwBtuJ7mc.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/LnIukPEZHuVJwBtuJ7mc.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/LnIukPEZHuVJwBtuJ7mc.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/LnIukPEZHuVJwBtuJ7mc.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/LnIukPEZHuVJwBtuJ7mc.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Lighthouse currently reports on opportunities to compress images in JPEG format only. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;imagemin&quot;&gt;Imagemin &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-imagemin-to-compress-images/#imagemin&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Imagemin is an excellent choice for image compression because it supports a wide
variety of image formats and is easily integrated with build scripts and build
tools. Imagemin is available as both a
&lt;a href=&quot;https://github.com/imagemin/imagemin-cli&quot; rel=&quot;noopener&quot;&gt;CLI&lt;/a&gt; and an &lt;a href=&quot;https://www.npmjs.com/package/imagemin&quot; rel=&quot;noopener&quot;&gt;npm
module&lt;/a&gt;. Generally, the npm module is
the best choice because it offers more configuration options, but the CLI can be
a decent alternative if you want to try Imagemin without touching any code.&lt;/p&gt;
&lt;h3 id=&quot;plugins&quot;&gt;Plugins &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-imagemin-to-compress-images/#plugins&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Imagemin is built around &amp;quot;plugins.&amp;quot; A plugin is an npm package that compresses a
particular image format (e.g. &amp;quot;mozjpeg&amp;quot; compresses JPEGs). Popular image formats
may have multiple plugins to pick from.&lt;/p&gt;
&lt;p&gt;The most important thing to consider when choosing a plugin is whether it is
&amp;quot;lossy&amp;quot; or &amp;quot;lossless.&amp;quot; In lossless compression, no data is lost. Lossy
compression reduces file size, but at the expense of possibly reducing image
quality. If a plugin doesn&#39;t mention whether it is &amp;quot;lossy&amp;quot; or &amp;quot;lossless,&amp;quot; you
can tell by its API: if you can specify the image quality of the output, then it
is &amp;quot;lossy.&amp;quot;&lt;/p&gt;
&lt;p&gt;For most people, lossy plugins are the best choice. They offer significantly
greater filesize savings, and you can customize the compression levels to meet
your needs. The table below lists popular Imagemin plugins. These aren&#39;t the only plugins
available, but they&#39;d all be good choices for your project.&lt;/p&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Image Format&lt;/th&gt;
        &lt;th&gt;Lossy Plugin(s)&lt;/th&gt;
        &lt;th&gt;Lossless Plugin(s)&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;JPEG&lt;/td&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://www.npmjs.com/package/imagemin-mozjpeg&quot;&gt;imagemin-mozjpeg&lt;/a&gt;
        &lt;/td&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://www.npmjs.com/package/imagemin-jpegtran&quot;&gt;imagemin-jpegtran&lt;/a&gt;
        &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;PNG&lt;/td&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://www.npmjs.com/package/imagemin-pngquant&quot;&gt;imagemin-pngquant&lt;/a&gt;
        &lt;/td&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://www.npmjs.com/package/imagemin-optipng&quot;&gt;imagemin-optipng&lt;/a&gt;
        &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;GIF&lt;/td&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://www.npmjs.com/package/imagemin-giflossy&quot;&gt;imagemin-giflossy&lt;/a&gt;
        &lt;/td&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://www.npmjs.com/package/imagemin-gifsicle&quot;&gt;imagemin-gifsicle&lt;/a&gt;
        &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;SVG&lt;/td&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://www.npmjs.com/package/imagemin-svgo&quot;&gt;imagemin-svgo&lt;/a&gt;
        &lt;/td&gt;
        &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;WebP&lt;/td&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://www.npmjs.com/package/imagemin-webp&quot;&gt;imagemin-webp&lt;/a&gt;
        &lt;/td&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://www.npmjs.com/package/imagemin-webp&quot;&gt;imagemin-webp&lt;/a&gt;
        &lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;h3 id=&quot;imagemin-cli&quot;&gt;Imagemin CLI &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-imagemin-to-compress-images/#imagemin-cli&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Imagemin CLI works with 5 different plugins: imagemin-gifsicle,
imagemin-jpegtran, imagemin-optipng, imagemin-pngquant, and imagemin-svgo.
Imagemin uses the appropriate plugin based on the image format of the
input.&lt;/p&gt;
&lt;p&gt;To compress the images in the &amp;quot;images/&amp;quot; directory and save them to the same
directory, run the following command (overwrites the original files):&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ imagemin images/* --out-dir&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;images&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;imagemin-npm-module&quot;&gt;Imagemin npm module &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/use-imagemin-to-compress-images/#imagemin-npm-module&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you use one of these build tools,
checkout the codelabs for Imagemin with
&lt;a href=&quot;https://web.dev/codelab-imagemin-webpack&quot;&gt;webpack&lt;/a&gt;, &lt;a href=&quot;https://web.dev/codelab-imagemin-gulp&quot;&gt;gulp&lt;/a&gt;,
or &lt;a href=&quot;https://web.dev/codelab-imagemin-grunt&quot;&gt;grunt&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can also use Imagemin by itself as a Node script.
This code uses the &amp;quot;imagemin-mozjpeg&amp;quot; plugin to compress JPEG files to a quality
of 50 (&#39;0&#39; being the worst; &#39;100&#39; being the best):&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; imagemin &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;imagemin&#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; imageminMozjpeg &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;imagemin-mozjpeg&#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 punctuation&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 keyword&quot;&gt;const&lt;/span&gt; files &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;imagemin&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;&#39;source_dir/*.jpg&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;another_dir/*.jpg&#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 literal-property property&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;destination_dir&#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;plugins&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 function&quot;&gt;imageminMozjpeg&lt;/span&gt;&lt;span class=&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;quality&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&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;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;files&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token 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;</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Avoid invisible text during font loading</title>
    <link href="https://web.dev/avoid-invisible-text/"/>
    <updated>2018-11-05T00:00:00Z</updated>
    <id>https://web.dev/avoid-invisible-text/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;why-should-you-care&quot;&gt;Why should you care? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/avoid-invisible-text/#why-should-you-care&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Fonts are often large files that take awhile to load. To deal with this, some
browsers hide text until the font loads (the &amp;quot;flash of invisible text&amp;quot;). If
you&#39;re optimizing for performance, you&#39;ll want to avoid the &amp;quot;flash of invisible
text&amp;quot; and show content to users immediately using a system font (the &amp;quot;flash of
unstyled text&amp;quot;).&lt;/p&gt;
&lt;h2 id=&quot;display-text-immediately&quot;&gt;Display text immediately &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/avoid-invisible-text/#display-text-immediately&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This guide outlines two ways to achieve this: the first approach is very simple
but does not have universal browser
&lt;a href=&quot;https://caniuse.com/#search=font-display&quot; rel=&quot;noopener&quot;&gt;support&lt;/a&gt;; the second approach is more
work but has full browser support. The best choice for you is the one that
you&#39;ll actually implement and maintain.&lt;/p&gt;
&lt;h2 id=&quot;option-#1-use-font-display&quot;&gt;Option #1: Use font-display &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/avoid-invisible-text/#option-#1-use-font-display&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Before&lt;/th&gt;
        &lt;th&gt;After&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;
&lt;code&gt;@font-face {
  font-family: Helvetica;
}
&lt;/code&gt;
        &lt;/td&gt;
        &lt;td&gt;
&lt;code&gt;@font-face {
  font-family: Helvetica;
  &lt;strong&gt;font-display: swap;&lt;/strong&gt;
}
&lt;/code&gt;
        &lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@font-face/font-display&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;font-display&lt;/code&gt;&lt;/a&gt;
is an API for specifying font display strategy. &lt;code&gt;swap&lt;/code&gt; tells the browser that
text using this font should be displayed immediately using a system font. Once
the custom font is ready, the system font is swapped out.&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 60, 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;
      60
    &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 58, 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;
      58
    &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 11.1, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      11.1
    &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/CSS/@font-face/font-display#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;If a browser does not support &lt;code&gt;font-display&lt;/code&gt;, the browser continues to follow
its default behavior for loading fonts.&lt;/p&gt;
&lt;p&gt;These are the default font-loading behaviors for common browsers:&lt;/p&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;&lt;strong&gt;Browser&lt;/strong&gt;&lt;/th&gt;
        &lt;th&gt;&lt;strong&gt;Default behavior if font is not ready…&lt;/strong&gt;&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;Edge&lt;/td&gt;
        &lt;td&gt;Uses a system font until font is ready. Swaps out font.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Chrome&lt;/td&gt;
        &lt;td&gt;
          Will hide text for up to 3 seconds. If text is still not ready, uses a
          system font until font is ready. Swaps out font.
        &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Firefox&lt;/td&gt;
        &lt;td&gt;
          Will hide text for up to 3 seconds. If text is still not ready, uses a
          system font until font is ready. Swaps out font.
        &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Safari&lt;/td&gt;
        &lt;td&gt;Hides text until font is ready.&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;h2 id=&quot;option-#2-wait-to-use-custom-fonts-until-they-are-loaded&quot;&gt;Option #2: Wait to use custom fonts until they are loaded &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/avoid-invisible-text/#option-#2-wait-to-use-custom-fonts-until-they-are-loaded&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With a bit more work, the same behavior can be implemented to work across all
browsers.&lt;/p&gt;
&lt;p&gt;There are three parts to this approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Don&#39;t use a custom font on initial page load. This ensures that the
browser displays text immediately using a system font.&lt;/li&gt;
&lt;li&gt;Detect when your custom font is loaded. This can be accomplished with a
couple lines of JavaScript code, thanks to the &lt;a href=&quot;https://github.com/bramstein/fontfaceobserver&quot; rel=&quot;noopener&quot;&gt;FontFaceObserver&lt;/a&gt; library.&lt;/li&gt;
&lt;li&gt;Update page styling to use the custom font.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here are the changes you can expect to make to implement this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Refactor your CSS to not use a custom font on initial page load.&lt;/li&gt;
&lt;li&gt;Add a script to your page. This script detects when the custom font is
loaded and then will update the page styling.&lt;/li&gt;
&lt;/ul&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-avoid-invisible-text&quot;&gt;Use Font Face Observer to display text immediately&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;verify&quot;&gt;Verify &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/avoid-invisible-text/#verify&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run Lighthouse to verify the site is using &lt;code&gt;font-display: swap&lt;/code&gt; to display
text:&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;Make sure the &lt;strong&gt;Performance&lt;/strong&gt; checkbox is selected in the &lt;em&gt;Categories&lt;/em&gt; list.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Generate report&lt;/strong&gt; button.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Confirm that the &lt;strong&gt;Ensure text remains visible during webfont load&lt;/strong&gt; audit is passing.&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Art direction</title>
    <link href="https://web.dev/codelab-art-direction/"/>
    <updated>2018-11-05T00:00:00Z</updated>
    <id>https://web.dev/codelab-art-direction/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;try-out-this-demo&quot;&gt;Try out this demo &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-art-direction/#try-out-this-demo&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;li&gt;Reload the app using different browser sizes. Notice how different the images
are: they&#39;re not only different sizes but also different croppings and
aspect ratios.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;whats-going-on-here&quot;&gt;What&#39;s going on here? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-art-direction/#whats-going-on-here&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images#Art_direction&quot; rel=&quot;noopener&quot;&gt;Art direction&lt;/a&gt;
shows different images on different display sizes.&lt;/p&gt;
&lt;p&gt;A responsive image loads different sizes of the same image. Art direction takes
this a step further and loads completely different images depending on the
display.&lt;/p&gt;
&lt;p&gt;Use art direction to improve visual presentation. For example, the different
image croppings in this demo ensure that the point of interest (the flower) is
always shown in detail, regardless of the display. Art direction&#39;s benefits are
purely aesthetic; it provides no performance benefit over responsive images.&lt;/p&gt;
&lt;h2 id=&quot;view-the-code&quot;&gt;View the code &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-art-direction/#view-the-code&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;View &lt;code&gt;index.html&lt;/code&gt; to see the art direction code for this demo.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-the-code-works&quot;&gt;How the code works &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-art-direction/#how-the-code-works&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Art direction uses the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/picture&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt;&lt;/a&gt;,
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/source&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt;&lt;/a&gt;,
and &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;picture&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; tag provides a wrapper for zero or more &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; tags and one &lt;code&gt;&amp;lt;image&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;source&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; tag specifies a media resource.&lt;/p&gt;
&lt;p&gt;The browser uses the first &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; tag with a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/Media_Queries/Using_media_queries&quot; rel=&quot;noopener&quot;&gt;media query&lt;/a&gt;
that returns true. If none of the media queries match, the
browser falls back to loading the image specified by the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;.
tag.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Gotchas&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; - Be careful when ordering source tags. The browser uses the first &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; tag with a matching media query, even if subsequent &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; tags also have matching media queries. - The value of the &lt;code&gt;srcset&lt;/code&gt; attribute is an image filepath. - Use images that are appropriately sized. Just because art direction is used for aesthetic purposes, doesn&#39;t mean that it shouldn&#39;t be performant too. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;strong&gt;img&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag makes this code work on browsers that don&#39;t
support the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;
&lt;p&gt;If a browser does not support the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; tag, it loads the
image specified by the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Gotchas&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; - The &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag should always be included, and it should always be listed last, after all &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; tags. - The resource specified by the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag should be a size that works on all devices, so it can be used as a fallback. &lt;/div&gt;&lt;/aside&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Avoid flash of invisible text</title>
    <link href="https://web.dev/codelab-avoid-invisible-text/"/>
    <updated>2018-11-05T00:00:00Z</updated>
    <id>https://web.dev/codelab-avoid-invisible-text/</id>
    <content type="html" mode="escaped">&lt;p&gt;This code lab shows you how to display text immediately using &lt;a href=&quot;https://github.com/bramstein/fontfaceobserver&quot; rel=&quot;noopener&quot;&gt;Font Face
Observer&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;add-font-face-observer&quot;&gt;Add Font Face Observer &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-avoid-invisible-text/#add-font-face-observer&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/bramstein/fontfaceobserver&quot; rel=&quot;noopener&quot;&gt;Font Face Observer&lt;/a&gt; is a script
that detects when a font loads. The
&lt;a href=&quot;https://github.com/bramstein/fontfaceobserver/blob/master/fontfaceobserver.js&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;fontfaceobserver.js&lt;/code&gt;&lt;/a&gt;
file has already been saved to the project directory, so you don&#39;t need to add it
separately. However, you do need to add a script tag for it.&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;li&gt;Add a script tag for &lt;code&gt;fontfaceobserver.js&lt;/code&gt; to &lt;code&gt;index.html&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&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;Some text.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&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;fontfaceobserver.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;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;text/javascript&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;use-font-face-observer&quot;&gt;Use Font Face Observer &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-avoid-invisible-text/#use-font-face-observer&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;create-observers&quot;&gt;Create Observers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-avoid-invisible-text/#create-observers&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Create an observer for each font family that it is used on the page.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add the following script after the &lt;code&gt;fontfaceobserver.js&lt;/code&gt; script. This creates
observers for the &amp;quot;Pacifico&amp;quot; and &amp;quot;Roboto&amp;quot; font families:&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&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;fontfaceobserver.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;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;text/javascript&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/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;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;text/javascript&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; pacificoObserver &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;FontFaceObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Pacifico&#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;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; robotoObserver &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;FontFaceObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Roboto&#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;/mark&gt;&lt;br /&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If you&#39;re ever unsure what font face observers you need to create, just look for
the &lt;code&gt;font-family&lt;/code&gt; declarations in your CSS. Pass the &lt;code&gt;font-family&lt;/code&gt; name of these declarations to
&lt;code&gt;FontFaceObserver()&lt;/code&gt;. There is no need to create a font observer for
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/font-family#%3Cgeneric-name%3E&quot; rel=&quot;noopener&quot;&gt;fallback fonts&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For example, if your CSS was:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Times New Roman&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Times&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;you would add &lt;code&gt;FontFaceObserver(&#39;Times New Roman&#39;)&lt;/code&gt;. Times and serif are
fallback fonts, so you would not need to declare FontFaceObservers for them.&lt;/p&gt;
&lt;h3 id=&quot;detect-font-load&quot;&gt;Detect font load &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-avoid-invisible-text/#detect-font-load&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The code for detecting a font load looks like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;robotoObserver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  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;Hooray! Font loaded.&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 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;robotoObserver.load()&lt;/code&gt; is a promise that resolves when the font loads.&lt;/p&gt;
&lt;p&gt;The demo site uses two different fonts, so you need to use &lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise/all&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Promise.all()&lt;/code&gt;&lt;/a&gt;
to wait until both fonts have loaded.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add this promise to your script, right below the FontFaceObservers that you
just declared:&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;Promise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;  pacificoObserver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  robotoObserver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;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;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;/* Do things */&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;h4 id=&quot;✔️check-in&quot;&gt;✔️Check-in &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-avoid-invisible-text/#%E2%9C%94%EF%B8%8Fcheck-in&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Your script should now look like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token 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;text/javascript&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; pacificoObserver &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;FontFaceObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Pacifico&#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; robotoObserver &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;FontFaceObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Roboto&#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;Promise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;  pacificoObserver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  robotoObserver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;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;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;/* Do things */&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;apply-fonts-loaded-class&quot;&gt;Apply &lt;code&gt;fonts-loaded&lt;/code&gt; class &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-avoid-invisible-text/#apply-fonts-loaded-class&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Replace the &lt;code&gt;/* Do things */&lt;/code&gt; comment in the script with this line:&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;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;documentElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;fonts-loaded&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This adds the &lt;code&gt;fonts-loaded&lt;/code&gt; class to the document&#39;s root element (the
&lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; tag) once both fonts have loaded.&lt;/p&gt;
&lt;h4 id=&quot;✔️check-in-2&quot;&gt;✔️Check-in &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-avoid-invisible-text/#%E2%9C%94%EF%B8%8Fcheck-in-2&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Your completed script should look like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token 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;text/javascript&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; pacificoObserver &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;FontFaceObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Pacifico&#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; robotoObserver &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;FontFaceObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Roboto&#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;  Promise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;    pacificoObserver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    robotoObserver&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;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;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;documentElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;className &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; fonts-loaded&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;update-css&quot;&gt;Update CSS &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-avoid-invisible-text/#update-css&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Your page should be styled to use a system font initially and custom fonts once
the &lt;code&gt;fonts-loaded&lt;/code&gt; class has been applied.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Update the CSS:&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;del class=&quot;highlight-line highlight-line-remove&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.header&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 selector&quot;&gt;html.fonts-loaded .header&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 property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Pacifico&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cursive&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&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.text&lt;br /&gt;html.fonts-loaded .text&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Roboto&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/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;h2 id=&quot;verify&quot;&gt;Verify &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-avoid-invisible-text/#verify&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;If the page looks like this, then you&#39;ve successfully implemented Font Face
Observer and gotten rid of the &amp;quot;Flash of Invisible Text.&amp;quot;&lt;/p&gt;
&lt;img alt=&quot;A heading in a cursive font.&quot; decoding=&quot;async&quot; height=&quot;246&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 572px) 572px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/st2aEbPzhPIwyJiobkgO.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/st2aEbPzhPIwyJiobkgO.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/st2aEbPzhPIwyJiobkgO.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/st2aEbPzhPIwyJiobkgO.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/st2aEbPzhPIwyJiobkgO.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/st2aEbPzhPIwyJiobkgO.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/st2aEbPzhPIwyJiobkgO.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/st2aEbPzhPIwyJiobkgO.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/st2aEbPzhPIwyJiobkgO.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/st2aEbPzhPIwyJiobkgO.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/st2aEbPzhPIwyJiobkgO.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/st2aEbPzhPIwyJiobkgO.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/st2aEbPzhPIwyJiobkgO.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/st2aEbPzhPIwyJiobkgO.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/st2aEbPzhPIwyJiobkgO.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/st2aEbPzhPIwyJiobkgO.png?auto=format&amp;w=1144 1144w&quot; width=&quot;572&quot; /&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Use density descriptors</title>
    <link href="https://web.dev/codelab-density-descriptors/"/>
    <updated>2018-11-05T00:00:00Z</updated>
    <id>https://web.dev/codelab-density-descriptors/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;explore-this-demo&quot;&gt;Explore This Demo &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-density-descriptors/#explore-this-demo&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;li&gt;Reload the page using different devices to see the browser load different
images.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can use the device emulator for this. If you&#39;re looking for specific display
densities, here are some devices to try:&lt;/p&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;1x density&lt;/td&gt;
        &lt;td&gt;Blackberry Playbook, many external monitors&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2x density&lt;/td&gt;
        &lt;td&gt;iPad or IPhone 5/6&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;3x density&lt;/td&gt;
        &lt;td&gt;Galaxy S5 or iPhone X&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Checkout &lt;code&gt;index.html&lt;/code&gt; for the code that makes this work.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-does-it-work&quot;&gt;How does it work? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-density-descriptors/#how-does-it-work&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The concept of density descriptors may be unfamiliar to most folks. To better
understand them, it helps to have a bit of background on how the browser works
with pixels.&lt;/p&gt;
&lt;h2 id=&quot;what-are-pixels&quot;&gt;What are pixels &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-density-descriptors/#what-are-pixels&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let&#39;s start at the very beginning by defining what a pixel is. This sounds
simple, but &amp;quot;pixel&amp;quot; can actually have many meanings:&lt;/p&gt;
&lt;dl&gt;
  &lt;dt&gt;
    Device pixel (a.k.a. &quot;physical pixel&quot;)
  &lt;/dt&gt;
  &lt;dd&gt;
    The smallest dot of color that can be displayed on a device.
  &lt;/dd&gt;
  &lt;dt&gt;
    Logical pixel
  &lt;/dt&gt;
  &lt;dd&gt;
    Information that specifies the color at a particular location on a grid.
    This type of pixel has no inherent physical size.
  &lt;/dd&gt;
  &lt;dt&gt;
    CSS pixel
  &lt;/dt&gt;
  &lt;dd&gt;
    The CSS spec defines a pixel as a unit of physical measurement. 1 pixel =
1/96th of an inch.
  &lt;/dd&gt;
&lt;/dl&gt;
&lt;h2 id=&quot;pixel-density&quot;&gt;Pixel Density &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-density-descriptors/#pixel-density&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Pixel density (also referred to as &amp;quot;screen density&amp;quot; or &amp;quot;display density&amp;quot;)
measures &lt;em&gt;the density of device pixels in a given physical area&lt;/em&gt;. This is
commonly measured using pixels per inch (ppi).&lt;/p&gt;
&lt;p&gt;For many years, 96 ppi was a very common display density (hence CSS defining a
pixel as 1/96th of an inch). Starting in the 1980s it was the default resolution
of Windows. In addition, it was the resolution of &lt;a href=&quot;https://en.wikipedia.org/wiki/Cathode_ray_tube&quot; rel=&quot;noopener&quot;&gt;CRT
monitors&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This began to change as LED monitors became common in the 2000s. In particular,
Apple made a big splash in 2010 when it introduced Retina displays. These
displays had a minimum resolution of 192 ppi, which was twice the resolution of
&amp;quot;regular&amp;quot; displays (192 ppi/96 ppi = 2).&lt;/p&gt;
&lt;h2 id=&quot;windowdevicepixelratio&quot;&gt;window.devicePixelRatio &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-density-descriptors/#windowdevicepixelratio&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With the introduction of newer display technology, &amp;quot;device pixels&amp;quot; began to vary
in physical size and &lt;a href=&quot;https://en.wikipedia.org/wiki/Pixel_aspect_ratio&quot; rel=&quot;noopener&quot;&gt;shape&lt;/a&gt;
and were no longer the same size as &amp;quot;CSS pixels&amp;quot;. The need to define the
relationship between the size of &amp;quot;device pixels&amp;quot; and &amp;quot;CSS pixels&amp;quot; is what led to
the introduction of the &lt;code&gt;devicePixelRatio&lt;/code&gt; (sometimes called the &amp;quot;CSS Pixel
Ratio&amp;quot;).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;devicePixelRatio&lt;/code&gt; defines the relationship between device pixels and CSS pixels
for a particular device. A 192 ppi device has a &lt;code&gt;devicePixelRatio&lt;/code&gt; of 2 (192
ppi/96 ppi = 2) because &amp;quot;2 of its display pixels are the size of 1 CSS pixel&amp;quot;.&lt;/p&gt;
&lt;p&gt;These days most devices have a device-pixel-ratio between 1.0 and 4.0.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; This ratio doesn&#39;t have to be a whole number. &lt;code&gt;1.5&lt;/code&gt;, &lt;code&gt;2.4&lt;/code&gt;, and &lt;code&gt;2.5&lt;/code&gt; are all device-pixel-ratios of common devices. &lt;/div&gt;&lt;/aside&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Determine the pixel density of a device by typing &lt;code&gt;window.devicePixelRatio&lt;/code&gt;
in the console.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;View &lt;a href=&quot;https://www.mydevice.io/#tab1&quot; rel=&quot;noopener&quot;&gt;this table&lt;/a&gt; to see the pixel ratios of
common devices. Most are between 1.0 and 4.0.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So how do you actually apply this information?&lt;/p&gt;
&lt;h2 id=&quot;size-images-based-on-device-pixel-ratios&quot;&gt;Size images based on device-pixel-ratios &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-density-descriptors/#size-images-based-on-device-pixel-ratios&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In order for images to look their very best on high resolution screens, it&#39;s
necessary to provide different image versions for different &lt;code&gt;devicePixelRatios&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Device Pixel Ratio&lt;/th&gt;
        &lt;th&gt;Indicates that:&lt;/th&gt;
        &lt;th&gt;
          On this device, an &amp;lt;img&amp;gt; tag with a CSS width of 250 pixels, will
          look best when the source image is...
        &lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;1&lt;/td&gt;
        &lt;td&gt;1 device pixel = 1 CSS pixel&lt;/td&gt;
        &lt;td&gt;250 pixels wide&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;2&lt;/td&gt;
        &lt;td&gt;2 device pixels = 1 CSS pixel&lt;/td&gt;
        &lt;td&gt;500 pixels wide&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;3&lt;/td&gt;
        &lt;td&gt;3 device pixels = 1 CSS pixel&lt;/td&gt;
        &lt;td&gt;750 pixels wide&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Things to note:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The pixel dimensions listed in image editors, file directories, and
other places are a measurement of logical pixels.&lt;/li&gt;
&lt;li&gt;For higher resolution screens and larger displays you&#39;ll need images with
larger dimensions. Merely enlarging smaller images defeats the purpose of
serving multiple image versions. The browser would have done this anyway if
a high resolution image was not provided.&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; Tools like &lt;a href=&quot;https://www.npmjs.com/package/sharp&quot;&gt;sharp&lt;/a&gt; make it easy to create multiple sizes of an image. This is covered in more detail here. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;use-density-descriptors-to-serve-multiple-images&quot;&gt;Use Density Descriptors to serve multiple &lt;br /&gt; images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-density-descriptors/#use-density-descriptors-to-serve-multiple-images&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Density descriptors, in conjunction with the &amp;quot;srcset &amp;quot; attribute, can be used to
serve different images to different devicePixelRatios.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Take a look at the &lt;code&gt;index.html&lt;/code&gt; file and note the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;flower.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;flower-1x.jpg 1x,&lt;br /&gt;          flower-2x.jpg 2x,&lt;br /&gt;          flower-3x.jpg 3x&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This example put into words:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1x&lt;/code&gt;, &lt;code&gt;2x&lt;/code&gt;, and &lt;code&gt;3x&lt;/code&gt; are all density descriptors that tell the browser
the pixel density that an image is intended for. This saves the browser
from needing to download an image to determine this information.&lt;/li&gt;
&lt;li&gt;The browser can choose between three images: &lt;code&gt;flower-1x.jpg&lt;/code&gt; (intended
for browsers with a &lt;code&gt;1.0&lt;/code&gt; pixel density), &lt;code&gt;flower-2x.jpg&lt;/code&gt; (intended for
browsers with a &lt;code&gt;2.0&lt;/code&gt; pixel density), and &lt;code&gt;flower-3x.jpg&lt;/code&gt; (intended for
browsers with a &lt;code&gt;3.0&lt;/code&gt; pixel density).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;flower.jpg&lt;/code&gt; is the fallback image for browsers that do not support
&lt;code&gt;srcset&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How to use this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use a devicePixelRatio and the &lt;code&gt;x&lt;/code&gt; unit to write density descriptors. For
example, the density descriptor for many Retina screens
(&lt;code&gt;window.devicePixelRatio = 2&lt;/code&gt;) would be written as &lt;code&gt;2x&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If a density descriptor isn&#39;t provided, it is assumed to be &lt;code&gt;1x&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Including density descriptors in filenames is a common convention (and will
help you keep track of files) but is not necessary. Images can have any
filename.&lt;/li&gt;
&lt;li&gt;There is no need to include a &lt;code&gt;sizes&lt;/code&gt; attribute. The &lt;code&gt;sizes&lt;/code&gt; attribute is only
used with width descriptors.&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Using Imagemin with Grunt</title>
    <link href="https://web.dev/codelab-imagemin-grunt/"/>
    <updated>2018-11-05T00:00:00Z</updated>
    <id>https://web.dev/codelab-imagemin-grunt/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;setup-the-imagemin-grunt-plugin&quot;&gt;Setup the Imagemin Grunt plugin &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-grunt/#setup-the-imagemin-grunt-plugin&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This Glitch already contains &lt;code&gt;grunt&lt;/code&gt;, &lt;code&gt;grunt-cli&lt;/code&gt;, and the &lt;code&gt;grunt-contrib-imagemin&lt;/code&gt;
plugin. To add the configuration for Imagemin, you&#39;ll need to edit your
&lt;code&gt;gruntfile.js&lt;/code&gt; file.&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;li&gt;In &lt;code&gt;gruntfile.js&lt;/code&gt;, replace the &lt;code&gt;//Add configuration here&lt;/code&gt; comment
with this code block:&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;imagemin&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;dynamic&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;files&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;cwd&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;images/&#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;expand&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;src&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;**/*.{png,jpg}&#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 punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This code block tells Grunt which files should be compressed with Imagemin.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;dynamic&lt;/code&gt; indicates that the list of files to compress will be &lt;em&gt;dynamically&lt;/em&gt;
generated by matching the filenames against the specified file pattern.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The file pattern &lt;code&gt;{cwd: &#39;images/&#39;, expand: true, src: [&#39;**/*.{png,jpg}&#39;]}&lt;/code&gt;
will match all the JPEG and PNG images in the &lt;code&gt;images/&lt;/code&gt; directory.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Have more questions about this? You can &lt;a href=&quot;https://gruntjs.com/configuring-tasks#building-the-files-object-dynamically&quot;&gt;read more about the Grunt file object here&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;ul&gt;
&lt;li&gt;Load the Imagemin task by adding this line immediately before
&lt;code&gt;grunt.registerTask(...)&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;
&lt;strong&gt;grunt.loadNpmTasks(&#39;grunt-contrib-imagemin&#39;);&lt;/strong&gt;
grunt.registerTask(&#39;default&#39;, [/* list plugins here */]);
&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Lastly, add Imagemin as the default Grunt task by replacing the &lt;code&gt;/* list plugins here */&lt;/code&gt; comment with &lt;code&gt;&#39;imagemin&#39;&lt;/code&gt;. That line should now look like this:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;
grunt.registerTask(&#39;default&#39;, &lt;strong&gt;[&#39;imagemin&#39;]&lt;/strong&gt;);
&lt;/pre&gt;
&lt;h2 id=&quot;✔︎-check-in&quot;&gt;✔︎ Check-in &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-grunt/#%E2%9C%94%EF%B8%8E-check-in&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The complete &lt;code&gt;gruntfile.js&lt;/code&gt; file should now look like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; grunt &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;grunt&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;grunt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;initConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&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;imagemin&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;dynamic&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;files&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;cwd&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;images/&#39;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;expand&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;src&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;**/*.{png,jpg}&#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 punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;grunt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;loadNpmTasks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;grunt-contrib-imagemin&#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;grunt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;registerTask&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;default&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;imagemin&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;customize-your-imagemin-configuration&quot;&gt;Customize your Imagemin Configuration &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-grunt/#customize-your-imagemin-configuration&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;imagemin-pngquant&lt;/code&gt; is a plugin for specifying compression quality levels.
We&#39;ve already added &lt;code&gt;imagemin-pngquant&lt;/code&gt; to this project in the &lt;code&gt;package.json&lt;/code&gt;
file so that you can use it to compress your PNGs. To use it, declare the plugin
and specify a compression quality level in your Gruntfile.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Declare the &lt;code&gt;imagemin-pngquant&lt;/code&gt; plugin by adding this line to the top of your
&lt;code&gt;gruntfile.js&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;
&lt;strong&gt;const pngquant = require(&#39;imagemin-pngquant&#39;);&lt;/strong&gt;
const grunt = require(&#39;grunt&#39;)
grunt.initConfig({
  ...
&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Add settings for compressing PNG images by adding an &lt;code&gt;options&lt;/code&gt; property to the
&lt;code&gt;imagemin&lt;/code&gt; object. That &lt;code&gt;options&lt;/code&gt; property should look like this:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;
grunt.initConfig({
  imagemin: {
    &lt;strong&gt;options: {&lt;/strong&gt;
      &lt;strong&gt;use: [&lt;/strong&gt;
        &lt;strong&gt;pngquant({quality: [0.5, 0.5]}),&lt;/strong&gt;
      &lt;strong&gt;]&lt;/strong&gt;
    &lt;strong&gt;},&lt;/strong&gt;
    dynamic: {
  ...
&lt;/pre&gt;
&lt;p&gt;This code tells Imagemin to compress PNGs using the Pngquant plugin. The
&lt;code&gt;quality&lt;/code&gt; field uses a &lt;code&gt;min&lt;/code&gt; and &lt;code&gt;max&lt;/code&gt; range of values to determine the
compression level—0 is the lowest and 1 is the highest. To force all images to
be compressed at 50% quality, pass &lt;code&gt;0.5&lt;/code&gt; as both the min and max value.&lt;/p&gt;
&lt;h2 id=&quot;✔︎-check-in-2&quot;&gt;✔︎ Check-in &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-grunt/#%E2%9C%94%EF%B8%8E-check-in-2&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Your &lt;code&gt;gruntfile.js&lt;/code&gt; file should now look like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; pngquant &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;imagemin-pngquant&#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; grunt &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;grunt&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;grunt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;initConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&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;imagemin&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;options&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;use&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 function&quot;&gt;pngquant&lt;/span&gt;&lt;span class=&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;quality&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 number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;dynamic&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;files&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;cwd&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;images/&#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;expand&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;src&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;**/*.{png,jpg}&#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 punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;grunt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;loadNpmTasks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;grunt-contrib-imagemin&#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;grunt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;registerTask&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;default&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;imagemin&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;But what about JPEGs? The project also has JPEG images, so you should specify how
they are compressed as well.&lt;/p&gt;
&lt;h2 id=&quot;customize-your-imagemin-configuration-continued&quot;&gt;Customize your Imagemin configuration (continued) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-grunt/#customize-your-imagemin-configuration-continued&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use the &lt;code&gt;imagemin-mozjpeg&lt;/code&gt; plugin, which has already been installed for you, to
compress JPEG images.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Declare the &lt;code&gt;imagemin-mozjpeg&lt;/code&gt; plugin by putting this line at the top your
&lt;code&gt;gruntfile.js&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;
&lt;strong&gt;const mozjpeg = require(&#39;imagemin-mozjpeg&#39;);&lt;/strong&gt;
const pngquant = require(&#39;imagemin-pngquant&#39;);
const grunt = require(&#39;grunt&#39;);
&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Next, add &lt;code&gt;mozjpeg({quality: 50})&lt;/code&gt; to the array in the &lt;code&gt;options&lt;/code&gt; object.
That array should now look like this:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;
grunt.initConfig({
  imagemin: {
    options: {
      use: [
        pngquant({quality: [0.5, 0.5]}),
        &lt;strong&gt;mozjpeg({quality: 50})&lt;/strong&gt;
      ]
    },
    dynamic: {
&lt;/pre&gt;
&lt;h2 id=&quot;✔︎-check-in-3&quot;&gt;✔︎ Check-in &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-grunt/#%E2%9C%94%EF%B8%8E-check-in-3&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Your &lt;code&gt;gruntfile.js&lt;/code&gt; file should now look like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; mozjpeg &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;imagemin-mozjpeg&#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; pngquant &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;imagemin-pngquant&#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; grunt &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;grunt&#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;grunt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;initConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&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;imagemin&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;options&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;use&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 function&quot;&gt;pngquant&lt;/span&gt;&lt;span class=&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;quality&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 number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&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 function&quot;&gt;mozjpeg&lt;/span&gt;&lt;span class=&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;quality&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;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;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;dynamic&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;files&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;cwd&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;images/&#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;expand&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;src&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;**/*.{png,jpg}&#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 punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;grunt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;loadNpmTasks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;grunt-contrib-imagemin&#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;grunt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;registerTask&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;default&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;imagemin&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;run-grunt-and-verify-results-with-lighthouse&quot;&gt;Run Grunt &amp;amp; verify results with Lighthouse &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-grunt/#run-grunt-and-verify-results-with-lighthouse&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Click the &lt;strong&gt;Tools&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt;Then click the &lt;strong&gt;Console&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt;Run Grunt by typing the following command into the console:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;devsite-terminal devsite-click-to-copy&quot;&gt;
grunt
&lt;/pre&gt;
&lt;p&gt;When Grunt completes you should see a message like this in console:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Minified 6 images (saved 667 kB - 66.5%)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Hooray! These results are much better.&lt;/p&gt;
&lt;p&gt;Lastly, it&#39;s a good idea to use Lighthouse to verify the changes that you just
made. Lighthouse&#39;s &amp;quot;Efficiently encode images&amp;quot; performance audit will let you
know if the JPEG images on your page are optimally compressed.&lt;/p&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;li&gt;Run the Lighthouse performance audit (Lighthouse &amp;gt; Options &amp;gt; Performance) on
the live version of your Glitch and verify that the &amp;quot;Efficiently encode
images&amp;quot; audit was passed.&lt;/li&gt;
&lt;/ul&gt;
&lt;img alt=&quot;Passing &amp;#x27;Efficiently encode images&amp;#x27; audit in Lighthouse&quot; class=&quot;screenshot&quot; decoding=&quot;async&quot; height=&quot;976&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 766px) 766px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/TTrEG19zxZRSWNv2pRG4.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/TTrEG19zxZRSWNv2pRG4.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/TTrEG19zxZRSWNv2pRG4.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/TTrEG19zxZRSWNv2pRG4.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/TTrEG19zxZRSWNv2pRG4.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/TTrEG19zxZRSWNv2pRG4.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/TTrEG19zxZRSWNv2pRG4.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/TTrEG19zxZRSWNv2pRG4.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/TTrEG19zxZRSWNv2pRG4.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/TTrEG19zxZRSWNv2pRG4.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/TTrEG19zxZRSWNv2pRG4.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/TTrEG19zxZRSWNv2pRG4.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/TTrEG19zxZRSWNv2pRG4.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/TTrEG19zxZRSWNv2pRG4.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/TTrEG19zxZRSWNv2pRG4.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/TTrEG19zxZRSWNv2pRG4.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/TTrEG19zxZRSWNv2pRG4.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/TTrEG19zxZRSWNv2pRG4.png?auto=format&amp;w=1532 1532w&quot; width=&quot;766&quot; /&gt;
&lt;p&gt;Success! You have used Imagemin to optimally compress the images on your page.&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Using Imagemin with Gulp</title>
    <link href="https://web.dev/codelab-imagemin-gulp/"/>
    <updated>2018-11-05T00:00:00Z</updated>
    <id>https://web.dev/codelab-imagemin-gulp/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;setup-the-imagemin-gulp-plugin&quot;&gt;Setup the Imagemin Gulp plugin &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-gulp/#setup-the-imagemin-gulp-plugin&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This Glitch already contains &lt;code&gt;gulp&lt;/code&gt;, &lt;code&gt;gulp-cli&lt;/code&gt;, and the &lt;code&gt;gulp-imagemin&lt;/code&gt; plugin.
To add the configuration for Imagemin, you&#39;ll need to edit your &lt;code&gt;gulpfile.js&lt;/code&gt; file.&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;li&gt;First, initialize the &lt;code&gt;gulp-imagemin&lt;/code&gt; plugin by adding this code at the top of
&lt;code&gt;gulpfile.js&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; imagemin &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;gulp-imagemin&#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;ul&gt;
&lt;li&gt;Next, replace the &lt;code&gt;//Add tasks here&lt;/code&gt; comment in &lt;code&gt;gulpfile.js&lt;/code&gt; with this code
block:&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;images/*&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;imagemin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;images/&#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;This code adds a Gulp task that uses Imagemin to compress the images in the
&lt;code&gt;images/&lt;/code&gt; directory. The original images are overwritten and saved in the same
&lt;code&gt;images/&lt;/code&gt; directory.&lt;/p&gt;
&lt;h2 id=&quot;✔︎-check-in&quot;&gt;✔︎ Check-in &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-gulp/#%E2%9C%94%EF%B8%8E-check-in&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Your &lt;code&gt;gulpfile.js&lt;/code&gt; file should now look like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; imagemin &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;gulp-imagemin&#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; gulp &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;gulp&#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;gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;default&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;images/*&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;imagemin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;images/&#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 punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You now have a &lt;code&gt;gulpfile.js&lt;/code&gt; that can be used to compress images.&lt;/p&gt;
&lt;h2 id=&quot;run-gulp&quot;&gt;Run Gulp &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-gulp/#run-gulp&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Click the &lt;strong&gt;Tools&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt;Then click the &lt;strong&gt;Console&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt;Run Gulp to compress your images by typing the following command into the
console:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;devsite-terminal devsite-click-to-copy&quot;&gt;
gulp
&lt;/pre&gt;
&lt;p&gt;When Gulp completes, you should see a message like this in the terminal:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;gulp-imagemin: Minified &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt; images &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;saved &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt; kB—14.8%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;A 15% improvement in file size is a good start; however, more improvements can
be made by using different compression settings.&lt;/p&gt;
&lt;h2 id=&quot;customize-your-imagemin-configuration&quot;&gt;Customize your Imagemin Configuration &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-gulp/#customize-your-imagemin-configuration&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;imagemin-pngquant&lt;/code&gt; is a plugin for specifying compression quality levels.
We&#39;ve already added &lt;code&gt;imagemin-pngquant&lt;/code&gt; to this project in the &lt;code&gt;package.json&lt;/code&gt;
file so that you can use it to compress your PNGs. To use it, declare the plugin
and specify a compression quality level in your gulpfile.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Declare the &lt;code&gt;imagemin-pngquant&lt;/code&gt; plugin by adding this line to the top of your
&lt;code&gt;gulpfile.js&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; pngquant &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;imagemin-pngquant&#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;ul&gt;
&lt;li&gt;Add the &lt;code&gt;imagemin-pngquant&lt;/code&gt; plugin (and its settings) by passing the following
array to &lt;code&gt;ImageminPlugin()&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pngquant&lt;/span&gt;&lt;span class=&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;quality&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 number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This code tells Imagemin to compress PNGs using the Pngquant plugin. The
&lt;code&gt;quality&lt;/code&gt; field uses a &lt;code&gt;min&lt;/code&gt; and &lt;code&gt;max&lt;/code&gt; range of values to determine the
compression level—0 is the lowest and 1 is the highest. To force all images to
be compressed at 50% quality, pass &lt;code&gt;0.5&lt;/code&gt; as both the min and max value.&lt;/p&gt;
&lt;h2 id=&quot;✔︎-check-in-2&quot;&gt;✔︎ Check-in &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-gulp/#%E2%9C%94%EF%B8%8E-check-in-2&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Your &lt;code&gt;gulpfile.js&lt;/code&gt; file should now look like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; pngquant &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;imagemin-pngquant&#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; imagemin &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;gulp-imagemin&#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; gulp &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;gulp&#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;gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;default&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;images/*&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;imagemin&lt;/span&gt;&lt;span class=&quot;token punctuation&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;pngquant&lt;/span&gt;&lt;span class=&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;quality&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 number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;images/&#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 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;But what about JPGs? The project also has JPG images; these need to be
compressed too.&lt;/p&gt;
&lt;h2 id=&quot;customize-your-imagemin-configuration-continued&quot;&gt;Customize your Imagemin Configuration (continued) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-gulp/#customize-your-imagemin-configuration-continued&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use the &lt;code&gt;imagemin-mozjpeg&lt;/code&gt; plugin, which has already been installed for you, to compress
JPG images.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Declare the &lt;code&gt;imagemin-mozjpeg&lt;/code&gt; plugin by putting this line at the top your &lt;code&gt;gulpfile.js&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; mozjpeg &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;imagemin-mozjpeg&#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;ul&gt;
&lt;li&gt;Next, add &lt;code&gt;mozjpeg({quality: 50})&lt;/code&gt; to the array that&#39;s passed to
&lt;code&gt;ImageminPlugin()&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;pngquant&lt;/span&gt;&lt;span class=&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;quality&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 number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&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 function&quot;&gt;mozjpeg&lt;/span&gt;&lt;span class=&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;quality&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;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;✔︎-check-in-3&quot;&gt;✔︎ Check-in &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-gulp/#%E2%9C%94%EF%B8%8E-check-in-3&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Your &lt;code&gt;gulpfile.js&lt;/code&gt; file should now look like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; mozjpeg &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;imagemin-mozjpeg&#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;const&lt;/span&gt; pngquant &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;imagemin-pngquant&#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; imagemin &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;gulp-imagemin&#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; gulp &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;gulp&#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;gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;default&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;images/*&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;imagemin&lt;/span&gt;&lt;span class=&quot;token punctuation&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;pngquant&lt;/span&gt;&lt;span class=&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;quality&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 number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&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 function&quot;&gt;mozjpeg&lt;/span&gt;&lt;span class=&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;quality&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;gulp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;images/&#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 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;re-run-gulp-and-verify-results-with-lighthouse&quot;&gt;Re-run Gulp &amp;amp; verify results with Lighthouse &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-gulp/#re-run-gulp-and-verify-results-with-lighthouse&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Re-run Gulp:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;devsite-terminal devsite-click-to-copy&quot;&gt;
gulp
&lt;/pre&gt;
&lt;p&gt;When Gulp completes, you should see a message like this in terminal:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;gulp-imagemin: Minified &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt; images &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;saved &lt;span class=&quot;token number&quot;&gt;667&lt;/span&gt; kB—66.5%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Hooray! These results are much better.&lt;/p&gt;
&lt;p&gt;Lastly, it&#39;s a good idea to use Lighthouse to verify the changes that you just
made.&lt;/p&gt;
&lt;p&gt;Lighthouse&#39;s &amp;quot;Efficiently encode images&amp;quot; performance audit can let you know if
the JPEG images on your page are optimally compressed.&lt;/p&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;li&gt;Run the Lighthouse performance audit (Lighthouse &amp;gt; Options &amp;gt; Performance) on
the live version of your Glitch and verify that the &amp;quot;Efficiently encode
images&amp;quot; audit was passed.&lt;/li&gt;
&lt;/ul&gt;
&lt;img alt=&quot;Passing &amp;#x27;Efficiently encode images&amp;#x27; audit in Lighthouse&quot; class=&quot;screenshot&quot; decoding=&quot;async&quot; height=&quot;976&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 766px) 766px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/vNZSjlHVQpG3R8SmojFi.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/vNZSjlHVQpG3R8SmojFi.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/vNZSjlHVQpG3R8SmojFi.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/vNZSjlHVQpG3R8SmojFi.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/vNZSjlHVQpG3R8SmojFi.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/vNZSjlHVQpG3R8SmojFi.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/vNZSjlHVQpG3R8SmojFi.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/vNZSjlHVQpG3R8SmojFi.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/vNZSjlHVQpG3R8SmojFi.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/vNZSjlHVQpG3R8SmojFi.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/vNZSjlHVQpG3R8SmojFi.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/vNZSjlHVQpG3R8SmojFi.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/vNZSjlHVQpG3R8SmojFi.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/vNZSjlHVQpG3R8SmojFi.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/vNZSjlHVQpG3R8SmojFi.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/vNZSjlHVQpG3R8SmojFi.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/vNZSjlHVQpG3R8SmojFi.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/vNZSjlHVQpG3R8SmojFi.png?auto=format&amp;w=1532 1532w&quot; width=&quot;766&quot; /&gt;
&lt;p&gt;Success! You have used Imagemin to optimally compress the images on your page.&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Using Imagemin with webpack</title>
    <link href="https://web.dev/codelab-imagemin-webpack/"/>
    <updated>2018-11-05T00:00:00Z</updated>
    <id>https://web.dev/codelab-imagemin-webpack/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;set-up-the-imagemin-webpack-plugin&quot;&gt;Set up the Imagemin webpack plugin &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-webpack/#set-up-the-imagemin-webpack-plugin&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This Glitch already contains &lt;code&gt;webpack&lt;/code&gt;, &lt;code&gt;webpack-cli&lt;/code&gt;, and
&lt;code&gt;imagemin-webpack-plugin&lt;/code&gt;. To add the configuration for Imagemin, you&#39;ll need
to edit your &lt;code&gt;webpack.config.js&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;The existing &lt;code&gt;webpack.config.js&lt;/code&gt; for this project has been copying images from
the &lt;code&gt;images/&lt;/code&gt; directory to the &lt;code&gt;dist/&lt;/code&gt; directory but it hasn&#39;t been
compressing them.&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;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Why would you copy images to a new &lt;code&gt;dist/&lt;/code&gt; folder? &lt;code&gt;dist/&lt;/code&gt; is short for &amp;quot;distribution&amp;quot; and it&#39;s fairly common practice to keep original code, images, etc. separate from their distributed versions because they may be slightly different. &lt;/div&gt;&lt;/aside&gt;
&lt;ul&gt;
&lt;li&gt;First, declare the Imagemin plugin by adding this code at the top of
&lt;code&gt;webpack.config.js&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ImageminPlugin &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;imagemin-webpack-plugin&#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;default&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Next, add the following code as the last item in the &lt;code&gt;plugins[]&lt;/code&gt; array. This
adds Imagemin to the list of plugins that webpack uses:&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ImageminPlugin&lt;/span&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; Why add it at the end of the array? Adding it there ensures that Imagemin runs last, after all the other plugins. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;✔︎-check-in&quot;&gt;✔︎ Check-in &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-webpack/#%E2%9C%94%EF%B8%8E-check-in&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Your complete &lt;code&gt;webpack.config.js&lt;/code&gt; file should now look like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ImageminPlugin &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;imagemin-webpack-plugin&#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;default&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; CopyWebpackPlugin &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;copy-webpack-plugin&#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; path &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;path&#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;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 literal-property property&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./index.js&#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;output&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;filename&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;bundle.js&#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;path&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;dist&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;plugins&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 keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CopyWebpackPlugin&lt;/span&gt;&lt;span class=&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;from&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;img/**/**&#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;to&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;dist&#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;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;     &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ImageminPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You now have a webpack config that compresses images using Imagemin.&lt;/p&gt;
&lt;h2 id=&quot;run-webpack&quot;&gt;Run webpack &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-webpack/#run-webpack&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&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;/ul&gt;
&lt;ol&gt;
&lt;li&gt;To compress your images, run webpack by typing the following command into 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 --config webpack.config.js --mode development&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;But what happens if you run webpack in production mode?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Re-run webpack, but this time in production mode:&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;webpack --config webpack.config.js --mode production&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This time around, webpack displays a warning letting you know that your PNG
files, in spite of some compression, still exceed the recommended size limit.
(webpack&#39;s &lt;code&gt;development&lt;/code&gt; &amp;amp; &lt;code&gt;production&lt;/code&gt; modes prioritize different things, which
is why you only see this warning while running webpack in production mode.)&lt;/p&gt;
&lt;p&gt;Customize our Imagemin configuration to fix this warning.&lt;/p&gt;
&lt;h2 id=&quot;customize-your-imagemin-configuration&quot;&gt;Customize your Imagemin Configuration &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-webpack/#customize-your-imagemin-configuration&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Add settings for compressing PNG images by passing the following object to &lt;code&gt;ImageminPlugin()&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;pngquant&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 literal-property property&quot;&gt;quality&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 number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This code tells Imagemin to compress PNGs using the Pngquant plugin. The
&lt;code&gt;quality&lt;/code&gt; field uses a &lt;code&gt;min&lt;/code&gt; and &lt;code&gt;max&lt;/code&gt; range of values to determine the
compression level—0 is the lowest and 1 is the highest. To force all images to
be compressed at 50% quality, pass &lt;code&gt;0.5&lt;/code&gt; as both the min and max value.&lt;/p&gt;
&lt;h2 id=&quot;✔︎-check-in-2&quot;&gt;✔︎ Check-in &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-webpack/#%E2%9C%94%EF%B8%8E-check-in-2&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Your &lt;code&gt;webpack.config.js&lt;/code&gt; file should now look like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ImageminPlugin &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;imagemin-webpack-plugin&#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;default&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; CopyWebpackPlugin &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;copy-webpack-plugin&#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; path &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;path&#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;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 literal-property property&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./index.js&#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;output&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;filename&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;bundle.js&#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;path&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;dist&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;plugins&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 keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CopyWebpackPlugin&lt;/span&gt;&lt;span class=&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;from&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;img/**/**&#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;to&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;dist&#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;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ImageminPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&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;pngquant&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 literal-property property&quot;&gt;quality&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 number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;But what about JPEGs? The project also has JPEG images, so you should specify
how they are compressed as well.&lt;/p&gt;
&lt;h2 id=&quot;customize-your-imagemin-configuration-continued&quot;&gt;Customize your Imagemin Configuration (continued) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-webpack/#customize-your-imagemin-configuration-continued&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Instead of using &lt;code&gt;imagemin-webpack-plugin&lt;/code&gt;&#39;s default plugin for JPG compression
(&lt;code&gt;imagemin-jpegtran&lt;/code&gt;), use the &lt;code&gt;imagemin-mozjpeg&lt;/code&gt; plugin. Unlike Jpegtran,
Mozjpeg let&#39;s you specify a compression quality for your JPG compression. We&#39;ve
already installed the Mozjpeg plugin for you in this Glitch, but you&#39;ll need to
edit your &lt;code&gt;webpack.config.js&lt;/code&gt; file:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Initialize the &lt;code&gt;imagemin-mozjpeg&lt;/code&gt; plugin by adding the following line at the
top of your &lt;code&gt;webpack.config.js&lt;/code&gt; file:&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; imageminMozjpeg &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;imagemin-mozjpeg&#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;ul&gt;
&lt;li&gt;Add a &lt;code&gt;plugins&lt;/code&gt; property to the object passed to &lt;code&gt;ImageminPlugin()&lt;/code&gt;, such that
the object now looks like this:&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ImageminPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&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;pngquant&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 literal-property property&quot;&gt;quality&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 number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&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;plugins&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 function&quot;&gt;imageminMozjpeg&lt;/span&gt;&lt;span class=&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;quality&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This code tells webpack to compress JPGs to a quality of 50 (0 is the worst;
100 is the best) using the Mozjpeg plugin.&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; Are you wondering why Mozjpeg is added to the plugins array, but Pngquant isn&#39;t? Good question. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;If you&#39;re adding settings for a plugin that is a default plugin of
&lt;code&gt;imagemin-webpack-plugin&lt;/code&gt;, they can be added as a key-object pair on the object
passed to &lt;code&gt;ImageminPlugin()&lt;/code&gt;. The settings for Pnquant are a good example of
this.&lt;/p&gt;
&lt;p&gt;However, if you&#39;re adding settings for non-default plugins (for example,
Mozjpeg), they should be added by including them in the array corresponding to
the &lt;code&gt;plugins&lt;/code&gt; property.&lt;/p&gt;
&lt;h2 id=&quot;✔︎-check-in-3&quot;&gt;✔︎ Check-in &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-webpack/#%E2%9C%94%EF%B8%8E-check-in-3&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Your code should now look like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; imageminMozjpeg &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;imagemin-mozjpeg&#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; ImageminPlugin &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;imagemin-webpack-plugin&#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;default&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; CopyWebpackPlugin &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;copy-webpack-plugin&#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; path &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;path&#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;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 literal-property property&quot;&gt;entry&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./index.js&#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;output&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;filename&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;bundle.js&#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;path&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;dist&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;plugins&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 keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CopyWebpackPlugin&lt;/span&gt;&lt;span class=&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;from&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;img/**/**&#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;to&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;dist&#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;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ImageminPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&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;pngquant&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 literal-property property&quot;&gt;quality&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 number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&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;plugins&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 function&quot;&gt;imageminMozjpeg&lt;/span&gt;&lt;span class=&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;quality&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;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;re-run-webpack-and-verify-results-with-lighthouse&quot;&gt;Re-run webpack and verify results with Lighthouse &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-imagemin-webpack/#re-run-webpack-and-verify-results-with-lighthouse&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;In the console, re-run webpack:&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;webpack --config webpack.config.js --mode production&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Hooray! Your changes should have fixed the webpack warnings.&lt;/p&gt;
&lt;p&gt;webpack warns you about large images, but it can&#39;t tell you if images are
uncompressed or undercompressed. This is why it&#39;s always a good idea to use
Lighthouse to verify your changes.&lt;/p&gt;
&lt;p&gt;Lighthouse&#39;s &amp;quot;Efficiently encode images&amp;quot; performance audit can let you know if
the JPEG images on your page are optimally compressed.&lt;/p&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;li&gt;Run the Lighthouse performance audit (&lt;strong&gt;Lighthouse &amp;gt; Options &amp;gt; Performance&lt;/strong&gt;)
on the live version of your Glitch and verify that the &lt;strong&gt;Efficiently encode
images&lt;/strong&gt; audit was passed.&lt;/li&gt;
&lt;/ul&gt;
&lt;img alt=&quot;Passing &amp;#x27;Efficiently encode images&amp;#x27; audit in Lighthouse&quot; class=&quot;screenshot&quot; decoding=&quot;async&quot; height=&quot;976&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 766px) 766px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/abAeMMdJEj9j2osonskn.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/abAeMMdJEj9j2osonskn.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/abAeMMdJEj9j2osonskn.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/abAeMMdJEj9j2osonskn.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/abAeMMdJEj9j2osonskn.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/abAeMMdJEj9j2osonskn.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/abAeMMdJEj9j2osonskn.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/abAeMMdJEj9j2osonskn.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/abAeMMdJEj9j2osonskn.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/abAeMMdJEj9j2osonskn.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/abAeMMdJEj9j2osonskn.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/abAeMMdJEj9j2osonskn.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/abAeMMdJEj9j2osonskn.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/abAeMMdJEj9j2osonskn.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/abAeMMdJEj9j2osonskn.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/abAeMMdJEj9j2osonskn.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/abAeMMdJEj9j2osonskn.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/abAeMMdJEj9j2osonskn.png?auto=format&amp;w=1532 1532w&quot; width=&quot;766&quot; /&gt;
&lt;p&gt;Success! You have used Imagemin to optimally compress the images on your page.&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Serve images with correct dimensions</title>
    <link href="https://web.dev/codelab-serve-images-correct-dimensions/"/>
    <updated>2018-11-05T00:00:00Z</updated>
    <id>https://web.dev/codelab-serve-images-correct-dimensions/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;run-lighthouse&quot;&gt;Run Lighthouse &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-serve-images-correct-dimensions/#run-lighthouse&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This Glitch is small enough that its images could be inspected by hand. However
for most websites, using a tool like Lighthouse to automate this is essential.&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;Lighthouse&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Make sure the &lt;strong&gt;Performance&lt;/strong&gt; checkbox is selected in the &lt;em&gt;Categories&lt;/em&gt; list.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Generate report&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt;Look for the results of the &lt;strong&gt;Properly Size Images&lt;/strong&gt; audit.&lt;/li&gt;
&lt;/ol&gt;
&lt;img alt=&quot;The properly size images audit failing in Lighthouse.&quot; decoding=&quot;async&quot; height=&quot;320&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/rGu0xlOX97DGQlujoHad.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/rGu0xlOX97DGQlujoHad.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/rGu0xlOX97DGQlujoHad.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/rGu0xlOX97DGQlujoHad.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/rGu0xlOX97DGQlujoHad.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/rGu0xlOX97DGQlujoHad.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/rGu0xlOX97DGQlujoHad.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/rGu0xlOX97DGQlujoHad.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/rGu0xlOX97DGQlujoHad.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/rGu0xlOX97DGQlujoHad.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/rGu0xlOX97DGQlujoHad.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/rGu0xlOX97DGQlujoHad.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/rGu0xlOX97DGQlujoHad.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/rGu0xlOX97DGQlujoHad.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/rGu0xlOX97DGQlujoHad.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/rGu0xlOX97DGQlujoHad.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/rGu0xlOX97DGQlujoHad.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/rGu0xlOX97DGQlujoHad.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;The Lighthouse audit shows that both of this page&#39;s images need to be resized.&lt;/p&gt;
&lt;h2 id=&quot;fix-flowerlogopng&quot;&gt;Fix &lt;code&gt;flower_logo.png&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-serve-images-correct-dimensions/#fix-flowerlogopng&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Start at the top of the page and fix the logo image.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Inspect &lt;code&gt;flower_logo.png&lt;/code&gt; in the DevTools Elements panel.&lt;/li&gt;
&lt;/ul&gt;
&lt;img alt=&quot;The DevTools elements panel&quot; decoding=&quot;async&quot; height=&quot;316&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/W7Ig8iyp6dKpQyNkJX3S.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/W7Ig8iyp6dKpQyNkJX3S.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/W7Ig8iyp6dKpQyNkJX3S.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/W7Ig8iyp6dKpQyNkJX3S.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/W7Ig8iyp6dKpQyNkJX3S.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/W7Ig8iyp6dKpQyNkJX3S.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/W7Ig8iyp6dKpQyNkJX3S.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/W7Ig8iyp6dKpQyNkJX3S.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/W7Ig8iyp6dKpQyNkJX3S.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/W7Ig8iyp6dKpQyNkJX3S.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/W7Ig8iyp6dKpQyNkJX3S.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/W7Ig8iyp6dKpQyNkJX3S.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/W7Ig8iyp6dKpQyNkJX3S.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/W7Ig8iyp6dKpQyNkJX3S.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/W7Ig8iyp6dKpQyNkJX3S.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/W7Ig8iyp6dKpQyNkJX3S.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/W7Ig8iyp6dKpQyNkJX3S.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/W7Ig8iyp6dKpQyNkJX3S.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;This is the CSS for &lt;code&gt;flower_logo.png&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.logo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The CSS width of this image is 50 pixels, so &lt;code&gt;flower_logo.png&lt;/code&gt; should be resized
to match. You can use &lt;a href=&quot;https://www.imagemagick.org/&quot; rel=&quot;noopener&quot;&gt;ImageMagick&lt;/a&gt; to resize the
image to fit. ImageMagick is a CLI tool for image editing that comes
pre-installed in the codelab environment.&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;In the console, type:&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;convert flower_logo.png -resize 50x50 flower_logo.png&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;fix-flowerphotojpg&quot;&gt;Fix flower_photo.jpg &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-serve-images-correct-dimensions/#fix-flowerphotojpg&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Next, fix the photo of the purple flowers.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Inspect &lt;code&gt;flower_photo.jpg&lt;/code&gt; in the DevTools elements panel.&lt;/li&gt;
&lt;/ul&gt;
&lt;img alt=&quot;The DevTools elements panel&quot; decoding=&quot;async&quot; height=&quot;275&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/zGpgvrz00hsMHKYvQoyU.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/zGpgvrz00hsMHKYvQoyU.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/zGpgvrz00hsMHKYvQoyU.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/zGpgvrz00hsMHKYvQoyU.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/zGpgvrz00hsMHKYvQoyU.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/zGpgvrz00hsMHKYvQoyU.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/zGpgvrz00hsMHKYvQoyU.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/zGpgvrz00hsMHKYvQoyU.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/zGpgvrz00hsMHKYvQoyU.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/zGpgvrz00hsMHKYvQoyU.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/zGpgvrz00hsMHKYvQoyU.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/zGpgvrz00hsMHKYvQoyU.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/zGpgvrz00hsMHKYvQoyU.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/zGpgvrz00hsMHKYvQoyU.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/zGpgvrz00hsMHKYvQoyU.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/zGpgvrz00hsMHKYvQoyU.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/zGpgvrz00hsMHKYvQoyU.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/zGpgvrz00hsMHKYvQoyU.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;This is the CSS for &lt;code&gt;flower_photo.jpg&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.photo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50vw&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 30px auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px solid black&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;&lt;code&gt;50vw&lt;/code&gt; sets the CSS width of &lt;code&gt;flower_photo.jpg&lt;/code&gt; to &amp;quot;half the width of
the browser.&amp;quot;
(&lt;a href=&quot;https://developer.mozilla.org/docs/Learn/CSS/Introduction_to_CSS/Values_and_units&quot; rel=&quot;noopener&quot;&gt;1vw&lt;/a&gt;
is equal to 1% the width of the browser).&lt;/p&gt;
&lt;p&gt;The ideal size for this image would depend on the device it is being viewed on,
so you should resize it to a size that works well for most of your users. You
can check your analytics data to learn which screen resolutions are common
amongst your users:&lt;/p&gt;
&lt;img alt=&quot;Google analytics of screen resolutions.&quot; decoding=&quot;async&quot; height=&quot;865&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 684px) 684px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/6j3106xF9uxWCZGh7PrL.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/6j3106xF9uxWCZGh7PrL.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/6j3106xF9uxWCZGh7PrL.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/6j3106xF9uxWCZGh7PrL.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/6j3106xF9uxWCZGh7PrL.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/6j3106xF9uxWCZGh7PrL.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/6j3106xF9uxWCZGh7PrL.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/6j3106xF9uxWCZGh7PrL.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/6j3106xF9uxWCZGh7PrL.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/6j3106xF9uxWCZGh7PrL.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/6j3106xF9uxWCZGh7PrL.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/6j3106xF9uxWCZGh7PrL.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/6j3106xF9uxWCZGh7PrL.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/6j3106xF9uxWCZGh7PrL.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/6j3106xF9uxWCZGh7PrL.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/6j3106xF9uxWCZGh7PrL.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/6j3106xF9uxWCZGh7PrL.png?auto=format&amp;w=1368 1368w&quot; width=&quot;684&quot; /&gt;
&lt;p&gt;This data indicates that 95%+ of the visitors to this site use screen
resolutions 1920 pixels wide or less.&lt;/p&gt;
&lt;p&gt;Using this information we can calculate how wide the image should be:
(1920 pixels wide) * (50% of browser width) = 960 pixels&lt;/p&gt;
&lt;p&gt;On resolutions greater than 1920 pixels wide, the image will be stretched to
cover the area. The resized image is still fairly large, so the effects of this
should not be very noticeable.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;a href=&quot;https://www.imagemagick.org/&quot; rel=&quot;noopener&quot;&gt;ImageMagick&lt;/a&gt; to resize the image to 960
pixels wide. In the terminal type:&lt;/li&gt;
&lt;/ul&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 comment&quot;&gt;# macOS/Linux&lt;/span&gt;&lt;br /&gt;convert flower_photo.jpg -resize 960x flower_photo.jpg&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Windows&lt;/span&gt;&lt;br /&gt;magick convert flower_photo.jpg -resize 960x flower_photo.jpg&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; &lt;code&gt;960x&lt;/code&gt; is not a typo; it specifies a width, but not a height. The image height will be scaled in proportion to the width. This is a handy trick for when you only care about an image&#39;s dimensions in one direction. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;re-run-lighthouse&quot;&gt;Re-Run Lighthouse &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-serve-images-correct-dimensions/#re-run-lighthouse&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Re-run the Lighthouse Performance audit to verify that you have successfully
re-sized the images.&lt;/li&gt;
&lt;/ul&gt;
&lt;img alt=&quot;Lighthouse properly size images audit.&quot; decoding=&quot;async&quot; height=&quot;271&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/ShzwI5v2hLhHR7CGDRqV.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/ShzwI5v2hLhHR7CGDRqV.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/ShzwI5v2hLhHR7CGDRqV.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/ShzwI5v2hLhHR7CGDRqV.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/ShzwI5v2hLhHR7CGDRqV.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/ShzwI5v2hLhHR7CGDRqV.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/ShzwI5v2hLhHR7CGDRqV.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/ShzwI5v2hLhHR7CGDRqV.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/ShzwI5v2hLhHR7CGDRqV.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/ShzwI5v2hLhHR7CGDRqV.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/ShzwI5v2hLhHR7CGDRqV.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/ShzwI5v2hLhHR7CGDRqV.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/ShzwI5v2hLhHR7CGDRqV.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/ShzwI5v2hLhHR7CGDRqV.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/ShzwI5v2hLhHR7CGDRqV.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/ShzwI5v2hLhHR7CGDRqV.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/ShzwI5v2hLhHR7CGDRqV.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/ShzwI5v2hLhHR7CGDRqV.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;… And it fails! Why is that?&lt;/p&gt;
&lt;p&gt;Lighthouse runs its tests on a Nexus 5x. The Nexus 5x has a 1080 x 1920 screen.
For the Nexus 5x, the optimal size of &lt;code&gt;flower_photo.jpg&lt;/code&gt; would be 540 pixels
wide (1080 pixels * . 5). This is much smaller than our resized image.&lt;/p&gt;
&lt;p&gt;Should you resize the image to be even smaller? Probably. However, the answer to
this isn&#39;t always clear-cut.&lt;/p&gt;
&lt;p&gt;The trade-off here is between image quality on high-resolution devices and
performance. It&#39;s easy to overestimate how closely users will be inspecting
images—so you should probably make them smaller—but there are
certainly use cases where image quality is more important.&lt;/p&gt;
&lt;p&gt;The good news is that you can bypass this tradeoff altogether by using
responsive images to serve multiple images sizes. You can learn more about this
in the &lt;a href=&quot;https://web.dev/serve-responsive-images&quot;&gt;Responsive Images guide&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Creating WebP Images with the Command Line</title>
    <link href="https://web.dev/codelab-serve-images-webp/"/>
    <updated>2018-11-05T00:00:00Z</updated>
    <id>https://web.dev/codelab-serve-images-webp/</id>
    <content type="html" mode="escaped">&lt;p&gt;The webp
&lt;a href=&quot;https://developers.google.com/speed/webp/docs/precompiled&quot;&gt;command line tool&lt;/a&gt;
has already been installed for you, so you&#39;re all set to get started. This tool
converts JPG, PNG, and TIFF images to WebP.&lt;/p&gt;
&lt;h2 id=&quot;convert-images-to-webp&quot;&gt;Convert images to WebP &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-serve-images-webp/#convert-images-to-webp&quot;&gt;#&lt;/a&gt;&lt;/h2&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;Type the following command:&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;cwebp -q &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt; images/flower1.jpg -o images/flower1.webp&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This command converts, at a quality of &lt;code&gt;50&lt;/code&gt; (&lt;code&gt;0&lt;/code&gt; is the worst; &lt;code&gt;100&lt;/code&gt; is the
best), the &lt;code&gt;images/flower1.jpg&lt;/code&gt; file and saves it as &lt;code&gt;images/flower1.webp&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; Are you wondering why you type &lt;code&gt;cwebp&lt;/code&gt; instead of &lt;code&gt;webp&lt;/code&gt;? WebP has two separate commands for encoding and decoding WebP images. &lt;code&gt;cwebp&lt;/code&gt; encodes images to WebP, while &lt;code&gt;dwebp&lt;/code&gt; decodes images from WebP. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;After doing this, you should see something like this in the console:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;Saving &lt;span class=&quot;token function&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;images/flower1.webp&#39;&lt;/span&gt;&lt;br /&gt;File:      images/flower1.jpg&lt;br /&gt;Dimension: &lt;span class=&quot;token number&quot;&gt;504&lt;/span&gt; x &lt;span class=&quot;token number&quot;&gt;378&lt;/span&gt;&lt;br /&gt;Output:    &lt;span class=&quot;token number&quot;&gt;29538&lt;/span&gt; bytes Y-U-V-All-PSNR &lt;span class=&quot;token number&quot;&gt;34.57&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;36.57&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;36.12&lt;/span&gt;   &lt;span class=&quot;token number&quot;&gt;35.09&lt;/span&gt; dB&lt;br /&gt;           &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.24&lt;/span&gt; bpp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;block count:  intra4:        &lt;span class=&quot;token number&quot;&gt;750&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;97.66&lt;/span&gt;%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;              intra16:        &lt;span class=&quot;token number&quot;&gt;18&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2.34&lt;/span&gt;%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;              skipped:         &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.00&lt;/span&gt;%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;bytes used:  header:            &lt;span class=&quot;token number&quot;&gt;116&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.4&lt;/span&gt;%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;             mode-partition:   &lt;span class=&quot;token number&quot;&gt;4014&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;13.6&lt;/span&gt;%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt; Residuals bytes  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;segment &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;segment &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;segment &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;segment &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;  total&lt;br /&gt;    macroblocks:  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;      &lt;span class=&quot;token number&quot;&gt;22&lt;/span&gt;%&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;      &lt;span class=&quot;token number&quot;&gt;26&lt;/span&gt;%&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;      &lt;span class=&quot;token number&quot;&gt;36&lt;/span&gt;%&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;      &lt;span class=&quot;token number&quot;&gt;17&lt;/span&gt;%&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;     &lt;span class=&quot;token number&quot;&gt;768&lt;/span&gt;&lt;br /&gt;      quantizer:  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;      &lt;span class=&quot;token number&quot;&gt;52&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;      &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;      &lt;span class=&quot;token number&quot;&gt;33&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 operator&quot;&gt;|&lt;/span&gt;&lt;br /&gt;   filter level:  &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;      &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;       &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;       &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;      &lt;span class=&quot;token number&quot;&gt;26&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You&#39;ve just successfully converted the image to WebP.&lt;/p&gt;
&lt;p&gt;However, running the &lt;code&gt;cwebp&lt;/code&gt; command one image at a time like this would take a
long time to convert many images. If you need to do this, you can use a script
instead.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Run this script in the console (don&#39;t forget the backticks):&lt;/li&gt;
&lt;/ul&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 variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token for-or-select variable&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; images/*&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; cwebp -q &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$file&lt;/span&gt;&quot;&lt;/span&gt; -o &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${file&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;.*}&lt;/span&gt;.webp&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This script converts, at a quality of &lt;code&gt;50&lt;/code&gt;, all the files in the &lt;code&gt;images/&lt;/code&gt;
directory, and saves them as a new file (same filename, but with a &lt;code&gt;.webp&lt;/code&gt; file
extension) in the same directory.&lt;/p&gt;
&lt;h3 id=&quot;✔︎-check-in&quot;&gt;✔︎ Check-in &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-serve-images-webp/#%E2%9C%94%EF%B8%8E-check-in&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You should now have 6 files in your &lt;code&gt;images/&lt;/code&gt; directory:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;flower1.jpg&lt;br /&gt;flower1.webp&lt;br /&gt;flower2.jpg&lt;br /&gt;flower2.webp&lt;br /&gt;flower3.png&lt;br /&gt;flower3.webp&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Next, update this Glitch to serve WebP images to browsers that support it.&lt;/p&gt;
&lt;h2 id=&quot;add-webp-images-using-the-lesspicturegreater-tag&quot;&gt;Add WebP images using the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; tag &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-serve-images-webp/#add-webp-images-using-the-lesspicturegreater-tag&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; tag allows you to serve WebP to newer browsers while maintaining
support for older browsers.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In &lt;code&gt;index.html&lt;/code&gt; replace &lt;code&gt;&amp;lt;img src=&amp;quot;images/flower1.jpg&amp;quot;/&amp;gt;&lt;/code&gt; with the following
HTML:&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image/webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;images/flower1.webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image/jpeg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;images/flower1.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;images/flower1.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Next, replace the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags for &lt;code&gt;flower2.jpg&lt;/code&gt; and &lt;code&gt;flower3.png&lt;/code&gt; with
&lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; tags.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;✔︎-check-in-2&quot;&gt;✔︎ Check-in &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-serve-images-webp/#%E2%9C%94%EF%B8%8E-check-in-2&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Once completed, the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; tags in &lt;code&gt;index.html&lt;/code&gt; should look like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image/webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;images/flower1.webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image/jpeg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;images/flower1.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;images/flower1.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image/webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;images/flower2.webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image/jpeg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;images/flower2.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;images/flower2.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image/webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;images/flower3.webp&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;image/png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;images/flower3.png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;images/flower3.png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Next, use Lighthouse to verify you&#39;ve correctly implemented WebP images on the
site.&lt;/p&gt;
&lt;h2 id=&quot;verify-webp-usage-with-lighthouse&quot;&gt;Verify WebP usage with Lighthouse &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-serve-images-webp/#verify-webp-usage-with-lighthouse&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Lighthouse&#39;s &lt;strong&gt;Serve images in next-gen formats&lt;/strong&gt; performance audit can let you
know if all the images on your site are using next-gen formats like WebP.&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;Lighthouse&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Make sure the &lt;strong&gt;Performance&lt;/strong&gt; checkbox is selected in the &lt;em&gt;Categories&lt;/em&gt; list.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Generate report&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt;Verify the &lt;strong&gt;Serve images in next-gen formats&lt;/strong&gt; audit is passed.&lt;/li&gt;
&lt;/ol&gt;
&lt;img alt=&quot;Passing &amp;#x27;Serve images in next-gen formats&amp;#x27; audit in Lighthouse&quot; decoding=&quot;async&quot; height=&quot;651&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 701px) 701px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/Y8x0FLWs1Xsf32DX20DG.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/Y8x0FLWs1Xsf32DX20DG.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/Y8x0FLWs1Xsf32DX20DG.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/Y8x0FLWs1Xsf32DX20DG.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/Y8x0FLWs1Xsf32DX20DG.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/Y8x0FLWs1Xsf32DX20DG.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/Y8x0FLWs1Xsf32DX20DG.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/Y8x0FLWs1Xsf32DX20DG.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/Y8x0FLWs1Xsf32DX20DG.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/Y8x0FLWs1Xsf32DX20DG.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/Y8x0FLWs1Xsf32DX20DG.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/Y8x0FLWs1Xsf32DX20DG.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/Y8x0FLWs1Xsf32DX20DG.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/Y8x0FLWs1Xsf32DX20DG.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/Y8x0FLWs1Xsf32DX20DG.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/Y8x0FLWs1Xsf32DX20DG.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/Y8x0FLWs1Xsf32DX20DG.png?auto=format&amp;w=1402 1402w&quot; width=&quot;701&quot; /&gt;
&lt;p&gt;Success! You are now serving WebP images on your site.&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Specifying multiple slot widths</title>
    <link href="https://web.dev/codelab-specifying-multiple-slot-widths/"/>
    <updated>2018-11-05T00:00:00Z</updated>
    <id>https://web.dev/codelab-specifying-multiple-slot-widths/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;try-out-this-demo&quot;&gt;Try out this demo &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-specifying-multiple-slot-widths/#try-out-this-demo&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&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;Reload the app in differently sized browser windows to see the browser load
different images and use different layouts at different browser sizes.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;view-the-code&quot;&gt;View the code &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-specifying-multiple-slot-widths/#view-the-code&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Checkout &lt;code&gt;index.html&lt;/code&gt; for the code that makes this work:&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;flower.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;flower-small.jpg 480w, flower-large.jpg 800w&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token attr-name&quot;&gt;sizes&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(max-width: 480px) 100vw, (max-width: 1024px) 50vw, 800px&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;whats-going-on-here&quot;&gt;What&#39;s going on here? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-specifying-multiple-slot-widths/#whats-going-on-here&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The value of the &lt;code&gt;sizes&lt;/code&gt; attribute dictates that image display width will be:
&amp;quot;100% of the viewport width&amp;quot; on viewports up to 480px wide, &amp;quot;50% of the viewport
width&amp;quot; on screens 481–1024px wide, and 800px on screens wider than 1024px. These
widths match the widths specified in the CSS.&lt;/p&gt;
&lt;p&gt;The ability to specify multiple slot widths accommodates page layouts that use
different styling (that is, image widths) for different viewport sizes.&lt;/p&gt;
&lt;h2 id=&quot;how-to-specify-multiple-slot-widths&quot;&gt;How to specify multiple slot widths &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-specifying-multiple-slot-widths/#how-to-specify-multiple-slot-widths&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Use a comma-separated list to specify multiple slot widths. Each list item,
except for the last item, consists of a media condition (e.g. &lt;code&gt;max-width&lt;/code&gt; or
&lt;code&gt;min-width&lt;/code&gt;) and a slot width.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The last item in this list is the default slot width. It is the default, so
you do not need to specify a media condition.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can list as many slot widths as you want - the number of images listed in
&lt;code&gt;srcset&lt;/code&gt; does not matter.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Slot width can be specified using a variety of units. The following are all
valid widths:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;100px&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;33vw&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;20em&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;calc(50vw-10px)&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following is not a valid width:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;25%&lt;/code&gt; (percentages cannot be used with the sizes attribute)&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
  
  <entry>
    <title>Lazy load offscreen images with lazysizes</title>
    <link href="https://web.dev/codelab-use-lazysizes-to-lazyload-images/"/>
    <updated>2018-11-05T00:00:00Z</updated>
    <id>https://web.dev/codelab-use-lazysizes-to-lazyload-images/</id>
    <content type="html" mode="escaped">&lt;p&gt;Lazy loading is the approach of waiting to load resources until they are needed,
rather than loading them in advance. This can improve performance by reducing
the amount of resources that need to be loaded and parsed on initial page load.&lt;/p&gt;
&lt;p&gt;Images that are offscreen during the initial pageload are ideal candidates for
this technique. Best of all, &lt;a href=&quot;https://github.com/aFarkas/lazysizes&quot; rel=&quot;noopener&quot;&gt;lazysizes&lt;/a&gt;
makes this a very simple strategy to implement.&lt;/p&gt;
&lt;h2 id=&quot;add-the-lazysizes-script-to-the-page&quot;&gt;Add the lazysizes script to the page &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-use-lazysizes-to-lazyload-images/#add-the-lazysizes-script-to-the-page&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;&lt;code&gt;lazysizes.min.js&lt;/code&gt; has already been downloaded and added to this Glitch. To
include it in the page:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add the following &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag to &lt;code&gt;index.html&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;mark class=&quot;highlight-line highlight-line-active&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;lazysizes.min.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Images End --&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&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;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The &lt;a href=&quot;https://raw.githubusercontent.com/aFarkas/lazysizes/gh-pages/lazysizes.min.js&quot;&gt;lazysizes.min.js&lt;/a&gt; file has already been added to this project, so there is no need to add it separately. The script you just added can use this script. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;lazysizes will intelligently load images as the user scrolls through the page
and prioritize the images that the user is going to encounter soon.&lt;/p&gt;
&lt;h2 id=&quot;indicate-the-images-to-lazy-load&quot;&gt;Indicate the images to lazy load &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-use-lazysizes-to-lazyload-images/#indicate-the-images-to-lazy-load&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Add the class &lt;code&gt;lazyload&lt;/code&gt; to images that should be lazy loaded. In addition,
change the &lt;code&gt;src&lt;/code&gt; attribute to &lt;code&gt;data-src&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, the changes for &lt;code&gt;flower3.png&lt;/code&gt; would look like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;del class=&quot;highlight-line highlight-line-remove&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;images/flower3.png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/del&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data-src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;images/flower3.png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lazyload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/ins&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;For this example, try lazy loading &lt;code&gt;flower3.png&lt;/code&gt;, &lt;code&gt;flower4.jpg&lt;/code&gt;, and
&lt;code&gt;flower5.jpg&lt;/code&gt;.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; You may be wondering why it is necessary to change the &lt;code&gt;src&lt;/code&gt; attribute to &lt;code&gt;data-src&lt;/code&gt;. If this attribute is not changed, all the images will load immediately instead of being lazy-loaded. &lt;code&gt;data-src&lt;/code&gt; is not an attribute that the browser recognizes, so when it encounters an image tag with this attribute, it doesn&#39;t load the image. In this case, that is a good thing, because it then allows the lazysizes script to decide when the image should be loaded, rather than the browser. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;see-it-in-action&quot;&gt;See it in action &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-use-lazysizes-to-lazyload-images/#see-it-in-action&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;That&#39;s it! To see these changes in action, follow these steps:&lt;/p&gt;
&lt;ul&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;Open the console and find the images that were just added. Their classes
should change from &lt;code&gt;lazyload&lt;/code&gt; to &lt;code&gt;lazyloaded&lt;/code&gt; as you scroll down the page.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;img alt=&quot;Images being lazy loaded&quot; decoding=&quot;async&quot; height=&quot;252&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 428px) 428px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/yXej5KAOMzoqoQAB2paq.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/yXej5KAOMzoqoQAB2paq.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/yXej5KAOMzoqoQAB2paq.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/yXej5KAOMzoqoQAB2paq.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/yXej5KAOMzoqoQAB2paq.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/yXej5KAOMzoqoQAB2paq.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/yXej5KAOMzoqoQAB2paq.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/yXej5KAOMzoqoQAB2paq.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/yXej5KAOMzoqoQAB2paq.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/yXej5KAOMzoqoQAB2paq.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/yXej5KAOMzoqoQAB2paq.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/yXej5KAOMzoqoQAB2paq.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/yXej5KAOMzoqoQAB2paq.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/yXej5KAOMzoqoQAB2paq.png?auto=format&amp;w=856 856w&quot; width=&quot;428&quot; /&gt;
&lt;ul&gt;
&lt;li&gt;Watch the network panel to see the image files load individually as you scroll
down the page.&lt;/li&gt;
&lt;/ul&gt;
&lt;img alt=&quot;Images being lazy loaded&quot; decoding=&quot;async&quot; height=&quot;233&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 418px) 418px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/tcQpLeAubOW1l42eyXiW.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/tcQpLeAubOW1l42eyXiW.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/tcQpLeAubOW1l42eyXiW.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/tcQpLeAubOW1l42eyXiW.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/tcQpLeAubOW1l42eyXiW.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/tcQpLeAubOW1l42eyXiW.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/tcQpLeAubOW1l42eyXiW.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/tcQpLeAubOW1l42eyXiW.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/tcQpLeAubOW1l42eyXiW.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/tcQpLeAubOW1l42eyXiW.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/tcQpLeAubOW1l42eyXiW.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/tcQpLeAubOW1l42eyXiW.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/tcQpLeAubOW1l42eyXiW.png?auto=format&amp;w=836 836w&quot; width=&quot;418&quot; /&gt;
&lt;h2 id=&quot;verify-using-lighthouse&quot;&gt;Verify using Lighthouse &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-use-lazysizes-to-lazyload-images/#verify-using-lighthouse&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Lastly, it&#39;s a good idea to use Lighthouse to verify these changes. Lighthouse&#39;s
&amp;quot;Defer offscreen images&amp;quot; performance audit will indicate if you&#39;ve forgotten to
add lazy loading to any offscreen images.&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;Lighthouse&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Make sure the &lt;strong&gt;Performance&lt;/strong&gt; checkbox is selected in the &lt;em&gt;Categories&lt;/em&gt; list.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Generate report&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt;Verify the &lt;strong&gt;Defer offscreen images&lt;/strong&gt; audit was passed.&lt;/li&gt;
&lt;/ol&gt;
&lt;img alt=&quot;Passing &amp;#x27;Efficiently encode images&amp;#x27; audit in Lighthouse&quot; decoding=&quot;async&quot; height=&quot;774&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/AWMJnCEi3IAgANHhTgiC.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/AWMJnCEi3IAgANHhTgiC.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/AWMJnCEi3IAgANHhTgiC.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/AWMJnCEi3IAgANHhTgiC.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/AWMJnCEi3IAgANHhTgiC.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/AWMJnCEi3IAgANHhTgiC.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/AWMJnCEi3IAgANHhTgiC.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/AWMJnCEi3IAgANHhTgiC.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/AWMJnCEi3IAgANHhTgiC.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/AWMJnCEi3IAgANHhTgiC.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/AWMJnCEi3IAgANHhTgiC.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/AWMJnCEi3IAgANHhTgiC.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/AWMJnCEi3IAgANHhTgiC.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/AWMJnCEi3IAgANHhTgiC.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/AWMJnCEi3IAgANHhTgiC.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/AWMJnCEi3IAgANHhTgiC.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/AWMJnCEi3IAgANHhTgiC.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/AWMJnCEi3IAgANHhTgiC.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Success! You have used lazysizes to lazy load the images on your page.&lt;/p&gt;
</content>
    <author>
      <name>Katie Hempenius</name>
    </author>
  </entry>
</feed>
