<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://web.dev/</id>
  <title>Demian Renzulli on web.dev</title>
  <updated>2026-04-15T23:21:06Z</updated>
  <author>
    <name>Demian Renzulli</name>
  </author>
  <link href="https://web.dev/authors/demianrenzulli/feed.xml" rel="self"/>
  <link href="https://web.dev/"/>
  <icon>https://web-dev.imgix.net/image/admin/EuTrT82fyivFn16L0vD9.jpg?auto=format</icon>
  <logo>https://web.dev/images/shared/rss-banner.png</logo>
  <subtitle>Our latest news, updates, and stories by Demian Renzulli.</subtitle>
  
  
  <entry>
    <title>How Terra improved user engagement thanks to Dark Mode</title>
    <link href="https://web.dev/terra-dark-mode/"/>
    <updated>2021-12-18T00:00:00Z</updated>
    <id>https://web.dev/terra-dark-mode/</id>
    <content type="html" mode="escaped">&lt;p&gt;Terra, one of Brazil&#39;s largest media companies with 75 million monthly users, reduced the bounce rate by 60% and increased the pages read per session by 170% on desktop for users that prefer dark mode by providing a custom dark theme.&lt;/p&gt;
&lt;p&gt;In this article, we&#39;ll analyze Terra&#39;s journey from identifying the size of the &amp;quot;dark mode&amp;quot; cohort, to applying a custom dark theme, and finally measuring the impact of this implementation on their main KPIs.&lt;/p&gt;
&lt;ul class=&quot;stats&quot;&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;60&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;Reduction in Bounce Rates&lt;/p&gt;
  &lt;/div&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;170&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;More pages per session&lt;/p&gt;
  &lt;/div&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-is-dark-mode&quot;&gt;What is dark mode? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/terra-dark-mode/#what-is-dark-mode&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Historically user interfaces in devices are displayed in &amp;quot;light mode&amp;quot;, which usually means displaying black text on top of light interfaces. The alternative is &amp;quot;dark mode&amp;quot;, with light text on a dark background, such as gray or black.&lt;/p&gt;
&lt;p&gt;Dark Mode has &lt;a href=&quot;https://web.dev/prefers-color-scheme/#why-dark-mode&quot;&gt;benefits&lt;/a&gt; for user experience. Some people prefer it for aesthetic or accessibility reasons. It has  other advantages, such as preserving battery life in devices. Users can express that they prefer dark mode via a setting in their devices, &lt;a href=&quot;https://web.dev/prefers-color-scheme/#activating-dark-mode-in-the-operating-system&quot;&gt;which depends on the operating system&lt;/a&gt;. For example, the following screenshot shows what the &lt;strong&gt;Dark Theme&lt;/strong&gt; configuration option looks like in devices that run &lt;strong&gt;Android Q&lt;/strong&gt;:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Android Q dark mode settings.&quot; decoding=&quot;async&quot; height=&quot;250&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 218px) 218px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/Yh6SEoWDK1SbqcGjlL6d.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/Yh6SEoWDK1SbqcGjlL6d.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/Yh6SEoWDK1SbqcGjlL6d.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/Yh6SEoWDK1SbqcGjlL6d.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/Yh6SEoWDK1SbqcGjlL6d.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/Yh6SEoWDK1SbqcGjlL6d.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/Yh6SEoWDK1SbqcGjlL6d.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/Yh6SEoWDK1SbqcGjlL6d.png?auto=format&amp;w=436 436w&quot; style=&quot;max-width: 218px; margin: 0 auto;&quot; width=&quot;218&quot; /&gt;
  &lt;figcaption&gt;Android&amp;nbsp;Q dark theme settings.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To offer a better experience to users who prefer &amp;quot;dark mode&amp;quot;, you can use the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@media/prefers-color-scheme&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;prefers-color-scheme&lt;/code&gt;&lt;/a&gt; media query, with a value of &lt;code&gt;light&lt;/code&gt; or &lt;code&gt;dark&lt;/code&gt;. This media query reflects the user&#39;s choice in their device. You can then load a &lt;a href=&quot;https://web.dev/prefers-color-scheme/#dark-mode-in-practice&quot;&gt;custom dark theme&lt;/a&gt; for those that prefer dark. For example, by loading a &amp;quot;dark&amp;quot; CSS file, and changing values such as font and background colors. The following code shows an example of that:&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;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;prefers-color-scheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dark&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;br /&gt;   // apply a dark theme&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;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;prefers-color-scheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; light&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;br /&gt;  // apply a light theme&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 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 76, 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;
      76
    &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 67, 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;
      67
    &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 12.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;
      12.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/@media/prefers-color-scheme#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; This article will only cover the technique of applying a custom dark theme, provided by the developer. Chrome 96 has introduced an &lt;a href=&quot;https://developer.chrome.com/blog/auto-dark-theme/#sign-up-for-the-origin-trial&quot;&gt;origin trial&lt;/a&gt; for &amp;quot;Auto Dark Themes&amp;quot; on Android, for which the browser applies an automatically generated dark theme to light themed sites, when the user has opted into dark themes in the operating system, without requiring the developer to provide styles for it. For more information about &amp;quot;Chrome Auto Dark Mode&amp;quot;, check out &lt;a href=&quot;https://developer.chrome.com/blog/auto-dark-theme/&quot;&gt;this article&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;identifying-the-prefers-light-vs-dark-user-cohorts&quot;&gt;Identifying the &amp;quot;prefers light&amp;quot; vs &amp;quot;dark&amp;quot; user cohorts &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/terra-dark-mode/#identifying-the-prefers-light-vs-dark-user-cohorts&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At the time of writing (December 2021), &lt;a href=&quot;https://chromestatus.com/features&quot; rel=&quot;noopener&quot;&gt;Chrome Platform Status&lt;/a&gt; determines that approximately &lt;a href=&quot;https://chromestatus.com/metrics/feature/timeline/popularity/3581&quot; rel=&quot;noopener&quot;&gt;22% of the web traffic&lt;/a&gt; comes from users with the &amp;quot;prefer dark&amp;quot; setting.&lt;/p&gt;
&lt;p&gt;This is aggregated data, so the real percentage of users who prefer dark that come to a site can vary. For that reason, to understand the size of this group it is advisable to run in house measurement.&lt;/p&gt;
&lt;p&gt;The following code creates an analytics dimension, to measure the performance of users that prefer &lt;code&gt;light&lt;/code&gt; vs. &lt;code&gt;dark&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getColorScheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; colorScheme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Unknown&#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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;matchMedia&lt;span class=&quot;token punctuation&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;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matchMedia&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;(prefers-color-scheme: dark)&#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;matches&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;            colorScheme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Dark&#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 keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matchMedia&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;(prefers-color-scheme: light)&#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;matches&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;            colorScheme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Light&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; colorScheme&lt;span 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;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ga&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ga&lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ga&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;q&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;ga&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;q&lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token 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;arguments&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;ga&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;l&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;create&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;UA-ID&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;auto&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;ga&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;set&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;color-scheme-preference&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getColorScheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&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;ga&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;send&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;pageview&#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;Terra implemented this code in their site and compared the behavior of both groups in mobile (Android) and desktop (Windows) devices. At that moment Terra wasn&#39;t providing a custom dark theme, so the goals of this experiment were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Determining the size of the group of users who prefer dark.&lt;/li&gt;
&lt;li&gt;Identifying patterns: for example, do users that prefer dark leave the site more quickly compared to those that prefer light?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Knowing this can inform decisions, for example: if it&#39;s necessary to provide a custom dark theme.
These are the results Terra obtained after testing for 14 days:&lt;/p&gt;
&lt;h3 id=&quot;mobile-android&quot;&gt;Mobile (Android) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/terra-dark-mode/#mobile-android&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In the case of mobile (Android) the numbers for bounce rate and pages per session didn&#39;t show big differences between the users that prefer &amp;quot;light&amp;quot;, compared to those that prefer &amp;quot;dark&amp;quot;:&lt;/p&gt;
&lt;div class=&quot;table-wrapper scrollbar&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Display Mode&lt;/th&gt;
        &lt;th&gt;Bounce Rate&lt;/th&gt;
        &lt;th&gt;Pages Per Session&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;Prefers Light&lt;/td&gt;
        &lt;td&gt;25.84%&lt;/td&gt;
        &lt;td&gt;3.96&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Prefers Dark&lt;/td&gt;
        &lt;td&gt;26.10%&lt;/td&gt;
        &lt;td&gt;3.75&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;h3 id=&quot;desktop-windows&quot;&gt;Desktop (Windows) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/terra-dark-mode/#desktop-windows&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In the case of desktop (Windows), the group of users that preferred &amp;quot;dark&amp;quot; stayed much less on the site: they had almost &lt;strong&gt;twice the bounce rate and read a little more than half of the pages&lt;/strong&gt; than those users that preferred &amp;quot;light&amp;quot;:&lt;/p&gt;
&lt;div class=&quot;table-wrapper scrollbar&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Display Mode&lt;/th&gt;
        &lt;th&gt;Bounce Rate&lt;/th&gt;
        &lt;th&gt;Pages Per Session&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;Prefers Light&lt;/td&gt;
        &lt;td&gt;13.20%&lt;/td&gt;
        &lt;td&gt;7.42&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Prefers Dark&lt;/td&gt;
        &lt;td&gt;24.12%&lt;/td&gt;
        &lt;td&gt;4.68&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Based on this data, Terra hypothesized that users who prefer &amp;quot;dark&amp;quot; stay less in desktop devices, due to the lack of support of a dark theme in their site.&lt;/p&gt;
&lt;p&gt;As a next step Terra decided to work on a &amp;quot;dark theme&amp;quot; strategy to see if they could improve the engagement for the group of users that preferred dark.&lt;/p&gt;
&lt;h2 id=&quot;implementing-a-dark-theme&quot;&gt;Implementing a dark theme &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/terra-dark-mode/#implementing-a-dark-theme&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most websites implement a dark theme by using the simple strategy shown previously of listening to user&#39;s configuration changes via the &lt;a href=&quot;https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;prefers-color-scheme&lt;/code&gt;&lt;/a&gt; media query and changing styles based on that.&lt;/p&gt;
&lt;p&gt;Terra decided to give more control to the user: when they detect that they have the &amp;quot;prefer dark&amp;quot; setting turned on in their devices (via the media query), they show them a prompt to ask them if they want to navigate in &amp;quot;night mode&amp;quot;. As long as the user doesn&#39;t deny the prompt (by clicking on the &amp;quot;Ignore&amp;quot; button), they honor the user&#39;s OS-setting, and apply a custom dark theme:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of Terra&amp;#x27;s light theme prompting the user to accept dark mode.&quot; decoding=&quot;async&quot; height=&quot;146&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 266px) 266px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/TRqfCAmBe025456JyX1b.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/TRqfCAmBe025456JyX1b.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/TRqfCAmBe025456JyX1b.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/TRqfCAmBe025456JyX1b.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/TRqfCAmBe025456JyX1b.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/TRqfCAmBe025456JyX1b.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/TRqfCAmBe025456JyX1b.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/TRqfCAmBe025456JyX1b.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/TRqfCAmBe025456JyX1b.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/TRqfCAmBe025456JyX1b.png?auto=format&amp;w=532 532w&quot; style=&quot;max-width: 266px; margin: 0 auto;&quot; width=&quot;266&quot; /&gt;
  &lt;figcaption&gt;Terra shows a prompt to the user asking if they want to navigate in dark mode after detecting that they prefer dark in their devices.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;As a complement of this strategy they provide additional configuration options in the &amp;quot;settings&amp;quot; screen, where the user can decide if they explicitly prefer &amp;quot;light&amp;quot;, &amp;quot;dark&amp;quot;, or want to rely on the underlying device settings.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshots of Terra&amp;#x27;s configuration screen to opt in and out of dark mode.&quot; decoding=&quot;async&quot; height=&quot;417&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 480px) 480px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/B7g0uvq2QB0eWVjnuMAl.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/B7g0uvq2QB0eWVjnuMAl.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/B7g0uvq2QB0eWVjnuMAl.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/B7g0uvq2QB0eWVjnuMAl.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/B7g0uvq2QB0eWVjnuMAl.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/B7g0uvq2QB0eWVjnuMAl.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/B7g0uvq2QB0eWVjnuMAl.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/B7g0uvq2QB0eWVjnuMAl.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/B7g0uvq2QB0eWVjnuMAl.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/B7g0uvq2QB0eWVjnuMAl.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/B7g0uvq2QB0eWVjnuMAl.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/B7g0uvq2QB0eWVjnuMAl.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/B7g0uvq2QB0eWVjnuMAl.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/B7g0uvq2QB0eWVjnuMAl.png?auto=format&amp;w=960 960w&quot; style=&quot;max-width: 480px; margin: 0 auto;&quot; width=&quot;480&quot; /&gt;
  &lt;figcaption&gt;Terra&#39;s themes configurations allow users to choose between &quot;Dark&quot; and &quot;Light&quot; themes or rely on the device&#39;s settings.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This is how Terra&#39;s &amp;quot;Night Mode&amp;quot; looks like:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of Terra&amp;#x27;s dark theme.&quot; decoding=&quot;async&quot; height=&quot;468&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 286px) 286px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QRW06FYeMghUI8obAQWC.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QRW06FYeMghUI8obAQWC.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QRW06FYeMghUI8obAQWC.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QRW06FYeMghUI8obAQWC.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QRW06FYeMghUI8obAQWC.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QRW06FYeMghUI8obAQWC.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QRW06FYeMghUI8obAQWC.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QRW06FYeMghUI8obAQWC.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QRW06FYeMghUI8obAQWC.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QRW06FYeMghUI8obAQWC.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QRW06FYeMghUI8obAQWC.png?auto=format&amp;w=572 572w&quot; style=&quot;max-width: 286px; margin: 0 auto;&quot; width=&quot;286&quot; /&gt;
  &lt;figcaption&gt;Terra&#39;s dark theme, &quot;Night Mode&quot;.&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; We have used mobile screenshots for simplicity, but Terra has applied the same strategy across mobile and desktop devices. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;measuring-the-impact-of-terras-dark-theme&quot;&gt;Measuring the impact of Terra&#39;s dark theme &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/terra-dark-mode/#measuring-the-impact-of-terras-dark-theme&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To measure the impact of the dark theme, Terra took the group of users that have the &amp;quot;Prefer Dark&amp;quot; setting turned on in their devices and compared engagement metrics when showing a &amp;quot;Light&amp;quot; vs. a &amp;quot;DarK&amp;quot; theme.
Here are the results for mobile (Android) and desktop (Windows):&lt;/p&gt;
&lt;h3 id=&quot;mobile-android-2&quot;&gt;Mobile (Android) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/terra-dark-mode/#mobile-android-2&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;While bounce rates remained similar, pages and sessions almost doubled (from 2.47 to 5.24) when users were exposed to a dark theme:&lt;/p&gt;
&lt;div class=&quot;table-wrapper scrollbar&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Display Mode&lt;/th&gt;
        &lt;th&gt;Bounce Rate&lt;/th&gt;
        &lt;th&gt;Pages Per Session&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;Prefers Light&lt;/td&gt;
        &lt;td&gt;26.91%&lt;/td&gt;
        &lt;td&gt;2.47&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Prefers Dark&lt;/td&gt;
        &lt;td&gt;23.91%&lt;/td&gt;
        &lt;td&gt;5.24&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;ul class=&quot;stats&quot;&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;2&lt;sub&gt;X&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;More pages per session&lt;/p&gt;
  &lt;/div&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;desktop-windows-2&quot;&gt;Desktop (Windows) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/terra-dark-mode/#desktop-windows-2&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Both metrics improved when showing a dark theme: bounce rates went from 27.25% to 10.82% and pages per session almost tripled (from 3.7 to 9.99).&lt;/p&gt;
&lt;div class=&quot;table-wrapper scrollbar&quot;&gt;
  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;Display Mode&lt;/th&gt;
        &lt;th&gt;Bounce Rate&lt;/th&gt;
        &lt;th&gt;Pages Per Session&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;Prefers Light&lt;/td&gt;
        &lt;td&gt;27.5%&lt;/td&gt;
        &lt;td&gt;3.7&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;Prefers Dark&lt;/td&gt;
        &lt;td&gt;10.82%&lt;/td&gt;
        &lt;td&gt;9.99&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;ul class=&quot;stats&quot;&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;60&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;Reduction in Bounce Rates&lt;/p&gt;
  &lt;/div&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;170&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;More pages per session&lt;/p&gt;
  &lt;/div&gt;
&lt;/ul&gt;
&lt;p&gt;Based on this data, Terra could confirm the benefits for the users from implementing a dark theme, and has decided to continue maintaining it as a core feature of the site.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/terra-dark-mode/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Dark Mode is a preference that users can turn on in their devices  to change the style of the screens into dark themes. This technique has reported benefits from the user experience and for different aspects of the user&#39;s devices such as saving  battery life.&lt;/p&gt;
&lt;p&gt;In this article we saw how providing a custom dark theme improved engagement metrics for the group of Terra&#39;s users that have the preferred dark mode setting turned on in their devices.&lt;/p&gt;
&lt;p&gt;For companies with the resources to implement an alternative dark theme this is the recommended approach. For those that don&#39;t have the time to invest in such a feature, Chrome is starting to roll out an &lt;a href=&quot;https://developer.chrome.com/blog/auto-dark-theme/&quot; rel=&quot;noopener&quot;&gt;Auto Dark Mode feature&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Demian Renzulli</name>
    </author><author>
      <name>André Cipriani Bandarra</name>
    </author><author>
      <name>Guilherme Moser de Souza</name>
    </author>
  </entry>
  
  <entry>
    <title>Building multiple Progressive Web Apps on the same domain</title>
    <link href="https://web.dev/building-multiple-pwas-on-the-same-domain/"/>
    <updated>2021-06-02T00:00:00Z</updated>
    <id>https://web.dev/building-multiple-pwas-on-the-same-domain/</id>
    <content type="html" mode="escaped">&lt;p&gt;In the &lt;a href=&quot;https://web.dev/multi-origin-pwas/&quot;&gt;Progressive Web Apps in multi-origin sites blog
post&lt;/a&gt;, Demian discussed the challenges that
sites built on multiple origins face when trying to build a single Progressive
Web App that encompasses all of them.&lt;/p&gt;
&lt;p&gt;An example of this type of site architecture is a ecommerce site where:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The home page is at &lt;code&gt;https://www.example.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The category pages are hosted at &lt;code&gt;https://category.example.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The product detail pages at &lt;code&gt;https://product.example.com&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As discussed in the article, the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Security/Same-origin_policy&quot; rel=&quot;noopener&quot;&gt;same-origin
policy&lt;/a&gt;
imposes several restrictions, preventing the sharing of service workers, caches,
and permissions across origins. For that reason, we strongly recommend
avoiding this type of configuration and for those that already have sites
built in this way, to consider migrating to a single origin site architecture
whenever possible.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Diagram showing a site divded into multiple origins and showing that technique is discouraged when building PWAs.&quot; decoding=&quot;async&quot; height=&quot;461&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QnwgUcEBv8o8k2XiAYbm.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QnwgUcEBv8o8k2XiAYbm.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QnwgUcEBv8o8k2XiAYbm.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QnwgUcEBv8o8k2XiAYbm.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QnwgUcEBv8o8k2XiAYbm.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QnwgUcEBv8o8k2XiAYbm.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QnwgUcEBv8o8k2XiAYbm.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QnwgUcEBv8o8k2XiAYbm.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QnwgUcEBv8o8k2XiAYbm.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QnwgUcEBv8o8k2XiAYbm.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QnwgUcEBv8o8k2XiAYbm.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QnwgUcEBv8o8k2XiAYbm.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QnwgUcEBv8o8k2XiAYbm.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QnwgUcEBv8o8k2XiAYbm.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QnwgUcEBv8o8k2XiAYbm.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QnwgUcEBv8o8k2XiAYbm.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QnwgUcEBv8o8k2XiAYbm.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/QnwgUcEBv8o8k2XiAYbm.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Avoid using different origins for site sections of the same site when trying to build a unified Progresive Web App.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In this post, we take a look at the opposite case: instead of a single PWA
across different origins we&#39;ll analyze the case of companies that want to
provide &lt;strong&gt;multiple PWAs&lt;/strong&gt;, taking advantage of the same &lt;strong&gt;domain name&lt;/strong&gt;, and make
the user aware that those PWAs belong to the same organization or service.&lt;/p&gt;
&lt;p&gt;As you might have noticed, we are using different, but interrelated terms, like
domains and origins. Before moving forward, let&#39;s review these concepts.&lt;/p&gt;
&lt;h2 id=&quot;technical-terms&quot;&gt;Technical terms &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/building-multiple-pwas-on-the-same-domain/#technical-terms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Domain:&lt;/strong&gt; Any sequence of labels as defined in the Domain Name System (DNS).
For example: &lt;code&gt;com&lt;/code&gt; and &lt;code&gt;example.com&lt;/code&gt; are domains.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hostname:&lt;/strong&gt; A DNS entry that resolves to at least one IP address. For
example: &lt;code&gt;www.example.com&lt;/code&gt; would be a hostname, &lt;code&gt;example.com&lt;/code&gt; could be a
hostname if it had an IP address, and &lt;code&gt;com&lt;/code&gt; would never resolve to an IP
address and so it could never be a hostname.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Origin:&lt;/strong&gt; A combination of a scheme, hostname and (optionally) port. For
example, &lt;code&gt;https://www.example.com:443&lt;/code&gt; is an origin.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As its name implies, &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Security/Same-origin_policy&quot; rel=&quot;noopener&quot;&gt;same-origin
policy&lt;/a&gt;
imposes restrictions on origins, so we&#39;ll mostly refer to the term throughout
the article. Nevertheless, we&#39;ll use &amp;quot;domains&amp;quot; or &amp;quot;subdomains&amp;quot; from time to
time, to describe the technique being used, in order to create the different
&amp;quot;origins&amp;quot;.&lt;/p&gt;
&lt;h2 id=&quot;the-case-for-multiple,-related-pwas&quot;&gt;The case for multiple, related PWAs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/building-multiple-pwas-on-the-same-domain/#the-case-for-multiple,-related-pwas&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In some cases, you might want to build independent apps, but still identify them
as belonging to the same organization or &amp;quot;brand&amp;quot;. Reusing the same &lt;strong&gt;domain
name&lt;/strong&gt; is a good way of establishing that relationship. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An ecommerce site wants to create a standalone experience to let sellers
manage their inventory, while making sure they understand that it belongs to
the main website where the users buy products.&lt;/li&gt;
&lt;li&gt;A sports news site wants to build a specific app for a major sporting event,
to let users receive stats about their favorite competitions via
notifications, and install it as a Progressive Web App, while making sure that
users recognize it as an app built by the news company.&lt;/li&gt;
&lt;li&gt;A company wants to build separate chat, mail, and calendar apps and wants them
to work as individual apps, tied to the company&#39;s name.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Avoid using different origins for site sections of the same site when trying to build a unified Progresive Web App.&quot; decoding=&quot;async&quot; height=&quot;475&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/XEwhGkkV9Bz7Z2KEJWjm.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/XEwhGkkV9Bz7Z2KEJWjm.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/XEwhGkkV9Bz7Z2KEJWjm.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/XEwhGkkV9Bz7Z2KEJWjm.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/XEwhGkkV9Bz7Z2KEJWjm.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/XEwhGkkV9Bz7Z2KEJWjm.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/XEwhGkkV9Bz7Z2KEJWjm.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/XEwhGkkV9Bz7Z2KEJWjm.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/XEwhGkkV9Bz7Z2KEJWjm.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/XEwhGkkV9Bz7Z2KEJWjm.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/XEwhGkkV9Bz7Z2KEJWjm.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/XEwhGkkV9Bz7Z2KEJWjm.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/XEwhGkkV9Bz7Z2KEJWjm.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/XEwhGkkV9Bz7Z2KEJWjm.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/XEwhGkkV9Bz7Z2KEJWjm.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/XEwhGkkV9Bz7Z2KEJWjm.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/XEwhGkkV9Bz7Z2KEJWjm.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/XEwhGkkV9Bz7Z2KEJWjm.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Company that owns example.com wants to provide three independent apps or PWAs, using the same domain name to establish the relationship between them.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;using-separate-origins&quot;&gt;Using separate origins &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/building-multiple-pwas-on-the-same-domain/#using-separate-origins&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The recommended approach in cases like these is for each conceptually
distinct app live on its own origin.&lt;/p&gt;
&lt;p&gt;If you want to use the same domain name inside all of them, you can do that by
using subdomains. For example, a company that provides multiple internet apps or
services can host a mail app at &lt;code&gt;https://mail.example.com&lt;/code&gt; and a calendar app at
&lt;code&gt;https://calendar.example.com&lt;/code&gt;, while offering the main service of their
business at &lt;code&gt;https://www.example.com&lt;/code&gt;. Another example is a sports site that
wants to create an independent app completely dedicated to an important sports
event, like a football championship at &lt;code&gt;https://footballcup.example.com&lt;/code&gt;, that
users can install and use independently of the main sport site, hosted at
&lt;code&gt;https://www.example.com&lt;/code&gt;. This approach might also be useful for platforms that
let customers create independent apps of their own under the company&#39;s brand.
For example, an app that lets merchants create their own PWAs at
&lt;code&gt;https://merchant1.example.com&lt;/code&gt;, &lt;code&gt;https://merchant2.example.com&lt;/code&gt;, etc.&lt;/p&gt;
&lt;p&gt;Using different origins ensures isolation between the apps, which means that
each of them can manage different browser features independently, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Installability:&lt;/strong&gt; Each app has its own Manifest and provides its own
installable experience.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Storage:&lt;/strong&gt; Each app has its own caches, local storage, and basically all
forms of device-local storage, without sharing them with the others.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Service Workers:&lt;/strong&gt; Each app has its own service worker for the registered
scopes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Permissions:&lt;/strong&gt; Permissions are also scoped by origins. Thanks to that, users
will know exactly which service they are giving permissions for, and
features like notifications will be properly attributed to each app.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Creating such a degree of isolation is the most desirable in the use case of
multiple, independent PWAs, so &lt;strong&gt;we strongly recommend this approach&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If apps on subdomains want to share local data with each other they will still
be able to do it via cookies, or for more advanced scenarios they could
consider synchronizing the storage through a server.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;ALT_TEXT_HERE&quot; decoding=&quot;async&quot; height=&quot;421&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/FfuseK64y4PRqXYEGEt9.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/FfuseK64y4PRqXYEGEt9.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/FfuseK64y4PRqXYEGEt9.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/FfuseK64y4PRqXYEGEt9.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/FfuseK64y4PRqXYEGEt9.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/FfuseK64y4PRqXYEGEt9.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/FfuseK64y4PRqXYEGEt9.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/FfuseK64y4PRqXYEGEt9.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/FfuseK64y4PRqXYEGEt9.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/FfuseK64y4PRqXYEGEt9.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/FfuseK64y4PRqXYEGEt9.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/FfuseK64y4PRqXYEGEt9.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/FfuseK64y4PRqXYEGEt9.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/FfuseK64y4PRqXYEGEt9.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/FfuseK64y4PRqXYEGEt9.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/FfuseK64y4PRqXYEGEt9.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/FfuseK64y4PRqXYEGEt9.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/FfuseK64y4PRqXYEGEt9.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Building different PWAs in distinct origins, by using subdomains is a good practice.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;using-the-same-origin&quot;&gt;Using the same origin &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/building-multiple-pwas-on-the-same-domain/#using-the-same-origin&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The second approach is building the different PWAs on the same origin. This
includes the following scenarios:&lt;/p&gt;
&lt;h3 id=&quot;non-overlapping-paths&quot;&gt;Non-overlapping paths &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/building-multiple-pwas-on-the-same-domain/#non-overlapping-paths&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Multiple PWAs or conceptual &amp;quot;web apps&amp;quot;, hosted on the same origin, with
non-overlapping paths. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;https://example.com/app1/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://example.com/app2/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;overlappingnested-paths&quot;&gt;Overlapping/nested paths &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/building-multiple-pwas-on-the-same-domain/#overlappingnested-paths&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Multiple PWAs on the same origin, one of whose scope is nested inside the other:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;https://example.com/&lt;/code&gt; (the &amp;quot;outer app&amp;quot;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://example.com/app/&lt;/code&gt; (the &amp;quot;inner app&amp;quot;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The service worker API and manifest format allow you to do either of the above,
using path-level scoping. However, in both cases, using the same origin presents
many problems and limitations, the root of which stems from the fact that the
browser won&#39;t fully consider these to be distinct &amp;quot;apps&amp;quot;, therefore &lt;strong&gt;this
approach is discouraged&lt;/strong&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;ALT_TEXT_HERE&quot; decoding=&quot;async&quot; height=&quot;420&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/6HX5zEb58sEEWQoJt82m.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/6HX5zEb58sEEWQoJt82m.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/6HX5zEb58sEEWQoJt82m.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/6HX5zEb58sEEWQoJt82m.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/6HX5zEb58sEEWQoJt82m.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/6HX5zEb58sEEWQoJt82m.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/6HX5zEb58sEEWQoJt82m.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/6HX5zEb58sEEWQoJt82m.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/6HX5zEb58sEEWQoJt82m.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/6HX5zEb58sEEWQoJt82m.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/6HX5zEb58sEEWQoJt82m.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/6HX5zEb58sEEWQoJt82m.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/6HX5zEb58sEEWQoJt82m.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/6HX5zEb58sEEWQoJt82m.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/6HX5zEb58sEEWQoJt82m.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/6HX5zEb58sEEWQoJt82m.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/6HX5zEb58sEEWQoJt82m.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/26V1DWN36MZr3mUo8ChSBlCpzp43/6HX5zEb58sEEWQoJt82m.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Using paths (overlapping or not) to provide two independent PWAs (“app1”, “app2”) under the same origin is discouraged.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In the next section, we analyze these challenges in more detail, and what can be
done, if using separate origins is not an option.&lt;/p&gt;
&lt;h2 id=&quot;challenges-for-multiple,-same-origin-pwas&quot;&gt;Challenges for multiple, same-origin PWAs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/building-multiple-pwas-on-the-same-domain/#challenges-for-multiple,-same-origin-pwas&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here are some practical issues common to both same-origin approaches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Storage:&lt;/strong&gt; Cookies, local storage, and all forms of device-local storage are
shared between apps. For that reason, if the user decides to wipe local data
for one app, it will wipe all the data from the origin; there&#39;s no way
to do this for a single app. Note that Chrome and some other
browsers will actively prompt users to wipe local data when uninstalling one
of the apps, and this will affect data for the other apps on the origin as
well. Another issue is that apps will also have to share their &lt;a href=&quot;https://web.dev/storage-for-the-web/#how-much&quot;&gt;storage
quota&lt;/a&gt; which means if either of
them takes up too much space, the other will be negatively impacted.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Permissions:&lt;/strong&gt; Permissions are tied to the origin. That means if the user
grants a permission to one app, it will apply to all apps on that origin
simultaneously. That may sound like a good thing (not having to ask for a
permission multiple times), but remember: if the user blocks permission to one
app, it will prevent the others from requesting that permission or using that
feature.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User settings:&lt;/strong&gt; Settings are also set per-origin. For example, if two apps
have different font sizes, and the user wants to adjust the zoom in only one
of them to compensate for it, they won&#39;t be able to do it without applying the
setting to the other apps as well.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These challenges make it difficult to encourage this approach. Nevertheless, if
you can&#39;t use a separate origin (e.g. a subdomain), as discussed in the &lt;a href=&quot;https://web.dev/building-multiple-pwas-on-the-same-domain/#using-separate-origins&quot;&gt;Using
separate origins&lt;/a&gt; section, from the two same-origin options we presented,
using non-overlapping paths is strongly recommended, over
overlapping/nested paths.&lt;/p&gt;
&lt;p&gt;As mentioned, the challenges discussed in this section are common to both
same-origin approaches. In the next section we&#39;ll go deeper into the details
of why using overlapping/nested paths is the least recommended strategy.&lt;/p&gt;
&lt;h2 id=&quot;additional-challenges-for-overlappingnested-paths&quot;&gt;Additional challenges for overlapping/nested paths &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/building-multiple-pwas-on-the-same-domain/#additional-challenges-for-overlappingnested-paths&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The additional issue with overlapping/nested paths approach (where
&lt;code&gt;https://example.com/&lt;/code&gt; is the outer app and  &lt;code&gt;https://example.com/app/&lt;/code&gt; is the
inner app), is that all URLs in the inner app will actually be considered
part of both the outer app and the inner app.&lt;/p&gt;
&lt;p&gt;In practice this presents the following issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Installation Promotion:&lt;/strong&gt; If the user visits the inner app (for example,
in a web browser), when the outer app is already installed in the user&#39;s
device, the browser won&#39;t show the install promotional banners, and the
&lt;a href=&quot;https://web.dev/customize-install/&quot;&gt;BeforeInstallPrompt event&lt;/a&gt; won&#39;t be
triggered. The reason is that the browser will check and see whether the
current page belongs to an app that&#39;s already installed, and it will conclude
that it is. The workaround for this is to install the inner app manually
(via &amp;quot;Create Shortcut&amp;quot; browser menu option), or to install the inner app
first, before the outer app.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/notification&quot; rel=&quot;noopener&quot;&gt;Notification&lt;/a&gt;
and the &lt;a href=&quot;https://web.dev/badging-api/&quot;&gt;Badging API&lt;/a&gt;&lt;/strong&gt;: If the outer app is
installed but the inner app is not, notifications and badges coming from the
inner app will be erroneously attributed to the outer app (which is the
nearest enclosing scope of an installed app). This feature works properly in
the case where both apps are installed on the user&#39;s device.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/WICG/sw-launch/blob/main/declarative_link_capturing.md&quot; rel=&quot;noopener&quot;&gt;Link
Capturing&lt;/a&gt;&lt;/strong&gt;:
The outer app may capture URLs that belong to the inner app. This is
especially likely if the outer app is installed but the inner app isn&#39;t.
Similarly, links within the outer app that link to the inner app will not
link capture into the inner app, since they are considered to be within the
outer app&#39;s scope. Additionally, on ChromeOS and Android, if these apps are
added to the Play Store (as &lt;a href=&quot;https://developer.chrome.com/docs/android/trusted-web-activity/overview/&quot; rel=&quot;noopener&quot;&gt;Trusted Web
Activities&lt;/a&gt;),
the outer app will capture all links. Even if the inner app is installed,
the OS will still offer the user the choice of opening them in the outer
app.&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/building-multiple-pwas-on-the-same-domain/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this article we looked at different ways in which developers can build
multiple Progressive Web Apps related to each other within the same &lt;strong&gt;domain&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In summary, we strongly recommend using a different origin (e.g. by using
subdomains) to host independent PWAs. Hosting them in the same origin presents
many challenges, mainly because the browser won&#39;t fully consider
these to be distinct apps.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Separate origins: &lt;strong&gt;Recommended&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Same origin, non-overlapping paths: &lt;strong&gt;Not recommended&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Same origin, overlapping/nested paths: &lt;strong&gt;Strongly not recommended&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If it&#39;s not possible to use different origins, using non-overlapping paths (e.g.
&lt;code&gt;https://example.com/app1/&lt;/code&gt; and &lt;code&gt;https://example.com/app2/&lt;/code&gt; it&#39;s strongly
recommended over using overlapping or nested paths, like &lt;code&gt;https://example.com/&lt;/code&gt;
(for the outer app) and &lt;code&gt;https://example.com/app/&lt;/code&gt; (for the inner app).&lt;/p&gt;
&lt;h2 id=&quot;additional-resources&quot;&gt;Additional resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/building-multiple-pwas-on-the-same-domain/#additional-resources&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/multi-origin-pwas/&quot;&gt;Progressive Web Apps in multi-origin
sites&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With many thanks for their technical reviews and suggestions: &lt;em&gt;Joe Medley,
Dominick Ng, Alan Cutter, Daniel Murphy, Penny McLachlan, Thomas Steiner and
Darwin Huang&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Photo by &lt;a href=&quot;https://unsplash.com/@timmossholder&quot; rel=&quot;noopener&quot;&gt;Tim Mossholder&lt;/a&gt; on
&lt;a href=&quot;https://unsplash.com/photos/GmvH5v9l3K4&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>Demian Renzulli</name>
    </author><author>
      <name>Matt Giuca</name>
    </author><author>
      <name>Chase Phillips</name>
    </author>
  </entry>
  
  <entry>
    <title>Broadcast updates to pages with service workers</title>
    <link href="https://web.dev/broadcast-updates-guide/"/>
    <updated>2020-12-08T00:00:00Z</updated>
    <id>https://web.dev/broadcast-updates-guide/</id>
    <content type="html" mode="escaped">&lt;p&gt;In some scenarios the service worker might need to proactively communicate with any of the active
tabs it controls to inform of a certain event. Examples include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Informing the page when a new version of the service worker has been installed, so that the page
can show an &lt;strong&gt;&amp;quot;Update to refresh&amp;quot;&lt;/strong&gt; button to the user to access the new functionality
immediately.&lt;/li&gt;
&lt;li&gt;Letting the user know about a change on cached data that took place on the service worker side, by
showing an indication, like: &lt;strong&gt;&amp;quot;The app is now ready to work offline&amp;quot;&lt;/strong&gt;, or &lt;strong&gt;&amp;quot;New version of the
content available&amp;quot;&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Diagram showing a service worker communicating with the page to send an update.&quot; decoding=&quot;async&quot; height=&quot;318&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 550px) 550px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/RpZYhHYGpPY9e3AjxuaQ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/RpZYhHYGpPY9e3AjxuaQ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/RpZYhHYGpPY9e3AjxuaQ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/RpZYhHYGpPY9e3AjxuaQ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/RpZYhHYGpPY9e3AjxuaQ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/RpZYhHYGpPY9e3AjxuaQ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/RpZYhHYGpPY9e3AjxuaQ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/RpZYhHYGpPY9e3AjxuaQ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/RpZYhHYGpPY9e3AjxuaQ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/RpZYhHYGpPY9e3AjxuaQ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/RpZYhHYGpPY9e3AjxuaQ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/RpZYhHYGpPY9e3AjxuaQ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/RpZYhHYGpPY9e3AjxuaQ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/RpZYhHYGpPY9e3AjxuaQ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/RpZYhHYGpPY9e3AjxuaQ.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/RpZYhHYGpPY9e3AjxuaQ.png?auto=format&amp;w=1100 1100w&quot; width=&quot;550&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;We&#39;ll call these types of use cases where the service worker doesn&#39;t need to receive a message from
the page to start a communication &lt;strong&gt;&amp;quot;broadcast updates&amp;quot;&lt;/strong&gt;. In this guide we&#39;ll review different
ways of implementing this type of communication between pages and service workers, by using standard
browser APIs and the &lt;a href=&quot;https://developer.chrome.com/docs/workbox/&quot; rel=&quot;noopener&quot;&gt;Workbox library&lt;/a&gt;.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Check out &lt;a href=&quot;https://web.dev/workers-overview/&quot;&gt;Workers overview&lt;/a&gt; for a high-level explanation of when to use web workers versus service workers and the rest of the &lt;a href=&quot;https://web.dev/reliable/#communicate-with-workers&quot;&gt;Communicate with workers&lt;/a&gt; series for guides on other common use cases. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;production-cases&quot;&gt;Production cases &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/broadcast-updates-guide/#production-cases&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;tinder&quot;&gt;Tinder &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/broadcast-updates-guide/#tinder&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tinder PWA uses &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-window/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;workbox-window&lt;/code&gt;&lt;/a&gt; to listen to
important service worker lifecycle moments from the page (&amp;quot;installed&amp;quot;, &amp;quot;controlled&amp;quot; and
&amp;quot;activated&amp;quot;). That way when a new service worker comes into play, it shows an &lt;strong&gt;&amp;quot;Update Available&amp;quot;&lt;/strong&gt;
banner, so that they can refresh the PWA and access the latest features:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of Tinder&amp;#x27;s webapp &amp;#x27;Update Available&amp;#x27; functionality.&quot; decoding=&quot;async&quot; height=&quot;366&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 650px) 650px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/I8TQ9quakuxJc4l6aNvW.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/I8TQ9quakuxJc4l6aNvW.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/I8TQ9quakuxJc4l6aNvW.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/I8TQ9quakuxJc4l6aNvW.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/I8TQ9quakuxJc4l6aNvW.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/I8TQ9quakuxJc4l6aNvW.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/I8TQ9quakuxJc4l6aNvW.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/I8TQ9quakuxJc4l6aNvW.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/I8TQ9quakuxJc4l6aNvW.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/I8TQ9quakuxJc4l6aNvW.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/I8TQ9quakuxJc4l6aNvW.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/I8TQ9quakuxJc4l6aNvW.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/I8TQ9quakuxJc4l6aNvW.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/I8TQ9quakuxJc4l6aNvW.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/I8TQ9quakuxJc4l6aNvW.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/I8TQ9quakuxJc4l6aNvW.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/I8TQ9quakuxJc4l6aNvW.png?auto=format&amp;w=1300 1300w&quot; width=&quot;650&quot; /&gt;
  &lt;figcaption&gt;In the Tinder PWA, the service worker tells the page that a new version is ready, and the page shows users a &quot;Update Available&quot; banner.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;squoosh&quot;&gt;Squoosh &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/broadcast-updates-guide/#squoosh&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In the &lt;a href=&quot;https://squoosh.app/&quot; rel=&quot;noopener&quot;&gt;Squoosh PWA&lt;/a&gt;, when the service worker has cached all of the necessary
assets to make it work offline, it sends a message to the page to show a &amp;quot;Ready to work offline&amp;quot;
toast, letting the user know about the feature:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of Squoosh webapp &amp;#x27;Ready to work offline&amp;#x27; functionality.&quot; decoding=&quot;async&quot; height=&quot;380&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 550px) 550px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tRM8WvCI0aEdVGWGDpLS.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tRM8WvCI0aEdVGWGDpLS.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tRM8WvCI0aEdVGWGDpLS.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tRM8WvCI0aEdVGWGDpLS.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tRM8WvCI0aEdVGWGDpLS.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tRM8WvCI0aEdVGWGDpLS.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tRM8WvCI0aEdVGWGDpLS.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tRM8WvCI0aEdVGWGDpLS.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tRM8WvCI0aEdVGWGDpLS.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tRM8WvCI0aEdVGWGDpLS.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tRM8WvCI0aEdVGWGDpLS.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tRM8WvCI0aEdVGWGDpLS.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tRM8WvCI0aEdVGWGDpLS.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tRM8WvCI0aEdVGWGDpLS.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tRM8WvCI0aEdVGWGDpLS.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tRM8WvCI0aEdVGWGDpLS.png?auto=format&amp;w=1100 1100w&quot; width=&quot;550&quot; /&gt;
  &lt;figcaption&gt;In the Squoosh PWA the service worker broadcasts an update to the page when cache is ready, and the page displays &quot;Ready to work offline&quot; toast.
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;using-workbox&quot;&gt;Using Workbox &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/broadcast-updates-guide/#using-workbox&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;listen-to-service-worker-lifecycle-events&quot;&gt;Listen to service worker lifecycle events &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/broadcast-updates-guide/#listen-to-service-worker-lifecycle-events&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;workbox-window&lt;/code&gt; provides a straightforward interface to listen to &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-window/#important-service-worker-lifecycle-moments&quot; rel=&quot;noopener&quot;&gt;important service worker lifecycle
events&lt;/a&gt;.
Under the hood, the library uses client-side APIs like
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/ServiceWorkerRegistration/onupdatefound&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;updatefound&lt;/code&gt;&lt;/a&gt;
and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/ServiceWorker/onstatechange&quot; rel=&quot;noopener&quot;&gt;statechange&lt;/a&gt;
and provides higher level event listeners in the &lt;code&gt;workbox-window&lt;/code&gt; object, making it easier for the
user to consume these events.&lt;/p&gt;
&lt;p&gt;The following page code lets you detect every time a new version of the service worker is installed,
so you can communicate it to the user:&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; wb &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;Workbox&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/sw.js&#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;wb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;installed&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isUpdate&lt;span class=&quot;token punctuation&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;// Show &quot;Update App&quot; banner&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;wb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&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;inform-the-page-of-changes-in-cache-data&quot;&gt;Inform the page of changes in cache data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/broadcast-updates-guide/#inform-the-page-of-changes-in-cache-data&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Workbox package
&lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-broadcast-update/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;workbox-broadcast-update&lt;/code&gt;&lt;/a&gt;
provides a standard way of notifying window clients that a cached response has been updated. This is
most commonly used along with the &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-strategies/#stale-while-revalidate&quot; rel=&quot;noopener&quot;&gt;StaleWhileRevalidate
strategy&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To broadcast updates add a &lt;code&gt;broadcastUpdate.BroadcastUpdatePlugin&lt;/code&gt; to your strategy options in the
service worker side:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;registerRoute&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;workbox-routing&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;StaleWhileRevalidate&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;workbox-strategies&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;BroadcastUpdatePlugin&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;workbox-broadcast-update&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;registerRoute&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 parameter&quot;&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&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pathname&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/api/&#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;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StaleWhileRevalidate&lt;/span&gt;&lt;span class=&quot;token punctuation&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;BroadcastUpdatePlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&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;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;In your web app, you can listen for these events like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;serviceWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message&#39;&lt;/span&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 parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Optional: ensure the message came from workbox-broadcast-update&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;meta &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;workbox-broadcast-update&#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; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;cacheName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; updatedUrl&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;payload&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;// Do something with cacheName and updatedUrl.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// For example, get the cached content and update&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// the content on the page.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cache &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cacheName&lt;span class=&quot;token punctuation&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; updatedResponse &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;updatedUrl&lt;span class=&quot;token punctuation&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; updatedText &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; updatedResponse&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;using-browser-apis&quot;&gt;Using browser APIs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/broadcast-updates-guide/#using-browser-apis&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If the functionality that Workbox provides is not enough for your needs, use the following browser
APIs to implement &lt;strong&gt;&amp;quot;broadcast updates&amp;quot;&lt;/strong&gt;:&lt;/p&gt;
&lt;h3 id=&quot;broadcast-channel-api&quot;&gt;Broadcast Channel API &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/broadcast-updates-guide/#broadcast-channel-api&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The service worker creates a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/BroadcastChannel&quot; rel=&quot;noopener&quot;&gt;BroadcastChannel
object&lt;/a&gt; and starts sending
messages to it. Any context (e.g. page) interested in receiving these messages can instantiate a
&lt;code&gt;BroadcastChannel&lt;/code&gt; object and implement a message handler to receive messages.&lt;/p&gt;
&lt;p&gt;To inform the page when a new service worker is installed, use the following code:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Create Broadcast Channel to send messages to the page&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; broadcast &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;BroadcastChannel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;sw-update-channel&#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;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;install&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Inform the page every time a new service worker is installed&lt;/span&gt;&lt;br /&gt;  broadcast&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&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;CRITICAL_SW_UPDATE&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The page listens to these events by subscribing to the &lt;code&gt;sw-update-channel&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 comment&quot;&gt;// Create Broadcast Channel and listen to messages sent to it&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; broadcast &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;BroadcastChannel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;sw-update-channel&#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;broadcast&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onmessage&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 parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;CRITICAL_SW_UPDATE&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Show &quot;update to refresh&quot; banner to the user.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This is a simple technique, but its limitation is browser support: at the moment of this writing,
&lt;a href=&quot;https://caniuse.com/?search=Broadcastchannel&quot; rel=&quot;noopener&quot;&gt;Safari doesn&#39;t support this API&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;client-api&quot;&gt;Client API &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/broadcast-updates-guide/#client-api&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Client&quot; rel=&quot;noopener&quot;&gt;Client API&lt;/a&gt; provides a straightforward
way of communicating with multiple clients from the service worker by iterating over an array of
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Client&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Client&lt;/code&gt;&lt;/a&gt; objects.&lt;/p&gt;
&lt;p&gt;Use the following service worker code to send a message to the last focused tab:&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;// Obtain an array of Window client objects&lt;/span&gt;&lt;br /&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matchAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;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 parameter&quot;&gt;clients&lt;/span&gt;&lt;span class=&quot;token punctuation&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;clients &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; clients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Respond to last focused tab&lt;/span&gt;&lt;br /&gt;    clients&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&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;MSG_ID&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The page implements a message handler to intercept these messages:&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;// Listen to messages&lt;/span&gt;&lt;br /&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;serviceWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onmessage&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 parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;     &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;MSG_ID&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;         &lt;span class=&quot;token comment&quot;&gt;// Process response&lt;/span&gt;&lt;br /&gt;   &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Client API is a great option for cases like broadcasting information to multiple active tabs. The
API is supported by all major browsers, but not all of its methods are. Check browser support before
using it.&lt;/p&gt;
&lt;h3 id=&quot;message-channel&quot;&gt;Message Channel &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/broadcast-updates-guide/#message-channel&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Channel_Messaging_API&quot; rel=&quot;noopener&quot;&gt;Message Channel&lt;/a&gt; requires
an initial configuration step, by passing a port from the page to the service worker, to establish a
communication channel between them. The page instantiates a &lt;code&gt;MessageChannel&lt;/code&gt; object and passes a
port to the service worker, via the &lt;code&gt;postMessage()&lt;/code&gt; interface:&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; messageChannel &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;MessageChannel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span 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;// Init port&lt;/span&gt;&lt;br /&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;serviceWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;controller&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&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;PORT_INITIALIZATION&#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;  messageChannel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;port2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The page listens to messages by implementing an &amp;quot;onmessage&amp;quot; handler on that port:&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;// Listen to messages&lt;/span&gt;&lt;br /&gt;messageChannel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;port1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onmessage&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 parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Process message&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 service worker receives the port and saves a reference to it:&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;// Initialize&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; communicationPort&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;PORT_INITIALIZATION&#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;    communicationPort &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ports&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;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;From that point it can send messages to the page, by calling &lt;code&gt;postMessage()&lt;/code&gt; in the reference to the
port:&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;// Communicate&lt;/span&gt;&lt;br /&gt;communicationPort&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&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;MSG_ID&#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;&lt;code&gt;MessageChannel&lt;/code&gt; might be more complex to implement, due to the need of initializing ports, but it&#39;s
supported by &lt;a href=&quot;https://caniuse.com/?search=channel%20messaging&quot; rel=&quot;noopener&quot;&gt;all major browsers&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next steps &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/broadcast-updates-guide/#next-steps&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this guide we explored one particular case of Window to service worker communication:
&lt;strong&gt;&amp;quot;broadcast updates&amp;quot;&lt;/strong&gt;. The examples explored include listening to important service worker
lifecycle events, and communicating to the page about changes in content or cached data. You can think
of more interesting use cases where the service worker proactively communicates with the page,
without receiving any message previously.&lt;/p&gt;
&lt;p&gt;For more patterns of Window and service worker communication check out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/imperative-caching-guide&quot;&gt;Imperative caching guide&lt;/a&gt;: Calling a service worker from the page to
cache resources in advance (e.g. in prefetching scenarios).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/two-way-communication-guide&quot;&gt;Two-way communication&lt;/a&gt;: Delegating a task to a service worker (e.g.
a heavy download), and keeping the page informed on the progress.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;additional-resources&quot;&gt;Additional resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/broadcast-updates-guide/#additional-resources&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-window/&quot; rel=&quot;noopener&quot;&gt;workbox-window&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-broadcast-update/&quot; rel=&quot;noopener&quot;&gt;workbox-broadcast-update&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/google-developer-experts/workbox-4-implementing-refresh-to-update-version-flow-using-the-workbox-window-module-41284967e79c&quot; rel=&quot;noopener&quot;&gt;Workbox 4: Implementing refresh-to-update-version flow using the workbox-window
module&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Demian Renzulli</name>
    </author><author>
      <name>Andrew Guan</name>
    </author>
  </entry>
  
  <entry>
    <title>Imperative caching guide</title>
    <link href="https://web.dev/imperative-caching-guide/"/>
    <updated>2020-12-08T00:00:00Z</updated>
    <id>https://web.dev/imperative-caching-guide/</id>
    <content type="html" mode="escaped">&lt;p&gt;Some websites might need to communicate to the service worker without the need of being
informed about the result. Here are some examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A page sends the service worker a list of URLs &lt;a href=&quot;https://web.dev/instant-navigation-experiences/&quot;&gt;to
prefetch&lt;/a&gt;, so that, when the user clicks on a
link the document or page subresources are already available in the cache, making subsequent
navigation much faster.&lt;/li&gt;
&lt;li&gt;The page asks the service worker to retrieve and cache a set of top articles, to have them
available for offline purposes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Delegating these types of non-critical tasks to the service worker has the benefit of freeing up the
main thread for better handling more pressing tasks such as responding to user interactions.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Diagram of a page requesting resources to cache to a service worker.&quot; decoding=&quot;async&quot; height=&quot;264&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 565px) 565px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gCpdKiIbSDZBMJEJE2tZ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gCpdKiIbSDZBMJEJE2tZ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gCpdKiIbSDZBMJEJE2tZ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gCpdKiIbSDZBMJEJE2tZ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gCpdKiIbSDZBMJEJE2tZ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gCpdKiIbSDZBMJEJE2tZ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gCpdKiIbSDZBMJEJE2tZ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gCpdKiIbSDZBMJEJE2tZ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gCpdKiIbSDZBMJEJE2tZ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gCpdKiIbSDZBMJEJE2tZ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gCpdKiIbSDZBMJEJE2tZ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gCpdKiIbSDZBMJEJE2tZ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gCpdKiIbSDZBMJEJE2tZ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gCpdKiIbSDZBMJEJE2tZ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gCpdKiIbSDZBMJEJE2tZ.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gCpdKiIbSDZBMJEJE2tZ.png?auto=format&amp;w=1130 1130w&quot; width=&quot;565&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;In this guide we&#39;ll explore how to implement a &lt;strong&gt;one-way&lt;/strong&gt; communication technique from the page to
the service worker by using standard browser APIs and the Workbox library. We&#39;ll call these types of
use cases &lt;strong&gt;imperative caching&lt;/strong&gt;.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Check out &lt;a href=&quot;https://web.dev/workers-overview/&quot;&gt;Workers overview&lt;/a&gt; for a high-level explanation of when to use web workers versus service workers and the rest of the &lt;a href=&quot;https://web.dev/reliable/#communicate-with-workers&quot;&gt;Communicate with workers&lt;/a&gt; series for guides on other common use cases. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;production-case&quot;&gt;Production case &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/imperative-caching-guide/#production-case&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;1-800-Flowers.com implemented &lt;strong&gt;imperative caching&lt;/strong&gt; (prefetching) with service workers via
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Worker/postMessage&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;postMessage()&lt;/code&gt;&lt;/a&gt; to prefetch the
top items in category pages to speed up subsequent navigation to product detail pages.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Logo of 1-800 Flowers.&quot; decoding=&quot;async&quot; height=&quot;203&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 400px) 400px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eNMKYuaKnlYu0N3IIhI5.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eNMKYuaKnlYu0N3IIhI5.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eNMKYuaKnlYu0N3IIhI5.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eNMKYuaKnlYu0N3IIhI5.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eNMKYuaKnlYu0N3IIhI5.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eNMKYuaKnlYu0N3IIhI5.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eNMKYuaKnlYu0N3IIhI5.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eNMKYuaKnlYu0N3IIhI5.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eNMKYuaKnlYu0N3IIhI5.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eNMKYuaKnlYu0N3IIhI5.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eNMKYuaKnlYu0N3IIhI5.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eNMKYuaKnlYu0N3IIhI5.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eNMKYuaKnlYu0N3IIhI5.png?auto=format&amp;w=800 800w&quot; width=&quot;400&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;They use a mixed approach to decide which items to prefetch:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;At page load time they ask the servicer worker to retrieve the JSON data for the top 9 items, and
add the resulting response objects to the cache.&lt;/li&gt;
&lt;li&gt;For the remaining items, they listen to the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Element/mouseover_event&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;mouseover&lt;/code&gt;
&lt;/a&gt; event, so that, when a
user moves the cursor on top of an item, they can trigger a fetch for the resource on &amp;quot;demand&amp;quot;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;They use the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Cache&quot; rel=&quot;noopener&quot;&gt;Cache API&lt;/a&gt; to store JSON
responses:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Logo of 1-800 Flowers.&quot; decoding=&quot;async&quot; height=&quot;287&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 728px) 728px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FH4clAbGShyIdhj4jWdL.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FH4clAbGShyIdhj4jWdL.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FH4clAbGShyIdhj4jWdL.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FH4clAbGShyIdhj4jWdL.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FH4clAbGShyIdhj4jWdL.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FH4clAbGShyIdhj4jWdL.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FH4clAbGShyIdhj4jWdL.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FH4clAbGShyIdhj4jWdL.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FH4clAbGShyIdhj4jWdL.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FH4clAbGShyIdhj4jWdL.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FH4clAbGShyIdhj4jWdL.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FH4clAbGShyIdhj4jWdL.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FH4clAbGShyIdhj4jWdL.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FH4clAbGShyIdhj4jWdL.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FH4clAbGShyIdhj4jWdL.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FH4clAbGShyIdhj4jWdL.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FH4clAbGShyIdhj4jWdL.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FH4clAbGShyIdhj4jWdL.png?auto=format&amp;w=1456 1456w&quot; width=&quot;728&quot; /&gt;
   &lt;figcaption&gt;Prefetching JSON product data from product listing pages in 1-800Flowers.com.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;When the user clicks on an item, the JSON data associated with it can be picked up from the cache,
without the need of going to the network, making the navigation faster.&lt;/p&gt;
&lt;h2 id=&quot;using-workbox&quot;&gt;Using Workbox &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/imperative-caching-guide/#using-workbox&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/docs/workbox/&quot; rel=&quot;noopener&quot;&gt;Workbox&lt;/a&gt; provides an easy way to send messages to
a service worker, via the &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-window/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;workbox-window&lt;/code&gt;&lt;/a&gt; package, a set of modules
that are intended to run in the window context. They&#39;re a complement to the other Workbox packages
that run in the service worker.&lt;/p&gt;
&lt;p&gt;To communicate the page with the service worker, first obtain a Workbox object reference to the
registered service worker:&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; wb &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;Workbox&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/sw.js&#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;wb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Then you can directly send the message declaratively, without the hassle of the getting the
registration, checking for activation, or thinking about the underlying communication API:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;wb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;messageSW&lt;/span&gt;&lt;span class=&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;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PREFETCH&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-property property&quot;&gt;&quot;payload&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;span class=&quot;token string-property property&quot;&gt;&quot;urls&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;span class=&quot;token string&quot;&gt;&quot;/data1.json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;data2.json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&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 service worker implements a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/ServiceWorkerGlobalScope/message_event&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;message&lt;/code&gt;&lt;/a&gt; handler to
listen to these messages. It can optionally return a response, although, in cases like these, it&#39;s
not necessary:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;PREFETCH&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// do something&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;using-browser-apis&quot;&gt;Using browser APIs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/imperative-caching-guide/#using-browser-apis&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If the Workbox library is not enough for your needs, here is how you can implement window to service
worker communication, using browser APIs.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://html.spec.whatwg.org/multipage/workers.html#dom-worker-postmessage&quot; rel=&quot;noopener&quot;&gt;postMessage API&lt;/a&gt;
can be used to establish a &lt;strong&gt;one-way&lt;/strong&gt; communication mechanism from the page to the service worker.&lt;/p&gt;
&lt;p&gt;The page calls
&lt;a href=&quot;https://html.spec.whatwg.org/multipage/workers.html#dom-worker-postmessage&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;postMessage()&lt;/code&gt;&lt;/a&gt; on the
service worker interface:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;serviceWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;controller&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;MSG_ID&#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;payload&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;some data to perform the task&#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;The service worker implements a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/ServiceWorkerGlobalScope/message_event&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;message&lt;/code&gt;&lt;/a&gt; handler to
listen to these messages.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MSG_ID&lt;/span&gt;&lt;span class=&quot;token punctuation&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 something&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The &lt;code&gt;{type : &#39;MSG_ID&#39;}&lt;/code&gt; attribute is not absolutely required, but it is one way to allow the page to
send different types of instructions to the service worker (that is, &#39;to prefetch&#39; vs. &#39;to clear
storage&#39;). The service worker can branch into different execution paths based on this flag.&lt;/p&gt;
&lt;p&gt;If the operation was successful, the user will be able to get the benefits from it but, if not, it won&#39;t alter the main user flow. For example, when 1-800-Flowers.com attempts to precache, the page doesn&#39;t need to know whether the service worker succeeded. If it does, then the user will enjoy a faster navigation. If it doesn&#39;t the page still needs to navigate to the new page. It&#39;s just going to take a little longer.&lt;/p&gt;
&lt;h3 id=&quot;a-simple-prefetching-example&quot;&gt;A simple prefetching example &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/imperative-caching-guide/#a-simple-prefetching-example&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One of the most common applications of &lt;strong&gt;imperative caching&lt;/strong&gt; is &lt;strong&gt;prefetching&lt;/strong&gt;, meaning fetching
resources for a given URL, before the user moves to it, in order to speed up navigation.&lt;/p&gt;
&lt;p&gt;There are different ways of implementing prefetching in sites:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using &lt;a href=&quot;https://web.dev/link-prefetch/&quot;&gt;Link prefetch tags&lt;/a&gt; in pages: resources are kept in the
browser cache for five minutes, after which the normal &lt;code&gt;Cache-Control&lt;/code&gt; rules for the resource
apply.&lt;/li&gt;
&lt;li&gt;Complementing the previous technique with &lt;a href=&quot;https://web.dev/instant-navigation-experiences/&quot;&gt;a runtime caching strategy in the service
worker&lt;/a&gt; to extend the lifetime of the prefetch
resource beyond this limit.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For relatively simple prefetching scenarios, like prefetching documents, or specific assets (JS,
CSS, etc.), those techniques are the best approach.&lt;/p&gt;
&lt;p&gt;If additional logic is required, for example, parsing the prefetch resource (a JSON file or page) in
order to fetch its internal URLs, it&#39;s more appropriate to delegate this task entirely to the
service worker.&lt;/p&gt;
&lt;p&gt;Delegating these types of operations to the service worker has the following advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Offloading the heavy lifting of fetching and post-fetch processing (which will be introduced
later) to a secondary thread. By doing this, it frees the main thread to handle more important
tasks such as responding to user interactions.&lt;/li&gt;
&lt;li&gt;Allowing multiple clients (e.g. tabs) to reuse a common functionality, and even calling the
service simultaneously without blocking the main thread.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;prefetch-product-detail-pages&quot;&gt;Prefetch product detail pages &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/imperative-caching-guide/#prefetch-product-detail-pages&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;First use &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Worker/postMessage&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;postMessage()&lt;/code&gt;&lt;/a&gt; on
the service worker interface and pass an array of URLs to cache:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;serviceWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;controller&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;PREFETCH&#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;payload&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;urls&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token string&quot;&gt;&#39;www.exmaple.com/apis/data_1.json&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token string&quot;&gt;&#39;www.exmaple.com/apis/data_2.json&#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 punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In the service worker, implement a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/ServiceWorkerGlobalScope/message_event&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;message&lt;/code&gt;&lt;/a&gt; handler to
intercept and process messages sent by any active tab:&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 function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&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 keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;PREFETCH&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; urls &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;urls&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; urls&lt;span class=&quot;token punctuation&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;fetchAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;urls&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span 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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In the previous code we introduced a small helper function called &lt;code&gt;fetchAsync()&lt;/code&gt; to iterate on the
array of URLs and issue a fetch request for each of them:&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;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&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;// await response of fetch call&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; prefetched &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;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 comment&quot;&gt;// (optionally) cache resources in the service worker storage&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 the response is obtained you can rely on the caching headers of the resource. In many cases
though, like in product detail pages, resources are not cached (which means, they have a
&lt;code&gt;Cache-control&lt;/code&gt; header of &lt;code&gt;no-cache&lt;/code&gt;). In cases like these you can override this behavior, by
storing the fetched resource in the service worker cache. This has the added benefit of allowing the
file to be served in offline scenarios.&lt;/p&gt;
&lt;h3 id=&quot;beyond-json-data&quot;&gt;Beyond JSON data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/imperative-caching-guide/#beyond-json-data&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Once the JSON data is fetched from a server endpoint, it often contains other URLs that are also
worth prefetching, such as an image or other endpoint data that are associated with this first-level
data.&lt;/p&gt;
&lt;p&gt;Let&#39;s say that in our example, the JSON data returned is the information of a grocery shopping site:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;productName&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;banana&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;productPic&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://cdn.example.com/product_images/banana.jpeg&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;unitPrice&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1.99&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;Modify the &lt;code&gt;fetchAsync()&lt;/code&gt; code to iterate over the list of products and cache the hero image for
each of them:&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;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; postProcess&lt;/span&gt;&lt;span class=&quot;token punctuation&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;// await response of fetch call&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; prefetched &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;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;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;//(optionally) cache resource in the service worker cache&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// carry out the post fetch process if supplied&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;postProcess&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;postProcess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;prefetched&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;postProcess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;prefetched&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; productJson &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; prefetched&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&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;productJson &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; productJson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;product_pic&lt;span class=&quot;token punctuation&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;fetchAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;productJson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;product_pic&lt;span class=&quot;token punctuation&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 can add some exception handling around this code for situations like 404s.
But the beauty of using a service worker to prefetch is that it can fail without much
consequence to the page and the main thread. You may also have more elaborate logic in the
post-processing of the prefetched content, making it more flexible and decoupled with the data it&#39;s
handling. The sky&#39;s the limit.&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; Prefetching techniques consume extra bytes for resources that are not immediately needed, so it needs to be applied thoughtfully; only prefetch resources when you are confident that users will need them. Avoid prefetching when users are on slow connections. You can detect that with the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Network_Information_API&quot;&gt;Network Information API&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/imperative-caching-guide/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this article, we covered a common use case of &lt;strong&gt;one-way&lt;/strong&gt; communication between page and service
worker: &lt;strong&gt;imperative caching&lt;/strong&gt;. The examples discussed are only meant for demonstrating one way of
using this pattern and the same approach can be applied to other use cases as well, for example,
caching top articles on demand for offline consumption, bookmarking, and others.&lt;/p&gt;
&lt;p&gt;For more patterns of page and service worker communication, check out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/broadcast-updates-guide&quot;&gt;Broadcast updates&lt;/a&gt;: Calling the page from the service worker to inform
about important updates (e.g. a new version of the webapp is available).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/two-way-communication-guide&quot;&gt;Two-way communication&lt;/a&gt;: Delegating a task to a service worker (e.g.
a heavy download), and keeping the page informed on the progress.&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Demian Renzulli</name>
    </author><author>
      <name>Andrew Guan</name>
    </author>
  </entry>
  
  <entry>
    <title>Two-way communication with service workers</title>
    <link href="https://web.dev/two-way-communication-guide/"/>
    <updated>2020-12-08T00:00:00Z</updated>
    <id>https://web.dev/two-way-communication-guide/</id>
    <content type="html" mode="escaped">&lt;p&gt;In some cases, a web app might need to establish a &lt;strong&gt;two-way&lt;/strong&gt; communication channel between the
page and the service worker.&lt;/p&gt;
&lt;p&gt;For example: in a podcast PWA one could build a feature to let the user &lt;a href=&quot;https://web.dev/app-like-pwas/#proactive-background-downloading&quot;&gt;download episodes for
offline consumption&lt;/a&gt; and allow the
service worker to keep the page regularly informed about the progress, so the &lt;a href=&quot;https://developer.mozilla.org/docs/Glossary/Main_thread&quot; rel=&quot;noopener&quot;&gt;main
thread&lt;/a&gt; can update the UI.&lt;/p&gt;
&lt;p&gt;In this guide we&#39;ll explore the different ways of implementing a &lt;strong&gt;two-way&lt;/strong&gt; communication between
the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Window&quot; rel=&quot;noopener&quot;&gt;Window&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Service_Worker_API&quot; rel=&quot;noopener&quot;&gt;service
worker&lt;/a&gt; context, by exploring
different APIs, the &lt;a href=&quot;https://developer.chrome.com/docs/workbox/&quot; rel=&quot;noopener&quot;&gt;Workbox library&lt;/a&gt;, as well as
some advanced cases.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Diagram showing a service worker and the page exchanging messages.&quot; decoding=&quot;async&quot; height=&quot;310&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/HIbZyXbQNijm1S4eQlEv.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/HIbZyXbQNijm1S4eQlEv.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/HIbZyXbQNijm1S4eQlEv.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/HIbZyXbQNijm1S4eQlEv.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/HIbZyXbQNijm1S4eQlEv.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/HIbZyXbQNijm1S4eQlEv.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/HIbZyXbQNijm1S4eQlEv.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/HIbZyXbQNijm1S4eQlEv.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/HIbZyXbQNijm1S4eQlEv.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/HIbZyXbQNijm1S4eQlEv.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/HIbZyXbQNijm1S4eQlEv.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/HIbZyXbQNijm1S4eQlEv.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/HIbZyXbQNijm1S4eQlEv.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/HIbZyXbQNijm1S4eQlEv.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/HIbZyXbQNijm1S4eQlEv.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/HIbZyXbQNijm1S4eQlEv.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/HIbZyXbQNijm1S4eQlEv.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/HIbZyXbQNijm1S4eQlEv.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Check out &lt;a href=&quot;https://web.dev/workers-overview/&quot;&gt;Workers overview&lt;/a&gt; for a high-level explanation of when to use web workers versus service workers and the rest of the &lt;a href=&quot;https://web.dev/reliable/#communicate-with-workers&quot;&gt;Communicate with workers&lt;/a&gt; series for guides on other common use cases. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;using-workbox&quot;&gt;Using Workbox &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/two-way-communication-guide/#using-workbox&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-window/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;workbox-window&lt;/code&gt;&lt;/a&gt; is a set of
modules of the &lt;a href=&quot;https://developer.chrome.com/docs/workbox/&quot; rel=&quot;noopener&quot;&gt;Workbox library&lt;/a&gt; that are intended
to run in the window context. The &lt;a href=&quot;https://developer.chrome.com/docs/workbox/reference/workbox-window/#type-Workbox&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Workbox&lt;/code&gt;
&lt;/a&gt; class provides a &lt;code&gt;messageSW()&lt;/code&gt; method to send a message to the instance&#39;s registered service worker and
await a response.&lt;/p&gt;
&lt;p&gt;The following page code creates a new &lt;code&gt;Workbox&lt;/code&gt; instance and sends a message to the service worker
to obtain its version:&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; wb &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;Workbox&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/sw.js&#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;wb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; swVersion &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; wb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;messageSW&lt;/span&gt;&lt;span class=&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;GET_VERSION&#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;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;Service Worker version:&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; swVersion&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 service worker implements a message listener on the other end, and responds to the registered
service worker:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;SW_VERSION&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;1.0.0&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;GET_VERSION&#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;    event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ports&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;SW_VERSION&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Under the hood the library uses a browser API that we&#39;ll review in the next section: &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Channel_Messaging_API&quot; rel=&quot;noopener&quot;&gt;Message
Channel&lt;/a&gt;, but abstracts many
implementation details, making it easier to use, while leveraging the &lt;a href=&quot;https://caniuse.com/mdn-api_messagechannel_port1&quot; rel=&quot;noopener&quot;&gt;wide browser
support&lt;/a&gt; this API has.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Diagram showing two-way communication between page and service worker, using Workbox Window.&quot; decoding=&quot;async&quot; height=&quot;410&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 700px) 700px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DxyWzq96JQCU3hADZiN8.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DxyWzq96JQCU3hADZiN8.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DxyWzq96JQCU3hADZiN8.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DxyWzq96JQCU3hADZiN8.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DxyWzq96JQCU3hADZiN8.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DxyWzq96JQCU3hADZiN8.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DxyWzq96JQCU3hADZiN8.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DxyWzq96JQCU3hADZiN8.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DxyWzq96JQCU3hADZiN8.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DxyWzq96JQCU3hADZiN8.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DxyWzq96JQCU3hADZiN8.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DxyWzq96JQCU3hADZiN8.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DxyWzq96JQCU3hADZiN8.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DxyWzq96JQCU3hADZiN8.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DxyWzq96JQCU3hADZiN8.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DxyWzq96JQCU3hADZiN8.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DxyWzq96JQCU3hADZiN8.png?auto=format&amp;w=1400 1400w&quot; width=&quot;700&quot; /&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;using-browser-apis&quot;&gt;Using Browser APIs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/two-way-communication-guide/#using-browser-apis&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If the Workbox library is not enough for your needs, there are several lower-level APIs available to
implement &lt;strong&gt;&amp;quot;two-way&amp;quot;&lt;/strong&gt; communication between pages and service workers. They have some similarities
and differences:&lt;/p&gt;
&lt;p&gt;Similarities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In all cases the communication starts on one end via the &lt;code&gt;postMessage()&lt;/code&gt; interface and is received
on the other end by implementing a &lt;code&gt;message&lt;/code&gt; handler.&lt;/li&gt;
&lt;li&gt;In practice, all the available APIs allow us to implement the same use cases, but some of them
might simplify development in some scenarios.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Differences:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They have different ways of identifying the other side of the communication: some of them use an
explicit reference to the other context, while others can communicate implicitly via a proxy
object instantiated on each side.&lt;/li&gt;
&lt;li&gt;Browser support varies among them.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Diagram showing two-way communication between page and service worker, and the available browser APIs.&quot; decoding=&quot;async&quot; height=&quot;273&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 600px) 600px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/nPYcdQbxAezigMiuoLJg.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/nPYcdQbxAezigMiuoLJg.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/nPYcdQbxAezigMiuoLJg.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/nPYcdQbxAezigMiuoLJg.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/nPYcdQbxAezigMiuoLJg.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/nPYcdQbxAezigMiuoLJg.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/nPYcdQbxAezigMiuoLJg.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/nPYcdQbxAezigMiuoLJg.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/nPYcdQbxAezigMiuoLJg.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/nPYcdQbxAezigMiuoLJg.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/nPYcdQbxAezigMiuoLJg.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/nPYcdQbxAezigMiuoLJg.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/nPYcdQbxAezigMiuoLJg.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/nPYcdQbxAezigMiuoLJg.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/nPYcdQbxAezigMiuoLJg.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/nPYcdQbxAezigMiuoLJg.png?auto=format&amp;w=1200 1200w&quot; width=&quot;600&quot; /&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;broadcast-channel-api&quot;&gt;Broadcast Channel API &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/two-way-communication-guide/#broadcast-channel-api&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;wdi-browser-compat&quot;&gt;
  &lt;span class=&quot;wdi-browser-compat__label&quot;&gt;Browser support&lt;/span&gt;
  &lt;ul class=&quot;wdi-browser-compat__items&quot;&gt;
    &lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;chrome&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Chrome 54, 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;
      54
    &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 38, 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;
      38
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Edge 79, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      79
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Safari 15.4, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      15.4
    &lt;/span&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/BroadcastChannel#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Broadcast_Channel_API&quot; rel=&quot;noopener&quot;&gt;Broadcast Channel API&lt;/a&gt;
allows basic communication between browsing contexts via &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/BroadcastChannel&quot; rel=&quot;noopener&quot;&gt;BroadcastChannel
objects&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To implement it, first, each context has to instantiate a &lt;code&gt;BroadcastChannel&lt;/code&gt; object with the same ID
and send and receive messages from it:&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; broadcast &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;BroadcastChannel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;channel-123&#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;The BroadcastChannel object exposes a &lt;code&gt;postMessage()&lt;/code&gt; interface to send a message to any listening
context:&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;//send message&lt;/span&gt;&lt;br /&gt;broadcast&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&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;MSG_ID&#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 punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Any browser context can listen to messages via the &lt;code&gt;onmessage&lt;/code&gt; method of the &lt;code&gt;BroadcastChannel&lt;/code&gt;
object:&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;//listen to messages&lt;/span&gt;&lt;br /&gt;broadcast&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onmessage&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 parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;MSG_ID&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;//process message...&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;As seen, there&#39;s no explicit reference to a particular context, so there&#39;s no need of obtaining a
reference first to the service worker or any particular client.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Diagram showing two-way communication between page and service worker, using a Broadcast Channel object.&quot; decoding=&quot;async&quot; height=&quot;355&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 700px) 700px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uWl3NVD3rxbSoA0JxvuN.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uWl3NVD3rxbSoA0JxvuN.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uWl3NVD3rxbSoA0JxvuN.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uWl3NVD3rxbSoA0JxvuN.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uWl3NVD3rxbSoA0JxvuN.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uWl3NVD3rxbSoA0JxvuN.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uWl3NVD3rxbSoA0JxvuN.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uWl3NVD3rxbSoA0JxvuN.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uWl3NVD3rxbSoA0JxvuN.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uWl3NVD3rxbSoA0JxvuN.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uWl3NVD3rxbSoA0JxvuN.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uWl3NVD3rxbSoA0JxvuN.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uWl3NVD3rxbSoA0JxvuN.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uWl3NVD3rxbSoA0JxvuN.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uWl3NVD3rxbSoA0JxvuN.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uWl3NVD3rxbSoA0JxvuN.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uWl3NVD3rxbSoA0JxvuN.png?auto=format&amp;w=1400 1400w&quot; width=&quot;700&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;The disadvantage is that, at the moment of this writing, the API has support from Chrome, Firefox
and Edge, but other browsers, like Safari, &lt;a href=&quot;https://caniuse.com/?search=broadcastchannel&quot; rel=&quot;noopener&quot;&gt;don&#39;t support it
yet&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;channel-api&quot;&gt;Client API &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/two-way-communication-guide/#channel-api&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;wdi-browser-compat&quot;&gt;
  &lt;span class=&quot;wdi-browser-compat__label&quot;&gt;Browser support&lt;/span&gt;
  &lt;ul class=&quot;wdi-browser-compat__items&quot;&gt;
    &lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;chrome&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Chrome 40, 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;
      40
    &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 44, 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;
      44
    &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 17, 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;
      17
    &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/API/Client#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Client&quot; rel=&quot;noopener&quot;&gt;Client API&lt;/a&gt; allows you to obtain a
reference to all the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/WindowClient&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;WindowClient&lt;/code&gt;&lt;/a&gt; objects representing the active tabs that the service worker is controlling.&lt;/p&gt;
&lt;p&gt;Since the page is controlled by a single service worker, it listens to and sends messages to the
active service worker directly via the &lt;code&gt;serviceWorker&lt;/code&gt; interface:&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;//send message&lt;/span&gt;&lt;br /&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;serviceWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;controller&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;MSG_ID&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;//listen to messages&lt;/span&gt;&lt;br /&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;serviceWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onmessage&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 parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;MSG_ID&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;//process response&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Similarly, the service worker listens to messages by implementing an &lt;code&gt;onmessage&lt;/code&gt; listener:&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;//listen to messages&lt;/span&gt;&lt;br /&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;MSG_ID&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;//Process message&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;To communicate back with any of its clients, the service worker obtains an array of
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/WindowClient&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;WindowClient&lt;/code&gt;&lt;/a&gt; objects by executing
methods such as
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Clients/matchAll&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Clients.matchAll()&lt;/code&gt;&lt;/a&gt; and
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Clients/get&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Clients.get()&lt;/code&gt;&lt;/a&gt;. Then it can
&lt;code&gt;postMessage()&lt;/code&gt; any of them:&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;//Obtain an array of Window client objects&lt;/span&gt;&lt;br /&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matchAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;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 parameter&quot;&gt;clients&lt;/span&gt;&lt;span class=&quot;token punctuation&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;clients &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; clients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;//Respond to last focused tab&lt;/span&gt;&lt;br /&gt;    clients&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&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;MSG_ID&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;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;figure&gt;
  &lt;img alt=&quot;Diagram showing a service worker communicating with an array of clients.&quot; decoding=&quot;async&quot; height=&quot;348&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oApyHsmljr1KkBW85Wv9.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oApyHsmljr1KkBW85Wv9.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oApyHsmljr1KkBW85Wv9.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oApyHsmljr1KkBW85Wv9.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oApyHsmljr1KkBW85Wv9.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oApyHsmljr1KkBW85Wv9.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oApyHsmljr1KkBW85Wv9.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oApyHsmljr1KkBW85Wv9.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oApyHsmljr1KkBW85Wv9.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oApyHsmljr1KkBW85Wv9.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oApyHsmljr1KkBW85Wv9.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oApyHsmljr1KkBW85Wv9.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oApyHsmljr1KkBW85Wv9.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oApyHsmljr1KkBW85Wv9.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oApyHsmljr1KkBW85Wv9.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;Client API&lt;/code&gt; is a good option to communicate easily with all the active tabs from a service worker
in a relatively straightforward way. The API is supported by &lt;a href=&quot;https://wpt.fyi/results/service-workers/service-worker/client-id.https.html?label=experimental&amp;amp;label=master&amp;amp;aligned&quot; rel=&quot;noopener&quot;&gt;all major
browsers&lt;/a&gt;,
but not all its methods might be available, so make sure to check browser support before
implementing it in your site.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; In this guide we focus mostly on reliability use cases. Another interesting usage of this API is synchronizing data across documents via a service worker. Check out &lt;a href=&quot;https://www.youtube.com/watch?v=9UNwHmagedE&amp;amp;feature=youtu.be&amp;amp;t=697&quot;&gt;this episode of HTTP 203&lt;/a&gt; to know more about it. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;message-channel&quot;&gt;Message Channel &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/two-way-communication-guide/#message-channel&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;wdi-browser-compat&quot;&gt;
  &lt;span class=&quot;wdi-browser-compat__label&quot;&gt;Browser support&lt;/span&gt;
  &lt;ul class=&quot;wdi-browser-compat__items&quot;&gt;
    &lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;chrome&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Chrome 2, 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;
      2
    &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 41, 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;
      41
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Edge 12, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      12
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Safari 5, 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;
      5
    &lt;/span&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/MessageChannel#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Channel_Messaging_API&quot; rel=&quot;noopener&quot;&gt;Message Channel&lt;/a&gt; requires
defining and passing a port from one context to another to establish a &lt;strong&gt;two-way&lt;/strong&gt; communication
channel.&lt;/p&gt;
&lt;p&gt;To initialize the channel, the page instantiates a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/MessageChannel/MessageChannel&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;MessageChannel&lt;/code&gt;&lt;/a&gt; object and uses it
to send a port to the registered service worker. The page also implements an &lt;code&gt;onmessage&lt;/code&gt; listener on
it to receive messages from the other context:&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; messageChannel &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;MessageChannel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span 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;//Init port&lt;/span&gt;&lt;br /&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;serviceWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;controller&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&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;PORT_INITIALIZATION&#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;  messageChannel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;port2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;//Listen to messages&lt;/span&gt;&lt;br /&gt;messageChannel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;port1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onmessage&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 parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Process message&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;figure&gt;
  &lt;img alt=&quot;Diagram showing a page passing a port to a service worker, to establish two-way communication.&quot; decoding=&quot;async&quot; height=&quot;344&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 600px) 600px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/THMp68FKAah5qYIsiFOk.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/THMp68FKAah5qYIsiFOk.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/THMp68FKAah5qYIsiFOk.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/THMp68FKAah5qYIsiFOk.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/THMp68FKAah5qYIsiFOk.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/THMp68FKAah5qYIsiFOk.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/THMp68FKAah5qYIsiFOk.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/THMp68FKAah5qYIsiFOk.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/THMp68FKAah5qYIsiFOk.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/THMp68FKAah5qYIsiFOk.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/THMp68FKAah5qYIsiFOk.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/THMp68FKAah5qYIsiFOk.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/THMp68FKAah5qYIsiFOk.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/THMp68FKAah5qYIsiFOk.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/THMp68FKAah5qYIsiFOk.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/THMp68FKAah5qYIsiFOk.png?auto=format&amp;w=1200 1200w&quot; width=&quot;600&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;The service worker receives the port, saves a reference to it and uses it to send a message to the other
side:&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; communicationPort&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;//Save reference to port&lt;/span&gt;&lt;br /&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;PORT_INITIALIZATION&#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;    communicationPort &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ports&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;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;//Send messages&lt;/span&gt;&lt;br /&gt;communicationPort&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;postMessage&lt;/span&gt;&lt;span class=&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;MSG_ID&#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;&lt;code&gt;MessageChannel&lt;/code&gt; is currently supported by &lt;a href=&quot;https://caniuse.com/?search=channel&quot; rel=&quot;noopener&quot;&gt;all major
browsers&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;advanced-apis:-background-sync-and-background-fetch&quot;&gt;Advanced APIs: Background Sync and Background Fetch &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/two-way-communication-guide/#advanced-apis:-background-sync-and-background-fetch&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In this guide we explored ways of implementing &lt;strong&gt;two-way&lt;/strong&gt; communication techniques, for relatively
simple cases, like passing a string message describing the operation to perform, or a list of URLs
to cache from one context to the other. In this section we&#39;ll explore two APIs to handle specific
scenarios: lack of connectivity and long downloads.&lt;/p&gt;
&lt;h4 id=&quot;background-sync&quot;&gt;Background Sync &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/two-way-communication-guide/#background-sync&quot;&gt;#&lt;/a&gt;&lt;/h4&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 49, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      49
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox, Not supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Edge 79, Supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
79
&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Safari, Not supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;p&gt;&lt;/p&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/SyncManager#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;A chat app might want to make sure that messages are never lost due to bad connectivity. The
&lt;a href=&quot;https://developer.chrome.com/blog/background-sync/&quot; rel=&quot;noopener&quot;&gt;Background Sync API&lt;/a&gt; lets you
defer actions to be retried when the user has stable connectivity. This is useful for ensuring that
whatever the user wants to send, is actually sent.&lt;/p&gt;
&lt;p&gt;Instead of the &lt;code&gt;postMessage()&lt;/code&gt; interface, the page registers a &lt;code&gt;sync&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;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;serviceWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ready&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 parameter&quot;&gt;swRegistration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; swRegistration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sync&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;myFirstSync&#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;The service worker then listens for the &lt;code&gt;sync&lt;/code&gt; event to process the message:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;sync&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tag &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;myFirstSync&#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;    event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitUntil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;doSomeStuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The function &lt;code&gt;doSomeStuff()&lt;/code&gt; should return a promise indicating the success/failure of whatever it&#39;s
trying to do. If it fulfills, the sync is complete. If it fails, another sync will be scheduled to
retry. Retry syncs also wait for connectivity, and employ an exponential back-off.&lt;/p&gt;
&lt;p&gt;Once the operation has been performed, the service worker can then communicate back with the page to
update the UI, by using any of the communication APIs explored earlier.&lt;/p&gt;
&lt;p&gt;Google search uses Background Sync to persist failed queries due to bad connectivity, and retry
them later when the user is online. Once the operation is performed, they communicate the result to
the user via a web push notification:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Diagram showing a page passing a port to a service worker, to establish two-way communication.&quot; decoding=&quot;async&quot; height=&quot;381&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 700px) 700px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eMbvk9IRoaAggs8t5CbF.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eMbvk9IRoaAggs8t5CbF.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eMbvk9IRoaAggs8t5CbF.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eMbvk9IRoaAggs8t5CbF.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eMbvk9IRoaAggs8t5CbF.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eMbvk9IRoaAggs8t5CbF.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eMbvk9IRoaAggs8t5CbF.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eMbvk9IRoaAggs8t5CbF.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eMbvk9IRoaAggs8t5CbF.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eMbvk9IRoaAggs8t5CbF.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eMbvk9IRoaAggs8t5CbF.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eMbvk9IRoaAggs8t5CbF.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eMbvk9IRoaAggs8t5CbF.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eMbvk9IRoaAggs8t5CbF.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eMbvk9IRoaAggs8t5CbF.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eMbvk9IRoaAggs8t5CbF.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eMbvk9IRoaAggs8t5CbF.png?auto=format&amp;w=1400 1400w&quot; width=&quot;700&quot; /&gt;
&lt;/figure&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Check out &lt;a href=&quot;https://web.dev/resilient-search-experiences/&quot;&gt;Resilient search experiences &lt;/a&gt; to learn how to implement this feature using &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-background-sync/&quot;&gt;Workbox Background Sync&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;background-fetch&quot;&gt;Background Fetch &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/two-way-communication-guide/#background-fetch&quot;&gt;#&lt;/a&gt;&lt;/h4&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 74, 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;
      74
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox, Not supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Edge 79, Supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
79
&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Safari, Not supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;p&gt;&lt;/p&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/BackgroundFetchManager#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;For relatively short bits of work like sending a message, or a list of URLs to cache, the options
explored so far are a good choice. If the task takes too long the browser will kill the service
worker, otherwise it&#39;s a risk to the user&#39;s privacy and battery.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.chrome.com/blog/background-fetch/&quot; rel=&quot;noopener&quot;&gt;Background Fetch API&lt;/a&gt;
allows you to offload a long task to a service worker, like downloading movies, podcasts, or levels
of a game.&lt;/p&gt;
&lt;p&gt;To communicate to the service worker from the page, use &lt;code&gt;backgroundFetch.fetch&lt;/code&gt;, instead of
&lt;code&gt;postMessage()&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;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;serviceWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ready&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;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;swReg&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; bgFetch &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; swReg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;backgroundFetch&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token string&quot;&gt;&#39;my-fetch&#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 string&quot;&gt;&#39;/ep-5.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;ep-5-artwork.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;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Episode 5: Interesting things.&#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;icons&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 literal-property property&quot;&gt;sizes&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;300x300&#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;src&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/ep-5-icon.png&#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;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;image/png&#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 punctuation&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;downloadTotal&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1024&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1024&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;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The &lt;code&gt;BackgroundFetchRegistration&lt;/code&gt; object allows the page listen to the &lt;code&gt;progress&lt;/code&gt; event to follow
the progress of the download:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;bgFetch&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;progress&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// If we didn&#39;t provide a total, we can&#39;t provide a %.&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;bgFetch&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;downloadTotal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; percent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;round&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;bgFetch&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;downloaded &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; bgFetch&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;downloadTotal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&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;  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Download progress: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;percent&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span 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;figure&gt;
  &lt;img alt=&quot;Diagram showing a page passing a port to a service worker, to establish two-way communication.&quot; decoding=&quot;async&quot; height=&quot;434&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 700px) 700px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Ii1koJyCl0drJyewTIxV.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Ii1koJyCl0drJyewTIxV.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Ii1koJyCl0drJyewTIxV.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Ii1koJyCl0drJyewTIxV.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Ii1koJyCl0drJyewTIxV.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Ii1koJyCl0drJyewTIxV.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Ii1koJyCl0drJyewTIxV.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Ii1koJyCl0drJyewTIxV.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Ii1koJyCl0drJyewTIxV.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Ii1koJyCl0drJyewTIxV.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Ii1koJyCl0drJyewTIxV.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Ii1koJyCl0drJyewTIxV.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Ii1koJyCl0drJyewTIxV.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Ii1koJyCl0drJyewTIxV.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Ii1koJyCl0drJyewTIxV.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Ii1koJyCl0drJyewTIxV.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Ii1koJyCl0drJyewTIxV.png?auto=format&amp;w=1400 1400w&quot; width=&quot;700&quot; /&gt;
    &lt;figcaption&gt;The UI is updated to indicate the progress of a download (left). Thanks to service workers, the operation can continue running when all tabs have been closed (right).
    &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; Check out the &lt;a href=&quot;https://developer.chrome.com/blog/background-fetch/&quot;&gt;Background Fetch guide&lt;/a&gt;, which includes an &lt;a href=&quot;https://bgfetch-http203.glitch.me/&quot;&gt;example podcast app&lt;/a&gt; along with its &lt;a href=&quot;https://glitch.com/edit/#!/bgfetch-http203&quot;&gt;Glitch code&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next steps &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/two-way-communication-guide/#next-steps&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this guide we explored the most general case of communication between page and service workers
(bidirectional communication).&lt;/p&gt;
&lt;p&gt;Many times, one might need only one context to communicate with the other, without receiving a
response. Check out the following guides for guidance how to implement unidirectional techniques in
your pages from and to the service worker, along with use cases and production examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/imperative-caching-guide&quot;&gt;Imperative caching guide&lt;/a&gt;: Calling a service worker from the page to
cache resources in advance (e.g. in prefetching scenarios).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/broadcast-updates-guide&quot;&gt;Broadcast updates&lt;/a&gt;: Calling the page from the service worker to inform
about important updates (e.g. a new version of the webapp is available).&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Demian Renzulli</name>
    </author><author>
      <name>Andrew Guan</name>
    </author>
  </entry>
  
  <entry>
    <title>Workers overview</title>
    <link href="https://web.dev/workers-overview/"/>
    <updated>2020-12-08T00:00:00Z</updated>
    <id>https://web.dev/workers-overview/</id>
    <content type="html" mode="escaped">&lt;p&gt;This overview explains how web workers and service workers can improve the performance of your website, and when to use a web worker versus a service worker. Check out the rest of &lt;a href=&quot;https://web.dev/workers-overview/#next-steps&quot;&gt;this series&lt;/a&gt; for
specific patterns of window and service worker communication.&lt;/p&gt;
&lt;h2 id=&quot;how-workers-can-improve-your-website&quot;&gt;How workers can improve your website &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/workers-overview/#how-workers-can-improve-your-website&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The browser uses a single thread (the &lt;a href=&quot;https://developer.mozilla.org/docs/Glossary/Main_thread&quot; rel=&quot;noopener&quot;&gt;main
thread&lt;/a&gt;) to run all the JavaScript in
a web page, as well as to perform tasks like rendering the page and performing garbage collection.
Running excessive JavaScript code can block the main thread, delaying the browser from performing
these tasks and leading to a poor user experience.&lt;/p&gt;
&lt;p&gt;In iOS/Android application development, a common pattern to ensure that the app&#39;s main thread
remains free to respond to user events is to offload operations to additional threads. In fact, in
the latest versions of Android, blocking the main thread for too long &lt;a href=&quot;https://www.youtube.com/watch?v=eHjHlujp3Tg&amp;amp;feature=youtu.be&amp;amp;t=806&quot; rel=&quot;noopener&quot;&gt;leads to an app
crash&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;On the web, JavaScript was designed around the concept of a single thread, and lacks capabilities
needed to implement a multithreading model like the one apps have, like shared memory.&lt;/p&gt;
&lt;p&gt;Despite these limitations, a similar pattern can be achieved in the web by using
workers to run scripts in background
threads, allowing them to perform tasks without interfering with the main thread. Workers are an
entire JavaScript scope running on a separate thread, without any shared memory.&lt;/p&gt;
&lt;p&gt;In this post you&#39;ll learn about two different types of workers (web workers and service workers), their similarities and differences, and the most common patterns for using them in production websites.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Diagram showing two links between the Window object and a web worker and service worker.&quot; decoding=&quot;async&quot; height=&quot;418&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 728px) 728px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eN5kePr9U0aZMgCyekhJ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eN5kePr9U0aZMgCyekhJ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eN5kePr9U0aZMgCyekhJ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eN5kePr9U0aZMgCyekhJ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eN5kePr9U0aZMgCyekhJ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eN5kePr9U0aZMgCyekhJ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eN5kePr9U0aZMgCyekhJ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eN5kePr9U0aZMgCyekhJ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eN5kePr9U0aZMgCyekhJ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eN5kePr9U0aZMgCyekhJ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eN5kePr9U0aZMgCyekhJ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eN5kePr9U0aZMgCyekhJ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eN5kePr9U0aZMgCyekhJ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eN5kePr9U0aZMgCyekhJ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eN5kePr9U0aZMgCyekhJ.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eN5kePr9U0aZMgCyekhJ.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eN5kePr9U0aZMgCyekhJ.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eN5kePr9U0aZMgCyekhJ.png?auto=format&amp;w=1456 1456w&quot; width=&quot;728&quot; /&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;web-workers-and-service-workers&quot;&gt;Web workers and service workers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/workers-overview/#web-workers-and-service-workers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;similarities&quot;&gt;Similarities &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/workers-overview/#similarities&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Web_Workers_API/Using_web_workers&quot; rel=&quot;noopener&quot;&gt;Web workers&lt;/a&gt;
and &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;
are two types of workers available to websites. They have some things in common:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Both run in a secondary thread, allowing JavaScript code to execute without blocking the main
thread and the user interface.&lt;/li&gt;
&lt;li&gt;They don&#39;t have access to the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Window&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Window&lt;/code&gt;&lt;/a&gt;
and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Document&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Document&lt;/code&gt;&lt;/a&gt; objects, so they can&#39;t
interact with the DOM directly, and they have limited access to browser APIs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;differences&quot;&gt;Differences &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/workers-overview/#differences&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One might think that most things that can be delegated to a web worker can be done in a service
worker and vice versa, but there are important differences between them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Unlike web workers, service workers allow you to intercept network requests (via the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/FetchEvent&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;fetch&lt;/code&gt;&lt;/a&gt; event) and to listen for Push
API events in the background (via the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/PushEvent&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;push&lt;/code&gt;&lt;/a&gt; event).&lt;/li&gt;
&lt;li&gt;A page can spawn multiple web workers, but a single service worker controls all the active tabs
under the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/ServiceWorkerRegistration/scope&quot; rel=&quot;noopener&quot;&gt;scope&lt;/a&gt; it was
registered with.&lt;/li&gt;
&lt;li&gt;The lifespan of the web worker is tightly coupled to the tab it belongs to, while the &lt;a href=&quot;https://web.dev/service-worker-lifecycle/&quot;&gt;service
worker&#39;s
lifecycle&lt;/a&gt; is
independent of it. For that reason, closing the tab where a web worker is running will terminate
it, while a service worker can continue running in the background, even when the site doesn&#39;t have
any active tabs open.&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; For relatively short bits of work like sending a message, the browser won&#39;t likely terminate a service worker when there are no active tabs, but if the task takes too long the browser will terminate the service worker, otherwise it&#39;s a risk to the user&#39;s privacy and battery. APIs like &lt;a href=&quot;https://developer.chrome.com/blog/background-fetch/&quot;&gt;Background Fetch&lt;/a&gt;, that can let you avoid the service worker&#39;s termination. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;use-cases&quot;&gt;Use cases &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/workers-overview/#use-cases&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The differences between both types of workers suggest in which situations one might want to use one
or the other:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use cases for web workers&lt;/strong&gt; are more commonly related to offloading work (like &lt;a href=&quot;https://www.youtube.com/watch?v=mDdgfyRB5kg&amp;amp;feature=youtu.be&amp;amp;t=875&quot; rel=&quot;noopener&quot;&gt;heavy
computations&lt;/a&gt;) to a secondary
thread, to avoid blocking the UI.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Diagram showing a link from the Window object to a web worker.&quot; decoding=&quot;async&quot; height=&quot;175&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ZCC24V8uqi6HfFjRzuPq.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ZCC24V8uqi6HfFjRzuPq.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ZCC24V8uqi6HfFjRzuPq.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ZCC24V8uqi6HfFjRzuPq.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ZCC24V8uqi6HfFjRzuPq.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ZCC24V8uqi6HfFjRzuPq.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ZCC24V8uqi6HfFjRzuPq.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ZCC24V8uqi6HfFjRzuPq.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ZCC24V8uqi6HfFjRzuPq.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ZCC24V8uqi6HfFjRzuPq.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ZCC24V8uqi6HfFjRzuPq.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ZCC24V8uqi6HfFjRzuPq.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ZCC24V8uqi6HfFjRzuPq.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ZCC24V8uqi6HfFjRzuPq.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ZCC24V8uqi6HfFjRzuPq.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Example:&lt;/strong&gt; the team that built the videogame &lt;a href=&quot;https://proxx.app/&quot; rel=&quot;noopener&quot;&gt;PROXX&lt;/a&gt; wanted to leave the
main thread as free as possible to take care of user input and animations. To achieve that, they
&lt;a href=&quot;https://web.dev/proxx-announce/#web-workers&quot;&gt;used web workers&lt;/a&gt; to run the game logic and state
maintenance on a separate thread.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of the videogame PROXX.&quot; decoding=&quot;async&quot; height=&quot;403&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 225px) 225px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xXNCbahCtPrS8rw6uf1z.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xXNCbahCtPrS8rw6uf1z.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xXNCbahCtPrS8rw6uf1z.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xXNCbahCtPrS8rw6uf1z.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xXNCbahCtPrS8rw6uf1z.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xXNCbahCtPrS8rw6uf1z.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xXNCbahCtPrS8rw6uf1z.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xXNCbahCtPrS8rw6uf1z.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xXNCbahCtPrS8rw6uf1z.png?auto=format&amp;w=450 450w&quot; style=&quot;width: inherit; margin: auto;&quot; width=&quot;225&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Service workers tasks&lt;/strong&gt; are generally more related to acting as a network proxy, handling
background tasks, and things like caching and offline.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of the videogame PROXX.&quot; decoding=&quot;async&quot; height=&quot;267&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 624px) 624px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/GmGcVnb2y1yNc4ZIFFQ8.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/GmGcVnb2y1yNc4ZIFFQ8.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/GmGcVnb2y1yNc4ZIFFQ8.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/GmGcVnb2y1yNc4ZIFFQ8.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/GmGcVnb2y1yNc4ZIFFQ8.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/GmGcVnb2y1yNc4ZIFFQ8.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/GmGcVnb2y1yNc4ZIFFQ8.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/GmGcVnb2y1yNc4ZIFFQ8.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/GmGcVnb2y1yNc4ZIFFQ8.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/GmGcVnb2y1yNc4ZIFFQ8.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/GmGcVnb2y1yNc4ZIFFQ8.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/GmGcVnb2y1yNc4ZIFFQ8.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/GmGcVnb2y1yNc4ZIFFQ8.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/GmGcVnb2y1yNc4ZIFFQ8.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/GmGcVnb2y1yNc4ZIFFQ8.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/GmGcVnb2y1yNc4ZIFFQ8.png?auto=format&amp;w=1248 1248w&quot; width=&quot;624&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; In a &lt;a href=&quot;https://bgfetch-http203.glitch.me/&quot; rel=&quot;noopener&quot;&gt;podcast PWA&lt;/a&gt;, one might want to allow users
to download complete episodes to listen to them while offline. A service worker, and, in particular,
the &lt;a href=&quot;https://developer.chrome.com/blog/background-fetch/&quot; rel=&quot;noopener&quot;&gt;Background Fetch API&lt;/a&gt; can
be used to that end. That way, if the user closes the tab while the episode is downloading, the task
doesn&#39;t have to be interrupted.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of a Podcast PWA.&quot; decoding=&quot;async&quot; height=&quot;310&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oUH6K2JvcmfdAynTMjxQ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oUH6K2JvcmfdAynTMjxQ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oUH6K2JvcmfdAynTMjxQ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oUH6K2JvcmfdAynTMjxQ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oUH6K2JvcmfdAynTMjxQ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oUH6K2JvcmfdAynTMjxQ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oUH6K2JvcmfdAynTMjxQ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oUH6K2JvcmfdAynTMjxQ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oUH6K2JvcmfdAynTMjxQ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oUH6K2JvcmfdAynTMjxQ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oUH6K2JvcmfdAynTMjxQ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oUH6K2JvcmfdAynTMjxQ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oUH6K2JvcmfdAynTMjxQ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oUH6K2JvcmfdAynTMjxQ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/oUH6K2JvcmfdAynTMjxQ.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
    &lt;figcaption&gt;The UI is updated to indicate the progress of a download (left). Thanks to service workers, the operation can continue running when all tabs have been closed (right).&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;tools-and-libraries&quot;&gt;Tools and libraries &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/workers-overview/#tools-and-libraries&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Window and worker communication can be implemented by using different lower level APIs. Fortunately,
there are libraries that abstract this process, taking care of the most common use cases. In this
section, we&#39;ll cover two of them that take care of window to web workers and service workers
respectively: &lt;a href=&quot;https://github.com/GoogleChromeLabs/comlink&quot; rel=&quot;noopener&quot;&gt;Comlink&lt;/a&gt; and
&lt;a href=&quot;https://developer.chrome.com/docs/workbox/&quot; rel=&quot;noopener&quot;&gt;Workbox&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of the videogame PROXX.&quot; decoding=&quot;async&quot; height=&quot;269&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MNMwX5KuJt5iOLZJbdO0.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MNMwX5KuJt5iOLZJbdO0.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MNMwX5KuJt5iOLZJbdO0.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MNMwX5KuJt5iOLZJbdO0.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MNMwX5KuJt5iOLZJbdO0.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MNMwX5KuJt5iOLZJbdO0.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MNMwX5KuJt5iOLZJbdO0.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MNMwX5KuJt5iOLZJbdO0.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MNMwX5KuJt5iOLZJbdO0.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MNMwX5KuJt5iOLZJbdO0.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MNMwX5KuJt5iOLZJbdO0.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MNMwX5KuJt5iOLZJbdO0.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MNMwX5KuJt5iOLZJbdO0.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MNMwX5KuJt5iOLZJbdO0.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/MNMwX5KuJt5iOLZJbdO0.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;comlink&quot;&gt;Comlink &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/workers-overview/#comlink&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/GoogleChromeLabs/comlink&quot; rel=&quot;noopener&quot;&gt;Comlink&lt;/a&gt; is a small (1.6k)
&lt;a href=&quot;https://en.wikipedia.org/wiki/Remote_procedure_call&quot; rel=&quot;noopener&quot;&gt;RPC&lt;/a&gt; library that takes care of many
underlying details when building websites that use Web Workers. It has been used in websites
like &lt;a href=&quot;https://proxx.app/&quot; rel=&quot;noopener&quot;&gt;PROXX&lt;/a&gt; and &lt;a href=&quot;https://squoosh.app/&quot; rel=&quot;noopener&quot;&gt;Squoosh&lt;/a&gt;. A summary of its motivations
and code samples can be found &lt;a href=&quot;https://surma.dev/things/when-workers/&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;workbox&quot;&gt;Workbox &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/workers-overview/#workbox&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/docs/workbox/&quot; rel=&quot;noopener&quot;&gt;Workbox&lt;/a&gt; is a popular library to build websites
that use service workers. It packages a set of best practices around things like caching, offline,
background synchronization, etc. The &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-window/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;workbox-window&lt;/code&gt;&lt;/a&gt; module provides a
convenient way to exchange messages between the service worker and the page.&lt;/p&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next steps &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/workers-overview/#next-steps&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The rest of this series focuses on patterns for window and service worker communication:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/imperative-caching-guide&quot;&gt;Imperative caching guide&lt;/a&gt;: Calling a service worker from the page to
cache resources in advance (e.g. in prefetching scenarios).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/broadcast-updates-guide/&quot;&gt;Broadcast updates&lt;/a&gt;: Calling the page from the service worker to inform
about important updates (e.g. a new version of the website is available).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/two-way-communication-guide/&quot;&gt;Two-way communication&lt;/a&gt;: Delegating a task to a service worker
(e.g. a heavy download), and keeping the page informed on the progress.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For patterns of window and web worker communication check out: &lt;a href=&quot;https://web.dev/off-main-thread/&quot;&gt;Use web workers to run JavaScript
off the browser&#39;s main thread&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Demian Renzulli</name>
    </author><author>
      <name>Andrew Guan</name>
    </author>
  </entry>
  
  <entry>
    <title>How Mercado Libre optimized for Web Vitals (TBT/FID)</title>
    <link href="https://web.dev/how-mercadolibre-optimized-web-vitals/"/>
    <updated>2020-09-21T00:00:00Z</updated>
    <id>https://web.dev/how-mercadolibre-optimized-web-vitals/</id>
    <content type="html" mode="escaped">&lt;p&gt;Mercado Libre is the largest e-commerce and payments ecosystem in Latin America. It is present in 18
countries and is a market leader in Brazil, Mexico, and Argentina (based on unique visitors and
pageviews).&lt;/p&gt;
&lt;p&gt;Web performance has been a focus for the company for a long time, but they recently formed a team to
monitor performance and apply optimizations across different parts of the site.&lt;/p&gt;
&lt;p&gt;This article summarizes the work done by &lt;a href=&quot;https://twitter.com/pazguille&quot; rel=&quot;noopener&quot;&gt;Guille Paz&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/pcarminatti/&quot; rel=&quot;noopener&quot;&gt;Pablo
Carminatti&lt;/a&gt;, and &lt;a href=&quot;https://twitter.com/oburkhay&quot; rel=&quot;noopener&quot;&gt;Oleh
Burkhay&lt;/a&gt; from Mercado Libre&#39;s frontend architecture team to optimize
one of the Core Web Vitals: &lt;a href=&quot;https://web.dev/fid/&quot;&gt;First Input Delay (FID)&lt;/a&gt; and its lab proxy,
&lt;a href=&quot;https://web.dev/tbt/&quot;&gt;Total Blocking Time (TBT)&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;stats&quot;&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;90&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;Reduction in Max Potential FID in Lighthouse&lt;/p&gt;
  &lt;/div&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;9&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;More users perceiving FID as &quot;Fast&quot; in CrUX&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&quot;long-tasks,-first-input-delay,-and-total-blocking-time&quot;&gt;Long tasks, First Input Delay, and Total Blocking Time &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-mercadolibre-optimized-web-vitals/#long-tasks,-first-input-delay,-and-total-blocking-time&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Running expensive JavaScript code can lead to &lt;a href=&quot;https://web.dev/long-tasks-devtools/&quot;&gt;long tasks&lt;/a&gt;,
which are those that run for more than &lt;strong&gt;50ms&lt;/strong&gt; in the browser&#39;s main thread.&lt;/p&gt;
&lt;p&gt;FID (First Input Delay) measures the time from when a user first interacts with a page (e.g. when
they click on a link) to the time when the browser is actually able to begin processing event
handlers in response to that interaction. A site that executes expensive JavaScript code will likely
have several long tasks, which will end up negatively impacting FID.&lt;/p&gt;
&lt;p&gt;To provide a good user experience, sites should strive to have a First Input Delay of less than 100
milliseconds:
&lt;picture&gt;&lt;/picture&gt;&lt;/p&gt;
  &lt;source srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eXyvkqRHQZ5iG38Axh1Z.svg&quot; media=&quot;(min-width: 640px)&quot; /&gt;
  &lt;img alt=&quot;Good fid values are 2.5 seconds, poor values are greater than 4.0 seconds and anything in between needs improvement&quot; decoding=&quot;async&quot; height=&quot;96&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Se4TiXIdp8jtLJVScWed.svg&quot; width=&quot;384&quot; /&gt;

&lt;p&gt;While Mercado Libre&#39;s site was performing well in most sections, they found in the &lt;a href=&quot;https://developer.chrome.com/docs/crux/&quot; rel=&quot;noopener&quot;&gt;Chrome User
Experience Report&lt;/a&gt; that
product detail pages had a poor FID. Based on that information, they decided to focus their efforts
on improving the interactivity for product pages in the site.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Mobile and Desktop versions of a Mercado Libre product detail page.&quot; decoding=&quot;async&quot; height=&quot;346&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gg8ohXTbFgr6Msacklt0.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gg8ohXTbFgr6Msacklt0.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gg8ohXTbFgr6Msacklt0.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gg8ohXTbFgr6Msacklt0.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gg8ohXTbFgr6Msacklt0.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gg8ohXTbFgr6Msacklt0.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gg8ohXTbFgr6Msacklt0.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gg8ohXTbFgr6Msacklt0.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gg8ohXTbFgr6Msacklt0.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gg8ohXTbFgr6Msacklt0.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gg8ohXTbFgr6Msacklt0.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gg8ohXTbFgr6Msacklt0.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gg8ohXTbFgr6Msacklt0.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gg8ohXTbFgr6Msacklt0.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gg8ohXTbFgr6Msacklt0.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gg8ohXTbFgr6Msacklt0.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gg8ohXTbFgr6Msacklt0.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gg8ohXTbFgr6Msacklt0.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
   &lt;figcaption&gt;
      Mobile and Desktop versions of a Mercado Libre product detail page.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;These pages allow the user to perform complex interactions, so the goal was interactivity
optimization, without interfering with valuable functionality.&lt;/p&gt;
&lt;h2 id=&quot;measure-interactivity-of-product-detail-pages&quot;&gt;Measure interactivity of product detail pages &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-mercadolibre-optimized-web-vitals/#measure-interactivity-of-product-detail-pages&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;FID requires a real user and thus cannot be measured in the lab. However, the &lt;a href=&quot;https://web.dev/tbt/&quot;&gt;Total Blocking Time
(TBT)&lt;/a&gt; metric is lab-measurable, correlates well with FID in the field, and
also captures issues that affect interactivity.&lt;/p&gt;
&lt;p&gt;In the following trace, for example, while the &lt;strong&gt;total time&lt;/strong&gt; spent running tasks on the main thread
is 560 ms, only 345 ms of that time is considered &lt;strong&gt;total blocking time&lt;/strong&gt; (the sum of the portions
of each task that exceeds 50ms):&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/us8USZRiCh9sg1X2zEpN.svg&quot;&gt;&lt;img alt=&quot;A tasks timeline on the main thread showing blocking time&quot; decoding=&quot;async&quot; height=&quot;156&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/us8USZRiCh9sg1X2zEpN.svg&quot; width=&quot;800&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Mercado Libre took TBT as a proxy metric in the lab, in order to measure and improve the
interactivity of product detail pages in the real world.&lt;/p&gt;
&lt;p&gt;Here&#39;s the general approach they took:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;a href=&quot;https://www.webpagetest.org/&quot; rel=&quot;noopener&quot;&gt;WebPageTest&lt;/a&gt; to determine exactly which scripts were keeping
the main thread busy on a real device.&lt;/li&gt;
&lt;li&gt;Use &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview/&quot; rel=&quot;noopener&quot;&gt;Lighthouse&lt;/a&gt; to determine the impact of
the changes in &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/lighthouse-max-potential-fid/&quot; rel=&quot;noopener&quot;&gt;Max Potential First Input Delay (Max Potential
FID)&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; During this project Mercado Libre used &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/lighthouse-max-potential-fid/&quot;&gt;Max Potential FID&lt;/a&gt; in Lighthouse because that was the tool&#39;s main metric for measuring interactivity at that time. Lighthouse now recommends using &lt;a href=&quot;https://web.dev/tbt/&quot;&gt;Total Blocking Time&lt;/a&gt; instead. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;use-webpagetest-to-visualize-long-tasks&quot;&gt;Use WebPageTest to visualize long tasks &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-mercadolibre-optimized-web-vitals/#use-webpagetest-to-visualize-long-tasks&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;WebPageTest (WPT) is a web performance tool that allows you to run tests on real devices in
different locations around the world.&lt;/p&gt;
&lt;p&gt;Mercado Libre used WPT to reproduce the experience of their users by choosing a device type and
location similar to real users. Specifically, they chose a &lt;strong&gt;Moto 4G device&lt;/strong&gt; and &lt;strong&gt;Dulles,
Virginia&lt;/strong&gt;, because they wanted to approximate the experience of Mercado Libre users in Mexico. By
observing the main thread view of WPT, Mercado Libre found that there were several consecutive long
tasks blocking the main thread for 2 seconds:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Main thread view of Mercado Libre&amp;#x27;s product detail pages.&quot; decoding=&quot;async&quot; height=&quot;188&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/NbVmhDK9MLvyvEBbBYAZ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/NbVmhDK9MLvyvEBbBYAZ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/NbVmhDK9MLvyvEBbBYAZ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/NbVmhDK9MLvyvEBbBYAZ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/NbVmhDK9MLvyvEBbBYAZ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/NbVmhDK9MLvyvEBbBYAZ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/NbVmhDK9MLvyvEBbBYAZ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/NbVmhDK9MLvyvEBbBYAZ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/NbVmhDK9MLvyvEBbBYAZ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/NbVmhDK9MLvyvEBbBYAZ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/NbVmhDK9MLvyvEBbBYAZ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/NbVmhDK9MLvyvEBbBYAZ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/NbVmhDK9MLvyvEBbBYAZ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/NbVmhDK9MLvyvEBbBYAZ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/NbVmhDK9MLvyvEBbBYAZ.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/NbVmhDK9MLvyvEBbBYAZ.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/NbVmhDK9MLvyvEBbBYAZ.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/NbVmhDK9MLvyvEBbBYAZ.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
   &lt;figcaption&gt;
      Main thread view of Mercado Libre&#39;s product detail pages.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Analyzing the corresponding waterfall they found that a considerable part of those two seconds came
from their analytics module. The main bundle size of the application was large (950KB) and took a
long time to parse, compile, and execute.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Waterfall view of product detail pages.&quot; decoding=&quot;async&quot; height=&quot;363&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7QHKOutyGzfXN52hPOOz.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7QHKOutyGzfXN52hPOOz.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7QHKOutyGzfXN52hPOOz.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7QHKOutyGzfXN52hPOOz.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7QHKOutyGzfXN52hPOOz.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7QHKOutyGzfXN52hPOOz.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7QHKOutyGzfXN52hPOOz.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7QHKOutyGzfXN52hPOOz.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7QHKOutyGzfXN52hPOOz.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7QHKOutyGzfXN52hPOOz.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7QHKOutyGzfXN52hPOOz.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7QHKOutyGzfXN52hPOOz.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7QHKOutyGzfXN52hPOOz.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7QHKOutyGzfXN52hPOOz.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7QHKOutyGzfXN52hPOOz.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7QHKOutyGzfXN52hPOOz.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7QHKOutyGzfXN52hPOOz.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7QHKOutyGzfXN52hPOOz.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
   &lt;figcaption&gt;
      Waterfall view of Mercado Libre&#39;s product detail pages.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;use-lighthouse-to-determine-max-potential-fid&quot;&gt;Use Lighthouse to determine Max Potential FID &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-mercadolibre-optimized-web-vitals/#use-lighthouse-to-determine-max-potential-fid&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Lighthouse doesn&#39;t allow you to choose between different devices and locations, but it&#39;s a very
useful tool for diagnosing sites and obtaining performance recommendations.&lt;/p&gt;
&lt;p&gt;When running Lighthouse on product detail pages, Mercado Libre found that the &lt;strong&gt;Max Potential FID&lt;/strong&gt;
was the only metric marked in red, with a value of &lt;strong&gt;1710ms&lt;/strong&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Lighthouse metrics in a PSI report for Mercado Libre&amp;#x27;s product detail pages.&quot; decoding=&quot;async&quot; height=&quot;235&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rufTQY4scq1V3ghVIQPy.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rufTQY4scq1V3ghVIQPy.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rufTQY4scq1V3ghVIQPy.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rufTQY4scq1V3ghVIQPy.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rufTQY4scq1V3ghVIQPy.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rufTQY4scq1V3ghVIQPy.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rufTQY4scq1V3ghVIQPy.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rufTQY4scq1V3ghVIQPy.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rufTQY4scq1V3ghVIQPy.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rufTQY4scq1V3ghVIQPy.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rufTQY4scq1V3ghVIQPy.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rufTQY4scq1V3ghVIQPy.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rufTQY4scq1V3ghVIQPy.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rufTQY4scq1V3ghVIQPy.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rufTQY4scq1V3ghVIQPy.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rufTQY4scq1V3ghVIQPy.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rufTQY4scq1V3ghVIQPy.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/rufTQY4scq1V3ghVIQPy.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Based on this, Mercado Libre set a goal to improve their Max Potential FID score in a laboratory
tool like Lighthouse and WebPageTest, under the assumption that these improvements would affect
their real users, and therefore, show up in real user monitoring tools like the Chrome User
Experience Report.&lt;/p&gt;
&lt;h2 id=&quot;optimize-long-tasks&quot;&gt;Optimize long tasks &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-mercadolibre-optimized-web-vitals/#optimize-long-tasks&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;first-iteration&quot;&gt;First iteration &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-mercadolibre-optimized-web-vitals/#first-iteration&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Based on the main thread trace, Mercado Libre set the goal of optimizing the two modules that were
running expensive code.&lt;/p&gt;
&lt;p&gt;They started optimizing the performance of the internal tracking module. This module contained a
CPU-heavy task that wasn&#39;t critical for the module to work, and therefore could be safely removed.
This led to a 2% reduction in JavaScript for the whole site.&lt;/p&gt;
&lt;p&gt;After that they started to work on &lt;strong&gt;improving the general bundle size&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;Mercado Libre used
&lt;a href=&quot;https://github.com/webpack-contrib/webpack-bundle-analyzer&quot; rel=&quot;noopener&quot;&gt;webpack-bundle-analyzer&lt;/a&gt; to detect
opportunities for optimization:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Initially they were requiring the full &lt;a href=&quot;https://lodash.com/&quot; rel=&quot;noopener&quot;&gt;Lodash module&lt;/a&gt;. This was replaced
with a &lt;a href=&quot;https://lodash.com/per-method-packages&quot; rel=&quot;noopener&quot;&gt;per-method require&lt;/a&gt; to load only a subset of
Lodash instead of the whole library, and used in conjunction with
&lt;a href=&quot;https://github.com/lodash/lodash-webpack-plugin&quot; rel=&quot;noopener&quot;&gt;lodash-webpack-plugin&lt;/a&gt; to shrink Lodash even
further.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;They also applied the following &lt;a href=&quot;https://babeljs.io/&quot; rel=&quot;noopener&quot;&gt;Babel&lt;/a&gt; optimizations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using &lt;a href=&quot;https://babeljs.io/docs/en/babel-plugin-transform-runtime&quot; rel=&quot;noopener&quot;&gt;@babel/plugin-transform-runtime&lt;/a&gt;
to reuse Babel&#39;s helpers throughout the code, and reduce the size of the bundle considerably.&lt;/li&gt;
&lt;li&gt;Using
&lt;a href=&quot;https://github.com/jean-smaug/babel-plugin-search-and-replace#readme&quot; rel=&quot;noopener&quot;&gt;babel-plugin-search-and-replace&lt;/a&gt;
to replace tokens at build time, in order to remove a large configuration file inside the main
bundle.&lt;/li&gt;
&lt;li&gt;Adding
&lt;a href=&quot;https://github.com/oliviertassinari/babel-plugin-transform-react-remove-prop-types#readme&quot; rel=&quot;noopener&quot;&gt;babel-plugin-transform-react-remove-prop-types&lt;/a&gt;
to save some extra bytes by removing the prop types.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As a result of these optimizations, the bundle size was reduced &lt;strong&gt;by approximately 16%&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;measure-impact&quot;&gt;Measure impact &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-mercadolibre-optimized-web-vitals/#measure-impact&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The changes lowered Mercado Libre&#39;s consecutive long tasks &lt;strong&gt;from two seconds to one second&lt;/strong&gt;:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Main thread view of Mercado Libre&amp;#x27;s product detail pages after first round of optimizations.&quot; decoding=&quot;async&quot; height=&quot;315&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/17At96aKcPrvNTWgb3FU.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/17At96aKcPrvNTWgb3FU.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/17At96aKcPrvNTWgb3FU.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/17At96aKcPrvNTWgb3FU.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/17At96aKcPrvNTWgb3FU.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/17At96aKcPrvNTWgb3FU.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/17At96aKcPrvNTWgb3FU.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/17At96aKcPrvNTWgb3FU.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/17At96aKcPrvNTWgb3FU.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/17At96aKcPrvNTWgb3FU.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/17At96aKcPrvNTWgb3FU.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/17At96aKcPrvNTWgb3FU.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/17At96aKcPrvNTWgb3FU.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/17At96aKcPrvNTWgb3FU.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/17At96aKcPrvNTWgb3FU.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/17At96aKcPrvNTWgb3FU.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/17At96aKcPrvNTWgb3FU.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/17At96aKcPrvNTWgb3FU.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
   &lt;figcaption&gt;
      In the top waterfall of WPT there’s a long red bar (in the &lt;b&gt;Page is Interactive&lt;/b&gt; row) between seconds 3 and 5. In the bottom waterfall, the bar has been broken into smaller pieces, occupying the main thread for shorter periods of time.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Lighthouse showed a &lt;strong&gt;57% reduction&lt;/strong&gt; in Max Potential First Input Delay:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Lighthouse metrics in a PSI report for Mercado Libre&amp;#x27;s product detail pages after first round of optimizations.&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/tcFciHGuF3MxnTr1y5ue01OGLBn2/Sxa1wKCXVfsHZNbfQ1ZZ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Sxa1wKCXVfsHZNbfQ1ZZ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Sxa1wKCXVfsHZNbfQ1ZZ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Sxa1wKCXVfsHZNbfQ1ZZ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Sxa1wKCXVfsHZNbfQ1ZZ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Sxa1wKCXVfsHZNbfQ1ZZ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Sxa1wKCXVfsHZNbfQ1ZZ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Sxa1wKCXVfsHZNbfQ1ZZ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Sxa1wKCXVfsHZNbfQ1ZZ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Sxa1wKCXVfsHZNbfQ1ZZ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Sxa1wKCXVfsHZNbfQ1ZZ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Sxa1wKCXVfsHZNbfQ1ZZ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Sxa1wKCXVfsHZNbfQ1ZZ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Sxa1wKCXVfsHZNbfQ1ZZ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Sxa1wKCXVfsHZNbfQ1ZZ.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Sxa1wKCXVfsHZNbfQ1ZZ.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Sxa1wKCXVfsHZNbfQ1ZZ.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Sxa1wKCXVfsHZNbfQ1ZZ.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;second-iteration&quot;&gt;Second iteration &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-mercadolibre-optimized-web-vitals/#second-iteration&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The team continued digging into long tasks in order to find subsequent improvements.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Detailed view of main thread view of Mercado Libre&amp;#x27;s product detail pages after first round of optimizations.&quot; decoding=&quot;async&quot; height=&quot;259&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tlMIQRWDAeEY7UV4cFQo.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tlMIQRWDAeEY7UV4cFQo.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tlMIQRWDAeEY7UV4cFQo.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tlMIQRWDAeEY7UV4cFQo.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tlMIQRWDAeEY7UV4cFQo.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tlMIQRWDAeEY7UV4cFQo.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tlMIQRWDAeEY7UV4cFQo.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tlMIQRWDAeEY7UV4cFQo.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tlMIQRWDAeEY7UV4cFQo.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tlMIQRWDAeEY7UV4cFQo.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tlMIQRWDAeEY7UV4cFQo.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tlMIQRWDAeEY7UV4cFQo.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tlMIQRWDAeEY7UV4cFQo.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tlMIQRWDAeEY7UV4cFQo.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tlMIQRWDAeEY7UV4cFQo.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tlMIQRWDAeEY7UV4cFQo.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tlMIQRWDAeEY7UV4cFQo.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/tlMIQRWDAeEY7UV4cFQo.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
   &lt;figcaption&gt;
      The Waterfall (not pictured) helped Mercado Libre identify which libraries were using the main thread heavily (&lt;b&gt;Browser Main Thread&lt;/b&gt; row) and the &lt;b&gt;Page is Interactive&lt;/b&gt; row clearly shows that this main thread activity is blocking interactivity.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Based on that information they decided to implement the following changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Continue reducing the main bundle size to optimize compile and parse time (e.g. by removing
duplicate dependencies throughout the different modules).&lt;/li&gt;
&lt;li&gt;Apply &lt;a href=&quot;https://web.dev/reduce-javascript-payloads-with-code-splitting/&quot;&gt;code splitting&lt;/a&gt; at
component level, to divide JavaScript in smaller chunks and allow for smarter loading of the
different components.&lt;/li&gt;
&lt;li&gt;Defer &lt;a href=&quot;https://developers.google.com/web/updates/2019/02/rendering-on-the-web#rehydration&quot; rel=&quot;noopener&quot;&gt;component
hydration&lt;/a&gt; to
allow for a smarter use of the main thread. This technique is commonly referred to as &lt;a href=&quot;https://addyosmani.com/blog/rehydration/&quot; rel=&quot;noopener&quot;&gt;partial
hydration&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;measure-impact-2&quot;&gt;Measure impact &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-mercadolibre-optimized-web-vitals/#measure-impact-2&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The resulting WebPageTest trace showed even smaller chunks of JS execution:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Main thread view of Mercado Libre&amp;#x27;s product detail pages after secoond round of optimizations.&quot; decoding=&quot;async&quot; height=&quot;150&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gAvo2VXimablQ8OhFDdn.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gAvo2VXimablQ8OhFDdn.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gAvo2VXimablQ8OhFDdn.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gAvo2VXimablQ8OhFDdn.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gAvo2VXimablQ8OhFDdn.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gAvo2VXimablQ8OhFDdn.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gAvo2VXimablQ8OhFDdn.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gAvo2VXimablQ8OhFDdn.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gAvo2VXimablQ8OhFDdn.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gAvo2VXimablQ8OhFDdn.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gAvo2VXimablQ8OhFDdn.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gAvo2VXimablQ8OhFDdn.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gAvo2VXimablQ8OhFDdn.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gAvo2VXimablQ8OhFDdn.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gAvo2VXimablQ8OhFDdn.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gAvo2VXimablQ8OhFDdn.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gAvo2VXimablQ8OhFDdn.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gAvo2VXimablQ8OhFDdn.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;And their Max Potential FID time in Lighthouse was reduced &lt;strong&gt;by an additional 60%&lt;/strong&gt;:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Lighthouse metrics in a PSI report for Mercado Libre&amp;#x27;s product detail pages after first round of optimizations.&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/tcFciHGuF3MxnTr1y5ue01OGLBn2/7W672LOor2SgqZsmK3BL.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7W672LOor2SgqZsmK3BL.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7W672LOor2SgqZsmK3BL.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7W672LOor2SgqZsmK3BL.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7W672LOor2SgqZsmK3BL.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7W672LOor2SgqZsmK3BL.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7W672LOor2SgqZsmK3BL.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7W672LOor2SgqZsmK3BL.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7W672LOor2SgqZsmK3BL.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7W672LOor2SgqZsmK3BL.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7W672LOor2SgqZsmK3BL.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7W672LOor2SgqZsmK3BL.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7W672LOor2SgqZsmK3BL.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7W672LOor2SgqZsmK3BL.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7W672LOor2SgqZsmK3BL.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7W672LOor2SgqZsmK3BL.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7W672LOor2SgqZsmK3BL.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/7W672LOor2SgqZsmK3BL.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;visualize-progress-for-real-users&quot;&gt;Visualize progress for real users &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-mercadolibre-optimized-web-vitals/#visualize-progress-for-real-users&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While laboratory testing tools like WebPageTest and Lighthouse are great for iterating on solutions
during development, the true goal is to improve the experience for real users.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.chrome.com/docs/crux/&quot; rel=&quot;noopener&quot;&gt;Chrome User Experience
Report&lt;/a&gt; provides user
experience metrics for how real-world Chrome users experience popular destinations on the web. The
data from the report can be obtained by &lt;a href=&quot;https://developer.chrome.com/blog/chrome-ux-report-bigquery/&quot; rel=&quot;noopener&quot;&gt;running queries in
BigQuery&lt;/a&gt;,
&lt;a href=&quot;https://pagespeed.web.dev/&quot; rel=&quot;noopener&quot;&gt;PageSpeedInsights&lt;/a&gt;, or the &lt;a href=&quot;https://developer.chrome.com/blog/chrome-ux-report-api/&quot; rel=&quot;noopener&quot;&gt;CrUX
API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://datastudio.google.com/c/datasources/create?connectorId=AKfycbxk7u2UtsqzgaA7I0bvkaJbBPannEx0_zmeCsGh9bBZy7wFMLrQ8x24WxpBzk_ln2i7&quot; rel=&quot;noopener&quot;&gt;CrUX
dashboard&lt;/a&gt;
is an easy way to visualize the progress of core metrics:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;.&quot; decoding=&quot;async&quot; height=&quot;163&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3bUj9l2ISMr3mojaUG4o.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3bUj9l2ISMr3mojaUG4o.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3bUj9l2ISMr3mojaUG4o.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3bUj9l2ISMr3mojaUG4o.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3bUj9l2ISMr3mojaUG4o.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3bUj9l2ISMr3mojaUG4o.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3bUj9l2ISMr3mojaUG4o.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3bUj9l2ISMr3mojaUG4o.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3bUj9l2ISMr3mojaUG4o.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3bUj9l2ISMr3mojaUG4o.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3bUj9l2ISMr3mojaUG4o.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3bUj9l2ISMr3mojaUG4o.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3bUj9l2ISMr3mojaUG4o.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3bUj9l2ISMr3mojaUG4o.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3bUj9l2ISMr3mojaUG4o.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3bUj9l2ISMr3mojaUG4o.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3bUj9l2ISMr3mojaUG4o.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/3bUj9l2ISMr3mojaUG4o.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
    &lt;figcaption&gt;
      Mercado Libre&#39;s FID progress between Jan 2020 and April 2020. Before the optimization project, 82% of the users were perceiving FID as fast (below 100ms). After, more than 91% of the users were perceiving the metric as fast.
    &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/how-mercadolibre-optimized-web-vitals/#next-steps&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Web performance is never a finished task, and Mercado Libre understands the value these
optimizations bring to their users. While they continue applying several optimizations across the
site, including &lt;a href=&quot;https://web.dev/instant-navigation-experiences/#production-cases&quot;&gt;prefetching&lt;/a&gt; in
product listing pages, image optimizations, and others, they continue adding improvements to product
listing pages to reduce Total Blocking Time (TBT), and by proxy FID, even more. These optimizations
include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Iterating on the code splitting solution.&lt;/li&gt;
&lt;li&gt;Improving the execution of third-party scripts.&lt;/li&gt;
&lt;li&gt;Continuing improvements in asset bundling at the bundler level
(&lt;a href=&quot;https://webpack.js.org/&quot; rel=&quot;noopener&quot;&gt;webpack&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Mercado Libre has a holistic view of performance, so while they continue optimizing interactivity in
the site, they have also started assessing opportunities for improvement on the other two current
&lt;a href=&quot;https://web.dev/vitals/&quot;&gt;Core Web Vitals&lt;/a&gt;: &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;LCP (Largest Contentful Paint)&lt;/a&gt;
and &lt;a href=&quot;https://web.dev/cls/&quot;&gt;CLS (Cumulative Layout Shift)&lt;/a&gt; even more.&lt;/p&gt;
</content>
    <author>
      <name>Demian Renzulli</name>
    </author><author>
      <name>Carlos Aranha</name>
    </author><author>
      <name>Joan Baca</name>
    </author>
  </entry>
  
  <entry>
    <title>Adaptive loading with service workers</title>
    <link href="https://web.dev/adaptive-loading-with-service-workers/"/>
    <updated>2020-06-23T00:00:00Z</updated>
    <id>https://web.dev/adaptive-loading-with-service-workers/</id>
    <content type="html" mode="escaped">&lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;fhqCwDP69PI&quot; videoStartAt=&quot;161&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
&lt;p&gt;Users access websites through a wide variety of devices and network connections. Even in major cities, where mobile networks are fast and reliable, one can end up experiencing slower load times, for example, when commuting in the subway, in a car, or just when moving around.
In regions like emerging markets, this phenomenon is even more common, not only due to unreliable networks, but also because devices tend to have less memory and CPU processing power.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/adaptive-loading-cds-2019/&quot;&gt;Adaptive loading&lt;/a&gt; is a web performance pattern that lets you adapt your site based on the user&#39;s network and device conditions.&lt;/p&gt;
&lt;p&gt;The adaptive loading pattern is made possible by &lt;a href=&quot;https://web.dev/service-workers-cache-storage/&quot;&gt;service workers&lt;/a&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;, the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/NavigatorConcurrentHardware/hardwareConcurrency&quot; rel=&quot;noopener&quot;&gt;Hardware Concurrency API&lt;/a&gt;, and the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Navigator/deviceMemory&quot; rel=&quot;noopener&quot;&gt;Device Memory API&lt;/a&gt;. In this guide we explore how you can use service workers and the Network Information API to achieve an adaptive loading strategy.&lt;/p&gt;
&lt;h2 id=&quot;production-case&quot;&gt;Production case &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/adaptive-loading-with-service-workers/#production-case&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.terra.com.br/&quot; rel=&quot;noopener&quot;&gt;Terra&lt;/a&gt; is one of the biggest media companies in Brazil. It has a large user base, coming from a wide variety of devices and networks.&lt;/p&gt;
&lt;p&gt;To provide a more reliable experience to all their users, Terra combines service workers and the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Network_Information_API&quot; rel=&quot;noopener&quot;&gt;Network Information API&lt;/a&gt; to deliver lower quality images to users on 2G or 3G connections.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of Terra&amp;#x27;s home page connected to different image qualities according to the connection type.&quot; decoding=&quot;async&quot; height=&quot;381&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 734px) 734px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/EQst12doZ2b8CLO0MtO5.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/EQst12doZ2b8CLO0MtO5.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/EQst12doZ2b8CLO0MtO5.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/EQst12doZ2b8CLO0MtO5.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/EQst12doZ2b8CLO0MtO5.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/EQst12doZ2b8CLO0MtO5.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/EQst12doZ2b8CLO0MtO5.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/EQst12doZ2b8CLO0MtO5.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/EQst12doZ2b8CLO0MtO5.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/EQst12doZ2b8CLO0MtO5.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/EQst12doZ2b8CLO0MtO5.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/EQst12doZ2b8CLO0MtO5.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/EQst12doZ2b8CLO0MtO5.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/EQst12doZ2b8CLO0MtO5.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/EQst12doZ2b8CLO0MtO5.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/EQst12doZ2b8CLO0MtO5.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/EQst12doZ2b8CLO0MtO5.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/EQst12doZ2b8CLO0MtO5.png?auto=format&amp;w=1468 1468w&quot; width=&quot;734&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;The company also found that the scripts and assets (like banners) loaded by ad networks were especially detrimental to users navigating in 3G or slower connections.&lt;/p&gt;
&lt;p&gt;As is the case with many publishers, Terra serves &lt;a href=&quot;https://amp.dev/&quot; rel=&quot;noopener&quot;&gt;AMP&lt;/a&gt; versions of their pages to users coming from search engines and other link sharing platforms. AMP pages are usually lightweight and help mitigate the impact of ads in performance by deprioritizing their load with respect to the main content of the page.&lt;/p&gt;
&lt;p&gt;Taking that into consideration, Terra decided to start serving AMP versions of their pages not only to users coming from search engines, but also to those navigating their site in 3G connections or slower.&lt;/p&gt;
&lt;p&gt;To achieve that, they use the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Network_Information_API&quot; rel=&quot;noopener&quot;&gt;Network Information API&lt;/a&gt; in the service worker to detect if the request comes from 3G or slower. If that&#39;s the case, they change the URL of the page to request the AMP version of the page instead.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of Terra&amp;#x27;s article page connected to different image qualities according to the connection type.&quot; decoding=&quot;async&quot; height=&quot;379&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 741px) 741px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/4kfDfkeIzxVXLoaTylug.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/4kfDfkeIzxVXLoaTylug.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/4kfDfkeIzxVXLoaTylug.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/4kfDfkeIzxVXLoaTylug.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/4kfDfkeIzxVXLoaTylug.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/4kfDfkeIzxVXLoaTylug.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/4kfDfkeIzxVXLoaTylug.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/4kfDfkeIzxVXLoaTylug.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/4kfDfkeIzxVXLoaTylug.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/4kfDfkeIzxVXLoaTylug.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/4kfDfkeIzxVXLoaTylug.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/4kfDfkeIzxVXLoaTylug.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/4kfDfkeIzxVXLoaTylug.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/4kfDfkeIzxVXLoaTylug.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/4kfDfkeIzxVXLoaTylug.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/4kfDfkeIzxVXLoaTylug.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/4kfDfkeIzxVXLoaTylug.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/4kfDfkeIzxVXLoaTylug.png?auto=format&amp;w=1482 1482w&quot; width=&quot;741&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Thanks to this technique, they send &lt;strong&gt;70% less bytes&lt;/strong&gt; to users on slower connections. The &lt;strong&gt;time spent&lt;/strong&gt; in AMP pages is higher for 3G users and ads in AMP pages have a better &lt;strong&gt;CTR (click-through-rate)&lt;/strong&gt; for that group.&lt;/p&gt;
&lt;h2 id=&quot;implement-adaptive-loading-with-workbox&quot;&gt;Implement adaptive loading with Workbox &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/adaptive-loading-with-service-workers/#implement-adaptive-loading-with-workbox&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this section we&#39;ll explore how &lt;a href=&quot;https://web.dev/workbox/&quot;&gt;Workbox&lt;/a&gt; can be used to implement adaptive loading strategies.&lt;/p&gt;
&lt;p&gt;Workbox provides several &lt;a href=&quot;https://web.dev/runtime-caching-with-workbox/&quot;&gt;runtime caching strategies&lt;/a&gt; out of the box. They are used to indicate how the service worker generates a response after receiving a &lt;code&gt;fetch&lt;/code&gt; event.&lt;/p&gt;
&lt;p&gt;For example, in a &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-strategies/#cache-first-cache-falling-back-to-network&quot; rel=&quot;noopener&quot;&gt;Cache First&lt;/a&gt; strategy the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Request&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Request&lt;/code&gt;&lt;/a&gt; will be fulfilled using the cached response (if available). If there isn&#39;t a cached response, the &lt;code&gt;Request&lt;/code&gt; will be fulfilled by a network request and the response will be cached.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;registerRoute&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;workbox-routing&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;CacheFirst&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;workbox-strategies&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;registerRoute&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;RegExp&lt;/span&gt;&lt;span class=&quot;token punctuation&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;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;CacheFirst&lt;/span&gt;&lt;span class=&quot;token punctuation&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;Caching strategies can be customized with &lt;a href=&quot;https://developer.chrome.com/docs/workbox/using-plugins/&quot; rel=&quot;noopener&quot;&gt;Workbox plugins&lt;/a&gt;. These allow you to add additional behaviors by manipulating requests and responses during the lifecycle of a request. Workbox has several built-in plugins for common cases and APIs, but you can also define a &lt;a href=&quot;https://developer.chrome.com/docs/workbox/using-plugins/#methods-for-custom-plugins&quot; rel=&quot;noopener&quot;&gt;custom plugin&lt;/a&gt;, and introduce some custom logic of your choice.&lt;/p&gt;
&lt;p&gt;To achieve adapting loading, define a custom plugin, called, for example, &lt;code&gt;adaptiveLoadingPlugin&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; adaptiveLoadingPlugin &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-variable function&quot;&gt;requestWillFetch&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token 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; urlParts &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; request&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 function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; imageQuality&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;      navigator &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connection&lt;br /&gt;        &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;effectiveType&lt;br /&gt;        &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;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 keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;3g&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;        imageQuality &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;q_30&#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;break&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;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; newUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; urlParts&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;splice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;urlParts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &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 number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; imageQuality&lt;span 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;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;.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;.png&#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; newRequest &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;Request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newUrl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;href&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; newRequest&lt;span 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 previous code does the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Implements a &lt;code&gt;requestWillFetch()&lt;/code&gt; callback: This is called whenever a network request is about to be made, so you can alter the &lt;code&gt;Request&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Checks the connection type, by using the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/NavigatorConcurrentHardware/hardwareConcurrency&quot; rel=&quot;noopener&quot;&gt;Network Information API&lt;/a&gt;. Based on the status of the network, it creates a new URL part, indicating the quality of the image to fetch (e.g. &lt;code&gt;q_30&lt;/code&gt; for 3G users).&lt;/li&gt;
&lt;li&gt;Creates a new URL based on the dynamic &lt;code&gt;newPart&lt;/code&gt; value, and returns the new &lt;code&gt;Request&lt;/code&gt; to be made, based on that URL.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next, pass the plugin to a &lt;code&gt;cacheFirst&lt;/code&gt; strategy containing a regular expression to match image URLs (e.g. &lt;code&gt;/img/&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;highlight-line&quot;&gt;workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;routing&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;registerRoute&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;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegExp&lt;/span&gt;&lt;span class=&quot;token punctuation&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;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;strategies&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cacheFirst&lt;/span&gt;&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 literal-property property&quot;&gt;cacheName&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;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&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&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      adaptiveLoadingPlugin&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;      workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;expiration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Plugin&lt;/span&gt;&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 literal-property property&quot;&gt;maxEntries&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&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;purgeOnQuotaError&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&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;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&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;As a result, when requests for images are intercepted, the runtime caching strategy will try to fulfill the request from the cache. If it&#39;s not available, it will run the logic in the plugin, to decide which image quality to fetch from the network.&lt;/p&gt;
&lt;p&gt;Finally the response will be persisted in the cache, and sent back to the page.&lt;/p&gt;
&lt;h2 id=&quot;cloudinary-workbox-plugin&quot;&gt;Cloudinary Workbox Plugin &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/adaptive-loading-with-service-workers/#cloudinary-workbox-plugin&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Cloudinary, a video and image hosting service, has a &lt;a href=&quot;https://www.npmjs.com/package/cloudinary-workbox-plugin&quot; rel=&quot;noopener&quot;&gt;Workbox Plugin&lt;/a&gt; that encapsulates the functionality explained in the previous section, making it even easier to implement.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Cloudinary and Workbox logos.&quot; decoding=&quot;async&quot; height=&quot;269&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 637px) 637px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/iY2R0e4PvimaoVORo8Go.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/iY2R0e4PvimaoVORo8Go.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/iY2R0e4PvimaoVORo8Go.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/iY2R0e4PvimaoVORo8Go.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/iY2R0e4PvimaoVORo8Go.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/iY2R0e4PvimaoVORo8Go.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/iY2R0e4PvimaoVORo8Go.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/iY2R0e4PvimaoVORo8Go.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/iY2R0e4PvimaoVORo8Go.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/iY2R0e4PvimaoVORo8Go.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/iY2R0e4PvimaoVORo8Go.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/iY2R0e4PvimaoVORo8Go.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/iY2R0e4PvimaoVORo8Go.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/iY2R0e4PvimaoVORo8Go.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/iY2R0e4PvimaoVORo8Go.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/iY2R0e4PvimaoVORo8Go.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/iY2R0e4PvimaoVORo8Go.png?auto=format&amp;w=1274 1274w&quot; width=&quot;637&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;The plugin is designed to work with the &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-webpack-plugin/&quot; rel=&quot;noopener&quot;&gt;Workbox webpack plugin&lt;/a&gt;. To implement it, use the &lt;a href=&quot;https://developer.chrome.com/docs/workbox/reference/workbox-webpack-plugin/#type-GenerateSW&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;GenerateSW()&lt;/code&gt;&lt;/a&gt; class:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;workboxPlugin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GenerateSW&lt;/span&gt;&lt;span class=&quot;token punctuation&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;swDest&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;sw.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;importScripts&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;./cloudinaryPlugin.js&#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 literal-property property&quot;&gt;runtimeCaching&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 literal-property property&quot;&gt;urlPattern&lt;/span&gt;&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;RegExp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;^https://res.cloudinary.com/.*/image/upload/&#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 literal-property property&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;CacheFirst&#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;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;cacheName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;cloudinary-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;br /&gt;          &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token function-variable function&quot;&gt;requestWillFetch&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;&lt;br /&gt;              cloudinaryPlugin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;requestWillFetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&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;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;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The previous code does the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Uses the &lt;code&gt;GenerateSW()&lt;/code&gt; class to configure webpack to generate a service worker in the destination indicated in &lt;code&gt;swDest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Imports the cloudinary plugin script.&lt;/li&gt;
&lt;li&gt;Defines a Cache First runtime caching strategy for requests for images to the Cloudinary CDN.&lt;/li&gt;
&lt;li&gt;Passes the &lt;a href=&quot;https://www.npmjs.com/package/cloudinary-workbox-plugin&quot; rel=&quot;noopener&quot;&gt;Cloudinary Workbox Plugin&lt;/a&gt; to adjust the image quality according to the network conditions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;explore-more-adaptive-loading-strategies&quot;&gt;Explore more adaptive loading strategies &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/adaptive-loading-with-service-workers/#explore-more-adaptive-loading-strategies&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can go beyond this, by mapping device signals, like &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/NavigatorConcurrentHardware/hardwareConcurrency&quot; rel=&quot;noopener&quot;&gt;hardware concurrency&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Navigator/deviceMemory&quot; rel=&quot;noopener&quot;&gt;device memory&lt;/a&gt; to device categories and then serving different assets depending on the device type (low-, mid- or high-end).&lt;/p&gt;
</content>
    <author>
      <name>Demian Renzulli</name>
    </author><author>
      <name>Jeff Posnick</name>
    </author>
  </entry>
  
  <entry>
    <title>App shell UX with service workers and streams</title>
    <link href="https://web.dev/app-shell-ux-with-service-workers/"/>
    <updated>2020-06-23T00:00:00Z</updated>
    <id>https://web.dev/app-shell-ux-with-service-workers/</id>
    <content type="html" mode="escaped">&lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;fhqCwDP69PI&quot; videoStartAt=&quot;438&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Single-page_application&quot; rel=&quot;noopener&quot;&gt;Single-page app (SPA)&lt;/a&gt; is an architectural pattern in which the browser runs JavaScript code to update the existing page when the user visits a different section of the site, as opposed to loading an entire new page.&lt;/p&gt;
&lt;p&gt;This means that the web app doesn&#39;t perform an actual page reload. The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/History_API&quot; rel=&quot;noopener&quot;&gt;History API&lt;/a&gt; is used instead to navigate back and forth through the user&#39;s history and manipulate the contents of the history stack.&lt;/p&gt;
&lt;p&gt;Using this type of architecture can provide an &lt;a href=&quot;https://web.dev/learn/pwa/architecture/&quot;&gt;app shell UX&lt;/a&gt; that&#39;s fast, reliable, and usually consumes less data when navigating.&lt;/p&gt;
&lt;p&gt;In multi-page apps (MPAs) each time a user navigates to a new URL, the browser progressively renders HTML specific to that page. This means a full page refresh every time you visit a new page.&lt;/p&gt;
&lt;p&gt;While both are equally valid models to use, you might want to bring some of the benefits of the app shell UX of SPAs to your existing MPA site.
In this article we&#39;ll analyze how you can achieve an SPA-like architecture in multi-page apps by combining partials, service workers, and streams.&lt;/p&gt;
&lt;h2 id=&quot;production-case&quot;&gt;Production case &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/app-shell-ux-with-service-workers/#production-case&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://dev.to/&quot; rel=&quot;noopener&quot;&gt;DEV&lt;/a&gt; is a community where software developers write articles, take part in discussions, and build their professional profiles.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of https://dev.to&quot; decoding=&quot;async&quot; height=&quot;482&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vk2qqXg5PmLV7oCR7xVh.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vk2qqXg5PmLV7oCR7xVh.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vk2qqXg5PmLV7oCR7xVh.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vk2qqXg5PmLV7oCR7xVh.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vk2qqXg5PmLV7oCR7xVh.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vk2qqXg5PmLV7oCR7xVh.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vk2qqXg5PmLV7oCR7xVh.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vk2qqXg5PmLV7oCR7xVh.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vk2qqXg5PmLV7oCR7xVh.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vk2qqXg5PmLV7oCR7xVh.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vk2qqXg5PmLV7oCR7xVh.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vk2qqXg5PmLV7oCR7xVh.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vk2qqXg5PmLV7oCR7xVh.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vk2qqXg5PmLV7oCR7xVh.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vk2qqXg5PmLV7oCR7xVh.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vk2qqXg5PmLV7oCR7xVh.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vk2qqXg5PmLV7oCR7xVh.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Vk2qqXg5PmLV7oCR7xVh.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Their architecture is a multi-page app based on traditional backend templating through Ruby on Rails. Their team was interested in some of the benefits of an app shell model, but didn&#39;t want to undertake a major architectural change or move away from their original tech stack.&lt;/p&gt;
&lt;p&gt;Here&#39;s how their solution works:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First, they create partials of their home page for the header and the footer (&lt;code&gt;shell_top.html&lt;/code&gt; and &lt;code&gt;shell_bottom.html&lt;/code&gt;) and deliver them as standalone HTML snippets with an endpoint. These assets are added to the cache at the service worker &lt;code&gt;install&lt;/code&gt; event (what&#39;s commonly referred to as &lt;a href=&quot;https://web.dev/precache-with-workbox/&quot;&gt;precaching&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;When a navigation request is intercepted by the service worker, they create a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/ReadableStream&quot; rel=&quot;noopener&quot;&gt;streamed response&lt;/a&gt; by combining the cached header and footer with the main page content that just came from the server. The body is the only actual part of the page that requires fetching data from the network.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Dev&amp;#x27;s architecture consisting on static headers and footers that are cached and a body requested from the network.&quot; decoding=&quot;async&quot; height=&quot;363&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QkGCrnzggZmrp1PXrbHb.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QkGCrnzggZmrp1PXrbHb.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QkGCrnzggZmrp1PXrbHb.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QkGCrnzggZmrp1PXrbHb.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QkGCrnzggZmrp1PXrbHb.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QkGCrnzggZmrp1PXrbHb.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QkGCrnzggZmrp1PXrbHb.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QkGCrnzggZmrp1PXrbHb.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QkGCrnzggZmrp1PXrbHb.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QkGCrnzggZmrp1PXrbHb.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QkGCrnzggZmrp1PXrbHb.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QkGCrnzggZmrp1PXrbHb.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QkGCrnzggZmrp1PXrbHb.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QkGCrnzggZmrp1PXrbHb.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QkGCrnzggZmrp1PXrbHb.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QkGCrnzggZmrp1PXrbHb.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QkGCrnzggZmrp1PXrbHb.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/QkGCrnzggZmrp1PXrbHb.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;The key element of this solution is the usage of &lt;a href=&quot;https://developer.chrome.com/blog/sw-readablestreams/&quot; rel=&quot;noopener&quot;&gt;streams&lt;/a&gt;, which enables &lt;a href=&quot;https://streams.spec.whatwg.org/#intro&quot; rel=&quot;noopener&quot;&gt;incremental creations and updates&lt;/a&gt; of data sources. The Streams API also provides an interface for reading or writing asynchronous chunks of data, only a subset of which might be available in memory at any given time.
This way, the header of the page can be rendered as soon as it&#39;s picked from the cache, while the rest of the content is being fetched from the network. As a result, the navigation experience is so fast that users don&#39;t perceive an actual page refresh, only the new content (the body) being updated.&lt;/p&gt;
&lt;p&gt;The resulting UX is similar to the app shell UX pattern of SPAs, implemented on a MPA site.&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 previous section contains a quick summary of DEV&#39;s solution. For a more detailed explanation check out their &lt;a href=&quot;https://dev.to/devteam/instant-webpages-and-terabytes-of-data-savings-through-the-magic-of-service-workers-1mkc&quot;&gt;blog post&lt;/a&gt; on this topic. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;implement-an-app-shell-ux-architecture-in-mpas-with-workbox&quot;&gt;Implement an app shell UX architecture in MPAs with Workbox &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/app-shell-ux-with-service-workers/#implement-an-app-shell-ux-architecture-in-mpas-with-workbox&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this section we&#39;ll cover a summary of the different parts involved in implementing an app shell UX architecture in MPAs.
For a more detailed post on how to implement this on a real site, check out &lt;a href=&quot;https://developer.chrome.com/blog/beyond-spa/&quot; rel=&quot;noopener&quot;&gt;Beyond SPAs: alternative architectures for your PWA&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;the-server&quot;&gt;The server &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/app-shell-ux-with-service-workers/#the-server&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;partials&quot;&gt;Partials &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/app-shell-ux-with-service-workers/#partials&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The first step is to adopt a site structure based on HTML partials. These are just modular pieces of your pages that can be reused across your site and also delivered as standalone HTML snippets.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;head partial&lt;/strong&gt; can contain all the logic needed to style and render the header of the page. The &lt;strong&gt;navbar partial&lt;/strong&gt; can contain the logic for the navigation bar, the &lt;strong&gt;footer partial&lt;/strong&gt; the code that needs to execute there, and so forth.&lt;/p&gt;
&lt;p&gt;The first time the user visits the site, your server generates a response by assembling the different parts of the page:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;routes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;index&#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 keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&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;  res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;headPartial &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; navbarPartial&lt;span class=&quot;token punctuation&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; tag &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;query&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tag &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFAULT_TAG&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 keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;requestData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;…&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;templates&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tag&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;footPartial&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&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;By using the &lt;code&gt;res&lt;/code&gt; (response) object&#39;s &lt;a href=&quot;https://nodejs.org/api/http.html#http_response_write_chunk_encoding_callback&quot; rel=&quot;noopener&quot;&gt;write() method&lt;/a&gt;, and referencing locally stored partial templates, the response can be &lt;a href=&quot;https://github.com/substack/stream-handbook&quot; rel=&quot;noopener&quot;&gt;streamed&lt;/a&gt; immediately, without getting blocked by any external data source. The browser takes this initial HTML and renders a meaningful interface and loading message right away.&lt;/p&gt;
&lt;p&gt;The next portion of the page uses API data, which involves a network request. The web app can&#39;t render anything else until it gets a response back and processes it, but at least users aren&#39;t staring at a blank screen while they wait.&lt;/p&gt;
&lt;h3 id=&quot;the-service-worker&quot;&gt;The service worker &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/app-shell-ux-with-service-workers/#the-service-worker&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The first time a user visits a site, the header of the page will be rendered faster, without having to wait for the body of the page. The browser still needs to go to the network to fetch the rest of the page.&lt;/p&gt;
&lt;p&gt;After the first page load, the service worker is registered, allowing you to fetch the partials for the different static parts of the page (header, navbar, footer, etc.) from the cache.&lt;/p&gt;
&lt;h4 id=&quot;precaching-static-assets&quot;&gt;Precaching static assets &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/app-shell-ux-with-service-workers/#precaching-static-assets&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The first step is to cache the partial HTML templates, so they are immediately available.
With &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-precaching/&quot; rel=&quot;noopener&quot;&gt;Workbox precaching&lt;/a&gt; you can store these files at the &lt;code&gt;install&lt;/code&gt; event of the service worker and keep them up to date when changes are deployed to the web app.&lt;/p&gt;
&lt;p&gt;Depending on the build process, Workbox has different solutions to generate a service worker and indicate the list of files to precache, including &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-webpack-plugin/&quot; rel=&quot;noopener&quot;&gt;webpack&lt;/a&gt; and &lt;a href=&quot;https://developers.google.com/codelabs/workbox-lab#0&quot; rel=&quot;noopener&quot;&gt;gulp&lt;/a&gt; plugins, a &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-build/&quot; rel=&quot;noopener&quot;&gt;generic node module&lt;/a&gt; and a &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-cli/&quot; rel=&quot;noopener&quot;&gt;command line interface&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For a partials configuration like the one described earlier, the resulting service worker file should contain something similar to the following:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;precaching&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;precacheAndRoute&lt;/span&gt;&lt;span class=&quot;token punctuation&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;url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;partials/about.html&#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;revision&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;518747aad9d7e&#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 punctuation&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 string&quot;&gt;&#39;partials/foot.html&#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;revision&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;69bf746a9ecc6&#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 comment&quot;&gt;// etc.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h4 id=&quot;streaming&quot;&gt;Streaming &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/app-shell-ux-with-service-workers/#streaming&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Next, add the service worker logic so that the precached partial HTML can be sent back to the web app immediately. This is a crucial part of being reliably fast. Using the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Streams_API&quot; rel=&quot;noopener&quot;&gt;Streams API&lt;/a&gt; within our service worker makes that possible.
&lt;a href=&quot;https://developer.chrome.com/docs/workbox/reference/workbox-streams/&quot; rel=&quot;noopener&quot;&gt;Workbox Streams&lt;/a&gt; abstracts the details of how streaming works. The package lets you pass to the library a mix of streaming sources, both from caches and runtime data that might come from the network. Workbox takes care of coordinating the individual sources and stitching them together into a single, streaming response.&lt;/p&gt;
&lt;p&gt;First, set up the strategies in Workbox to handle the different sources that will make up the streaming response.&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; cacheStrategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;strategies&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cacheFirst&lt;/span&gt;&lt;span class=&quot;token punctuation&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;cacheName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;core&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cacheNames&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;precache&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; apiStrategy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;strategies&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;staleWhileRevalidate&lt;/span&gt;&lt;span class=&quot;token punctuation&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;cacheName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;API_CACHE_NAME&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;workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;expiration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Plugin&lt;/span&gt;&lt;span class=&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;maxEntries&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;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;The first strategy reads data that&#39;s been precached, like the partial HTML templates.&lt;/li&gt;
&lt;li&gt;The second strategy implements the &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-strategies/#stale-while-revalidate&quot; rel=&quot;noopener&quot;&gt;stale-while-revalidate&lt;/a&gt; caching logic, along with &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-expiration/#restrict-the-number-of-cache-entries&quot; rel=&quot;noopener&quot;&gt;least-recently-used cache expiration&lt;/a&gt; logic once we reach 50 entries.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next, tell Workbox how to use the strategies to construct a complete, streaming response, by passing in an array of sources as functions to execute immediately:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;streams&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;token punctuation&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 operator&quot;&gt;=&gt;&lt;/span&gt; cacheStrategy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&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;request&lt;/span&gt;&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;Request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCacheKeyForURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/head.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&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 operator&quot;&gt;=&gt;&lt;/span&gt; cacheStrategy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&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;request&lt;/span&gt;&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;Request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCacheKeyForURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/navbar.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&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;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;event&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&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; tag &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;searchParams&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;tag&#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 constant&quot;&gt;DEFAULT_TAG&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; listResponse &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; apiStrategy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;…&lt;span class=&quot;token punctuation&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; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; listResponse&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; templates&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tag&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; cacheStrategy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&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;request&lt;/span&gt;&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;Request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCacheKeyForURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/foot.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&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;ul&gt;
&lt;li&gt;The first two sources are precached partial templates read directly from the service worker&#39;s cache, so they&#39;ll always be available immediately.&lt;/li&gt;
&lt;li&gt;The next source function fetches data from the network, and processes the response into the HTML that the web app expects.&lt;/li&gt;
&lt;li&gt;Finally, a cached copy of the footer and closing HTML tags is streamed to complete the response.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Workbox takes the result from each source and streams it to the web app, in sequence, only delaying if the next function in the array hasn&#39;t completed yet.
As a result, the user immediately sees the page being painted. The experience is so fast that when navigating the header stays in its position without making the user perceive the full page refresh. This is very similar to the UX that the app shell SPA model provides.&lt;/p&gt;
</content>
    <author>
      <name>Demian Renzulli</name>
    </author><author>
      <name>Jeff Posnick</name>
    </author>
  </entry>
  
  <entry>
    <title>Instant navigation experiences</title>
    <link href="https://web.dev/instant-navigation-experiences/"/>
    <updated>2020-06-23T00:00:00Z</updated>
    <id>https://web.dev/instant-navigation-experiences/</id>
    <content type="html" mode="escaped">&lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;fhqCwDP69PI&quot; videoStartAt=&quot;285&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
&lt;p&gt;Performing a task on a site commonly involves several steps. For example, purchasing a product in an e-commerce website might involve searching for a product, picking an item from the list of results, adding the item to the cart, and completing the operation by checking out.&lt;/p&gt;
&lt;p&gt;In technical terms, moving through different pages means making a &lt;strong&gt;navigation request&lt;/strong&gt;. As a general rule, you &lt;strong&gt;don&#39;t&lt;/strong&gt; want to use long-lived &lt;code&gt;Cache-Control&lt;/code&gt; headers to cache the HTML response for a navigation request. They should normally be satisfied via the network, with &lt;code&gt;Cache-Control: no-cache&lt;/code&gt;, to ensure that the HTML, along with the chain of subsequent network requests, is (reasonably) fresh.
Having to go against the network each time the user navigates to a new page unfortunately means that each navigation might be slow—at the very least, it means that it won&#39;t be &lt;em&gt;reliably&lt;/em&gt; fast.&lt;/p&gt;
&lt;p&gt;To speed up these requests, if you can anticipate the user&#39;s action, you can request these pages and assets beforehand and keep them in the cache for a short period of time until the user clicks on these links. This technique is called &lt;a href=&quot;https://web.dev/link-prefetch/&quot;&gt;prefetching&lt;/a&gt; and it&#39;s commonly implemented by adding &lt;code&gt;&amp;lt;link rel=&amp;quot;prefetch&amp;quot;&amp;gt;&lt;/code&gt; tags to pages, indicating the resource to prefetch.&lt;/p&gt;
&lt;p&gt;In this guide we&#39;ll explore different ways in which &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Service_Worker_API&quot; rel=&quot;noopener&quot;&gt;service workers&lt;/a&gt; can be used as a complement of traditional prefetching techniques.&lt;/p&gt;
&lt;h2 id=&quot;production-cases&quot;&gt;Production cases &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/instant-navigation-experiences/#production-cases&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.mercadolibre.com.ar/&quot; rel=&quot;noopener&quot;&gt;MercadoLibre&lt;/a&gt; is the biggest e-commerce site in Latin America. To speed up navigations, they dynamically inject &lt;code&gt;&amp;lt;link rel=&amp;quot;prefetch&amp;quot;&amp;gt;&lt;/code&gt; tags in some parts of the flow. For example, in listing pages, they fetch the next result page as soon as the user scrolls to the bottom of the listing:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of MercadoLibre&amp;#x27;s listing pages one and two and a Link Prefetch tag connecting both.&quot; decoding=&quot;async&quot; height=&quot;397&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 682px) 682px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/80D6QavdktSNb6xnhXE0.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/80D6QavdktSNb6xnhXE0.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/80D6QavdktSNb6xnhXE0.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/80D6QavdktSNb6xnhXE0.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/80D6QavdktSNb6xnhXE0.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/80D6QavdktSNb6xnhXE0.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/80D6QavdktSNb6xnhXE0.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/80D6QavdktSNb6xnhXE0.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/80D6QavdktSNb6xnhXE0.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/80D6QavdktSNb6xnhXE0.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/80D6QavdktSNb6xnhXE0.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/80D6QavdktSNb6xnhXE0.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/80D6QavdktSNb6xnhXE0.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/80D6QavdktSNb6xnhXE0.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/80D6QavdktSNb6xnhXE0.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/80D6QavdktSNb6xnhXE0.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/80D6QavdktSNb6xnhXE0.png?auto=format&amp;w=1364 1364w&quot; width=&quot;682&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Prefetched files are requested at the &amp;quot;Lowest&amp;quot; priority and stored in the &lt;a href=&quot;https://web.dev/http-cache/&quot;&gt;HTTP cache&lt;/a&gt; or the &lt;a href=&quot;https://calendar.perfplanet.com/2016/a-tale-of-four-caches/&quot; rel=&quot;noopener&quot;&gt;memory cache&lt;/a&gt; (depending on whether the resource is cacheable or not), for an amount of time that varies by browsers. For example, as of Chrome 85, this value is 5 minutes. Resources are kept around for five minutes, after which the normal &lt;code&gt;Cache-Control&lt;/code&gt; rules for the resource apply.&lt;/p&gt;
&lt;p&gt;Using service worker caching can help you extend the lifetime of prefetch resources beyond the five-minute window.&lt;/p&gt;
&lt;p&gt;For example, Italian sports portal &lt;a href=&quot;https://sport.virgilio.it/&quot; rel=&quot;noopener&quot;&gt;Virgilio Sport&lt;/a&gt; uses service workers to prefetch the most popular posts in their home page. They also use the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Network_Information_API&quot; rel=&quot;noopener&quot;&gt;Network Information API&lt;/a&gt; to avoid prefetching for users that are on a 2G connection.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Virgilio Sport logo.&quot; decoding=&quot;async&quot; height=&quot;100&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 340px) 340px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bqiSoliDKZ9SR1NX2Ek3.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bqiSoliDKZ9SR1NX2Ek3.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bqiSoliDKZ9SR1NX2Ek3.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bqiSoliDKZ9SR1NX2Ek3.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bqiSoliDKZ9SR1NX2Ek3.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bqiSoliDKZ9SR1NX2Ek3.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bqiSoliDKZ9SR1NX2Ek3.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bqiSoliDKZ9SR1NX2Ek3.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bqiSoliDKZ9SR1NX2Ek3.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bqiSoliDKZ9SR1NX2Ek3.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bqiSoliDKZ9SR1NX2Ek3.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bqiSoliDKZ9SR1NX2Ek3.png?auto=format&amp;w=680 680w&quot; width=&quot;340&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;As a result of this, over 3 weeks of observation Virgilio Sport witnessed load times for navigation to articles improve &lt;strong&gt;78%&lt;/strong&gt;, and the number of article impressions increase &lt;strong&gt;45%&lt;/strong&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of Virgilio Sport home and article pages, with impact metrics after prefetching.&quot; decoding=&quot;async&quot; height=&quot;442&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 536px) 536px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/wn7OR4CA21QJUYhs8OUu.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/wn7OR4CA21QJUYhs8OUu.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/wn7OR4CA21QJUYhs8OUu.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/wn7OR4CA21QJUYhs8OUu.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/wn7OR4CA21QJUYhs8OUu.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/wn7OR4CA21QJUYhs8OUu.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/wn7OR4CA21QJUYhs8OUu.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/wn7OR4CA21QJUYhs8OUu.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/wn7OR4CA21QJUYhs8OUu.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/wn7OR4CA21QJUYhs8OUu.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/wn7OR4CA21QJUYhs8OUu.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/wn7OR4CA21QJUYhs8OUu.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/wn7OR4CA21QJUYhs8OUu.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/wn7OR4CA21QJUYhs8OUu.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/wn7OR4CA21QJUYhs8OUu.png?auto=format&amp;w=1072 1072w&quot; width=&quot;536&quot; /&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;implement-precaching-with-workbox&quot;&gt;Implement precaching with Workbox &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/instant-navigation-experiences/#implement-precaching-with-workbox&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the following section we&#39;ll use &lt;a href=&quot;https://web.dev/workbox/&quot;&gt;Workbox&lt;/a&gt; to show how to implement different caching techniques in the service worker that can be used as a complement to &lt;code&gt;&amp;lt;link rel=&amp;quot;prefetch&amp;quot;&amp;gt;&lt;/code&gt;, or even a replacement for it, by delegating this task completely to the service worker.&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; You must take steps to ensure that adding a service worker to your site doesn&#39;t end up actually slowing down your navigations. Starting up the service worker without using it to respond to a navigation request will introduce a small amount of latency (as explained in &lt;a href=&quot;https://www.youtube.com/watch?v=25aCD5XL1Jk&quot;&gt;Building Faster, More Resilient Apps with Service Workers&lt;/a&gt;). You can mitigate this overhead by enabling a feature called &lt;a href=&quot;https://developer.chrome.com/blog/navigation-preload/&quot;&gt;navigation preload&lt;/a&gt;, and then using the &lt;a href=&quot;https://developer.chrome.com/blog/navigation-preload/#using-the-preloaded-response&quot;&gt;network response&lt;/a&gt; that&#39;s been preloaded inside of your fetch event handler. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;1-precache-static-pages-and-page-subresources&quot;&gt;1. Precache static pages and page subresources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/instant-navigation-experiences/#1-precache-static-pages-and-page-subresources&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/precache-with-workbox/&quot;&gt;Precaching&lt;/a&gt; is the ability of the service worker to save files to the cache while it&#39;s installing.&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; Precaching sounds similar to prefetching, but it&#39;s a different technique. In the first one, the service worker fetches and stores resources (typically static files) while it&#39;s installing and keeps them in the cache until a new version of the file is available. In the second, resources are requested ahead of time to have it in the cache for brief periods of time in order to speed up subsequent navigations. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;In the following cases precaching is used to achieve a similar goal as prefetching: making navigations faster.&lt;/p&gt;
&lt;h4 id=&quot;precaching-static-pages&quot;&gt;Precaching static pages &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/instant-navigation-experiences/#precaching-static-pages&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;For pages that are generated at build time (e.g. &lt;code&gt;about.html&lt;/code&gt;, &lt;code&gt;contact.html&lt;/code&gt;), or in completely static sites, one can just add the site&#39;s documents to the precache list, so they are already available in the cache every time the user accesses them:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;precaching&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;precacheAndRoute&lt;/span&gt;&lt;span class=&quot;token punctuation&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 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 string&quot;&gt;&#39;/about.html&#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;revision&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;abcd1234&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// ... other entries ...&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;precaching-page-subresources&quot;&gt;Precaching page subresources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/instant-navigation-experiences/#precaching-page-subresources&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Precaching static assets that the different sections of the site might use (e.g. JavaScript, CSS, etc.), is a general best practice and can give an extra boost in prefetching scenarios.&lt;/p&gt;
&lt;p&gt;To speed up navigations in an e-commerce site, you can use  &lt;code&gt;&amp;lt;link rel=&amp;quot;prefetch&amp;quot;&amp;gt;&lt;/code&gt; tags in listing pages to prefetch product detail pages for the first few products of a listing page. If you have already precached the product page subresources, this can make the navigation even faster.&lt;/p&gt;
&lt;p&gt;To implement this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add a &lt;code&gt;&amp;lt;link rel=&amp;quot;prefetch&amp;quot;&amp;gt;&lt;/code&gt; tag to the page:&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;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;prefetch&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/phones/smartphone-5x.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;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Add the page subresources to the precache list in the service worker:&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;workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;precaching&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;precacheAndRoute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token string&quot;&gt;&#39;/styles/product-page.ac29.css&#39;&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;// ... other entries ...&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;2-extend-the-lifetime-of-prefetch-resources&quot;&gt;2. Extend the lifetime of prefetch resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/instant-navigation-experiences/#2-extend-the-lifetime-of-prefetch-resources&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As mentioned earlier, &lt;code&gt;&amp;lt;link rel=&amp;quot;prefetch&amp;quot;&amp;gt;&lt;/code&gt; fetches and keeps resources in the HTTP cache for a limited amount of time, after which point the &lt;code&gt;Cache-Control&lt;/code&gt; rules for a resource apply. As of Chrome 85, this value is 5 minutes.&lt;/p&gt;
&lt;p&gt;Service workers allow you to extend the lifetime of the prefetch pages, while providing the added benefit of making those resources available for offline usage.&lt;/p&gt;
&lt;p&gt;In the previous example, one could complement the &lt;code&gt;&amp;lt;link rel=&amp;quot;prefetch&amp;quot;&amp;gt;&lt;/code&gt; used to prefetch a product page with a &lt;a href=&quot;https://web.dev/runtime-caching-with-workbox/&quot;&gt;Workbox runtime caching strategy&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To implement that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add a &lt;code&gt;&amp;lt;link rel=&amp;quot;prefetch&amp;quot;&amp;gt;&lt;/code&gt; tag to the page:&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;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;prefetch&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/phones/smartphone-5x.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;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Implement a runtime caching strategy in the service worker for these types of requests:&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;workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;strategies&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StaleWhileRevalidate&lt;/span&gt;&lt;span class=&quot;token punctuation&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;cacheName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;document-cache&#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;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;expiration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Plugin&lt;/span&gt;&lt;span class=&quot;token punctuation&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;maxAgeSeconds&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&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;span class=&quot;token number&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 30 Days&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In this case, we have opted to use a &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-strategies/#stale-while-revalidate&quot; rel=&quot;noopener&quot;&gt;stale-while-revalidate strategy&lt;/a&gt;. In this strategy, pages can be requested from both the cache and the network, in parallel. The response comes from the cache if available, otherwise from the network. The cache is always kept up to date with the network response with each successful request.&lt;/p&gt;
&lt;h3 id=&quot;3-delegate-prefetching-to-the-service-worker&quot;&gt;3. Delegate prefetching to the service worker &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/instant-navigation-experiences/#3-delegate-prefetching-to-the-service-worker&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In most cases the best approach is to use &lt;code&gt;&amp;lt;link rel=&amp;quot;prefetch&amp;quot;&amp;gt;&lt;/code&gt;. The tag is a &lt;a href=&quot;https://www.w3.org/TR/resource-hints/&quot; rel=&quot;noopener&quot;&gt;resource hint&lt;/a&gt; designed to make prefetching as efficient as possible.&lt;/p&gt;
&lt;p&gt;In some cases, though, it might be better to delegate this task completely to the service worker.
For example: to prefetch the first few products in a client-side rendered product listing page, one might need to inject several &lt;code&gt;&amp;lt;link rel=&amp;quot;prefetch&amp;quot;&amp;gt;&lt;/code&gt; tags dynamically in the page, based on an API response. This can momentarily consume time on the page&#39;s main thread and make the implementation more difficult.&lt;/p&gt;
&lt;p&gt;In cases like this, use a &amp;quot;page to service worker communication strategy&amp;quot;, to delegate the task of prefetching completely to the service worker. This type of communication can be achieved by using &lt;a href=&quot;https://html.spec.whatwg.org/multipage/workers.html#dom-worker-postmessage&quot; rel=&quot;noopener&quot;&gt;worker.postMessage()&lt;/a&gt;:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;An icon of a page making two way communication with a service worker.&quot; decoding=&quot;async&quot; height=&quot;205&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 626px) 626px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vokHySREOo6Y3PpxzxRC.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vokHySREOo6Y3PpxzxRC.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vokHySREOo6Y3PpxzxRC.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vokHySREOo6Y3PpxzxRC.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vokHySREOo6Y3PpxzxRC.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vokHySREOo6Y3PpxzxRC.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vokHySREOo6Y3PpxzxRC.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vokHySREOo6Y3PpxzxRC.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vokHySREOo6Y3PpxzxRC.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vokHySREOo6Y3PpxzxRC.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vokHySREOo6Y3PpxzxRC.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vokHySREOo6Y3PpxzxRC.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vokHySREOo6Y3PpxzxRC.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vokHySREOo6Y3PpxzxRC.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vokHySREOo6Y3PpxzxRC.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vokHySREOo6Y3PpxzxRC.png?auto=format&amp;w=1252 1252w&quot; width=&quot;626&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-window/&quot; rel=&quot;noopener&quot;&gt;Workbox Window package&lt;/a&gt; simplifies this type of communication, abstracting many details of the underlying call being done.&lt;/p&gt;
&lt;p&gt;Prefetching with Workbox Window can be implemented in the following way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In the page: call the service worker passing it the type of message, and the list of URLs to prefetch:&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; wb &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;Workbox&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/sw.js&#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;wb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; prefetchResponse &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; wb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;messageSW&lt;/span&gt;&lt;span class=&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;PREFETCH_URLS&#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;urls&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 punctuation&quot;&gt;}&lt;/span&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;In the service worker: implement a message handler to issue a &lt;code&gt;fetch()&lt;/code&gt; request for each URL to prefetch:&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 function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;PREFETCH_URLS&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Fetch URLs and store them in the cache&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</content>
    <author>
      <name>Demian Renzulli</name>
    </author><author>
      <name>Jeff Posnick</name>
    </author><author>
      <name>Gilberto Cocchi</name>
    </author>
  </entry>
  
  <entry>
    <title>Resilient search experiences</title>
    <link href="https://web.dev/resilient-search-experiences/"/>
    <updated>2020-06-23T00:00:00Z</updated>
    <id>https://web.dev/resilient-search-experiences/</id>
    <content type="html" mode="escaped">&lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;fhqCwDP69PI&quot; videoStartAt=&quot;35&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
&lt;p&gt;Even in locations with fast networks a user might lose connection or connect to a flaky network, at some moments of the day.
For example: a user is on the subway searching on the phone for a product on an e-commerce website. They type the product name, click the &amp;quot;search&amp;quot; button, and while waiting for the results, the connection is lost, leading to the standard browser offline page.&lt;/p&gt;
&lt;p&gt;As a result, unless the user decides to come back to the site later, and repeat the same task, the site might lose a potential transaction and customer.&lt;/p&gt;
&lt;p&gt;To provide a more resilient search experience in these cases you can use the &lt;a href=&quot;https://developer.chrome.com/blog/background-sync/&quot; rel=&quot;noopener&quot;&gt;Background Sync API&lt;/a&gt;, which persists failed queries so they can be retried once the connection is recovered. This technique, in combination with &lt;a href=&quot;https://web.dev/push-notifications/&quot;&gt;Web Push Notifications&lt;/a&gt; lets you inform the user of the search results, allowing you to keep them engaged with your service.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-quaternary-box-bg color-quaternary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewbox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Code brackets&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M9.41 16.59L8 18l-6-6 6-6 1.41 1.41L4.83 12l4.58 4.59zm5.18-9.18L16 6l6 6-6 6-1.41-1.41L19.17 12l-4.58-4.59z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Try it&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Try the &lt;a href=&quot;https://web.dev/codelab-building-resilient-search-experiences&quot;&gt;Building resilient search experiences with Workbox&lt;/a&gt; for a hands-on demonstration of the ideas explained in this guide. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;production-case&quot;&gt;Production case &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/resilient-search-experiences/#production-case&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For concrete application of this technique let&#39;s take a look at Google Search for Chrome in Android.
When visiting the Google Search web app and going offline, instead of showing the standard network error page, the site serves a custom offline response, but allows users to enter their search query immediately.
The page also prompts the user to opt-in for notifications, to receive a link to the search results page once the connection is recovered.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of the background retry interface in Google Search.&quot; decoding=&quot;async&quot; height=&quot;475&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 257px) 257px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TqDtqgbKOsxRFnr2lNSy.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TqDtqgbKOsxRFnr2lNSy.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TqDtqgbKOsxRFnr2lNSy.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TqDtqgbKOsxRFnr2lNSy.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TqDtqgbKOsxRFnr2lNSy.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TqDtqgbKOsxRFnr2lNSy.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TqDtqgbKOsxRFnr2lNSy.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TqDtqgbKOsxRFnr2lNSy.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TqDtqgbKOsxRFnr2lNSy.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/TqDtqgbKOsxRFnr2lNSy.png?auto=format&amp;w=514 514w&quot; width=&quot;257&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;When the user performs a search, the service worker allows the query to be deferred and sent to Google&#39;s servers as soon as the device goes back online by using the &lt;a href=&quot;https://developer.chrome.com/blog/background-sync/&quot; rel=&quot;noopener&quot;&gt;Background Sync API&lt;/a&gt;, and to inform the user of the result by using the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Push_API&quot; rel=&quot;noopener&quot;&gt;Push API&lt;/a&gt;.&lt;/p&gt;
&lt;img alt=&quot;A screenshot of the offline flow in Google Search.&quot; decoding=&quot;async&quot; height=&quot;436&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/ZZItVQMLUPmVbwJlfDck.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/ZZItVQMLUPmVbwJlfDck.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/ZZItVQMLUPmVbwJlfDck.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/ZZItVQMLUPmVbwJlfDck.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/ZZItVQMLUPmVbwJlfDck.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/ZZItVQMLUPmVbwJlfDck.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/ZZItVQMLUPmVbwJlfDck.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/ZZItVQMLUPmVbwJlfDck.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/ZZItVQMLUPmVbwJlfDck.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/ZZItVQMLUPmVbwJlfDck.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/ZZItVQMLUPmVbwJlfDck.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/ZZItVQMLUPmVbwJlfDck.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/ZZItVQMLUPmVbwJlfDck.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/ZZItVQMLUPmVbwJlfDck.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/ZZItVQMLUPmVbwJlfDck.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/ZZItVQMLUPmVbwJlfDck.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/ZZItVQMLUPmVbwJlfDck.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/ZZItVQMLUPmVbwJlfDck.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Service workers allow Google Search to provide a &lt;a href=&quot;https://web.dev/google-search-sw/#meaningful-offline-experience&quot;&gt;meaningful offline experience&lt;/a&gt; and keep the user engaged, letting them complete their task.&lt;/p&gt;
&lt;h2 id=&quot;implement-resilient-search-experiences-with-workbox&quot;&gt;Implement resilient search experiences with Workbox &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/resilient-search-experiences/#implement-resilient-search-experiences-with-workbox&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While Google Search implements this functionality without using Workbox, the &lt;a href=&quot;https://developer.chrome.com/docs/workbox/&quot; rel=&quot;noopener&quot;&gt;Workbox library&lt;/a&gt; makes it easier by providing a &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-background-sync/&quot; rel=&quot;noopener&quot;&gt;Background Sync module&lt;/a&gt;, which takes care of many implementation details for us.&lt;/p&gt;
&lt;img alt=&quot;A service worker and a cache object communicating with each other.&quot; decoding=&quot;async&quot; height=&quot;383&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/X06meG8U60SABUabxwHb.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/X06meG8U60SABUabxwHb.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/X06meG8U60SABUabxwHb.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/X06meG8U60SABUabxwHb.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/X06meG8U60SABUabxwHb.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/X06meG8U60SABUabxwHb.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/X06meG8U60SABUabxwHb.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/X06meG8U60SABUabxwHb.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/X06meG8U60SABUabxwHb.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/X06meG8U60SABUabxwHb.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/X06meG8U60SABUabxwHb.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/X06meG8U60SABUabxwHb.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/X06meG8U60SABUabxwHb.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/X06meG8U60SABUabxwHb.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/X06meG8U60SABUabxwHb.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/X06meG8U60SABUabxwHb.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/X06meG8U60SABUabxwHb.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/X06meG8U60SABUabxwHb.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;To implement a resilient search experience in Workbox, first, import the following modules in your service worker:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;BackgroundSyncPlugin&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;workbox-background-sync&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;registerRoute&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;workbox-routing&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;NetworkOnly&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;workbox-strategies&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Next, create an instance of the &lt;a href=&quot;https://developer.chrome.com/docs/workbox/reference/workbox-background-sync/#type-BackgroundSyncPlugin&quot; rel=&quot;noopener&quot;&gt;workbox.backgroundSync plugin&lt;/a&gt;, to automatically add failed requests to a queue, so they can be retried later:&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; bgSyncPlugin &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;workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;backgroundSync&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Plugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;offlineQueryQueue&#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 literal-property property&quot;&gt;maxRetentionTime&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function-variable function&quot;&gt;onSync&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;queue&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entry &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; queue&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shiftRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&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; cache &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;offline-search-responses&#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; offlineUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;amp;notification=true&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;        cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;offlineUrl&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;offlineUrl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unshiftRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; error&lt;span 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;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The plugin receives the following parameters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;offlineQueryQueue&lt;/code&gt;: The name of the queue that will be used to persist the failed requests in &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/IndexedDB_API&quot; rel=&quot;noopener&quot;&gt;IndexedDB&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;maxRetentionTime&lt;/code&gt;: The amount of time in minutes a request may be retried, after which point they will be discarded.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;onSync&lt;/code&gt;: The callback that will be triggered when the connection is recovered. At that point, each failed request can be dequeued and processed, by calling &lt;code&gt;queue.shiftRequest()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, define a &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-strategies/#network-only&quot; rel=&quot;noopener&quot;&gt;networkOnly&lt;/a&gt; runtime caching strategy for requests to the search URL (e.g. &lt;code&gt;/search_action&lt;/code&gt;) and pass it the &lt;code&gt;bgSyncPlugin&lt;/code&gt; defined previously:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;routing&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;registerRoute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;  matchSearchUrl&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;workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;strategies&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NetworkOnly&lt;/span&gt;&lt;span class=&quot;token punctuation&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;bgSyncPlugin&lt;span 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;This tells Workbox to always go to the network when the service worker intercepts a request for the search endpoint, and to delegate to the Background Sync plugin the task of managing offline scenarios.&lt;/p&gt;
&lt;p&gt;As a result, when the user goes offline while searching, the query is automatically saved. When the connection is recovered the offline logic is triggered to process the request and inform the user of the result.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/resilient-search-experiences/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this article you learned how to implement a search experience capable of responding gracefully to offline scenarios, by combining the &lt;a href=&quot;https://developer.chrome.com/blog/background-sync/&quot; rel=&quot;noopener&quot;&gt;Background Sync API&lt;/a&gt; and the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Push_API&quot; rel=&quot;noopener&quot;&gt;Push API&lt;/a&gt;.
We used Workbox to show how to implement this feature, as it simplifies the process, but the same can be achieved by writing vanilla service worker code.&lt;/p&gt;
&lt;p&gt;In the code samples we focused on the core part of the feature: how requests are intercepted and managed by the service worker. For a step-by-step guide on how to implement this functionality, including the offline page and the notification logic, check out the codelab at the end of this article.&lt;/p&gt;
</content>
    <author>
      <name>Demian Renzulli</name>
    </author><author>
      <name>Jeff Posnick</name>
    </author>
  </entry>
  
  <entry>
    <title>Building resilient search experiences with Workbox</title>
    <link href="https://web.dev/codelab-building-resilient-search-experiences/"/>
    <updated>2020-06-23T00:00:00Z</updated>
    <id>https://web.dev/codelab-building-resilient-search-experiences/</id>
    <content type="html" mode="escaped">&lt;p&gt;This codelab shows you how to implement a resilient search experience with Workbox. The demo app it uses contains a search box that calls a server endpoint, and redirects the user to a basic HTML page.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; This codelab uses &lt;a href=&quot;https://www.google.com/chrome/&quot;&gt;Chrome DevTools&lt;/a&gt;. Download Chrome if you don&#39;t already have it. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;measure&quot;&gt;Measure &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-building-resilient-search-experiences/#measure&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before adding optimizations, it&#39;s always a good idea to first analyze the current state of the application.&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;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;In the new tab that just opened, check how the website behaves when going offline:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Network&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Open Chrome DevTools and select the Network panel.&lt;/li&gt;
&lt;li&gt;In the &lt;a href=&quot;https://developer.chrome.com/docs/devtools/network/reference/#throttling&quot; rel=&quot;noopener&quot;&gt;Throttling drop-down list&lt;/a&gt;, select &lt;strong&gt;Offline&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In the demo app enter a search query, then click the &lt;strong&gt;Search&lt;/strong&gt; button.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The standard browser error page is shown:&lt;/p&gt;
&lt;img alt=&quot;A screenshot of the default offline UX in the browser.&quot; decoding=&quot;async&quot; height=&quot;465&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/g4Naxj1RnipuqxqzC62x.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/g4Naxj1RnipuqxqzC62x.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/g4Naxj1RnipuqxqzC62x.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/g4Naxj1RnipuqxqzC62x.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/g4Naxj1RnipuqxqzC62x.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/g4Naxj1RnipuqxqzC62x.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/g4Naxj1RnipuqxqzC62x.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/g4Naxj1RnipuqxqzC62x.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/g4Naxj1RnipuqxqzC62x.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/g4Naxj1RnipuqxqzC62x.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/g4Naxj1RnipuqxqzC62x.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/g4Naxj1RnipuqxqzC62x.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/g4Naxj1RnipuqxqzC62x.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/g4Naxj1RnipuqxqzC62x.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/g4Naxj1RnipuqxqzC62x.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/g4Naxj1RnipuqxqzC62x.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/g4Naxj1RnipuqxqzC62x.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/g4Naxj1RnipuqxqzC62x.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h2 id=&quot;provide-a-fallback-response&quot;&gt;Provide a fallback response &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-building-resilient-search-experiences/#provide-a-fallback-response&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The service worker contains the code to add the offline page to the &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-precaching/#explanation-of-the-precache-list&quot; rel=&quot;noopener&quot;&gt;precache list&lt;/a&gt;, so it can always be cached at the service worker &lt;code&gt;install&lt;/code&gt; event.&lt;/p&gt;
&lt;p&gt;Usually you would need to instruct Workbox to add this file to the precache list at build time, by integrating the library with your build tool of choice (e.g. &lt;a href=&quot;https://webpack.js.org/&quot; rel=&quot;noopener&quot;&gt;webpack&lt;/a&gt; or &lt;a href=&quot;https://gulpjs.com/&quot; rel=&quot;noopener&quot;&gt;gulp&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;For simplicity, we&#39;ve already done it for you. The following code at &lt;code&gt;public/sw.js&lt;/code&gt; does that:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;FALLBACK_HTML_URL&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/index_offline.html&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;…&lt;br /&gt;workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;precaching&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;precacheAndRoute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;FALLBACK_HTML_URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&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; To learn more about how to integrate Workbox with build tools, check out the &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-webpack-plugin/&quot;&gt;webpack Workbox plugin&lt;/a&gt; and the &lt;a href=&quot;https://developers.google.com/codelabs/workbox-lab#0&quot;&gt;Gulp Workbox plugin&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Next, add code to use the offline page as a fallback response:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;To view the source, press &lt;strong&gt;View Source&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Add the following code to the bottom of &lt;code&gt;public/sw.js&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;routing&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setDefaultHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&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;workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;strategies&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NetworkOnly&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span 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;workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;routing&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCatchHandler&lt;/span&gt;&lt;span class=&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;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token 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;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;destination&lt;span class=&quot;token punctuation&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;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;document&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;FALLBACK_HTML_URL&lt;/span&gt;&lt;span class=&quot;token punctuation&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;break&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;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The Glitch UI says &lt;code&gt;workbox is not defined&lt;/code&gt; because it doesn&#39;t realize that the &lt;code&gt;importScripts()&lt;/code&gt; call on line 1 is importing the library. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The code does the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Defines a default &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-strategies/#network-only&quot; rel=&quot;noopener&quot;&gt;Network Only strategy&lt;/a&gt; that will apply to all requests.&lt;/li&gt;
&lt;li&gt;Declares a global error handler, by calling &lt;code&gt;workbox.routing.setCatchHandler()&lt;/code&gt; to manage failed requests. When requests are for documents, a fallback offline HTML page will be returned.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To test this functionality:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go back to the other tab that is running your app.&lt;/li&gt;
&lt;li&gt;Set the &lt;strong&gt;Throttling&lt;/strong&gt; drop-down list back to &lt;strong&gt;Online&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Press Chrome&#39;s &lt;strong&gt;Back&lt;/strong&gt; button to navigate back to the search page.&lt;/li&gt;
&lt;li&gt;Make sure that the &lt;strong&gt;Disable cache&lt;/strong&gt; checkbox in DevTools is disabled.&lt;/li&gt;
&lt;li&gt;Long-press Chrome&#39;s &lt;strong&gt;Reload&lt;/strong&gt; button and select
&lt;a href=&quot;https://stackoverflow.com/q/14969315/1669860&quot; rel=&quot;noopener&quot;&gt;&lt;strong&gt;Empty cache and hard reload&lt;/strong&gt;&lt;/a&gt;
to ensure that your service worker is updated.&lt;/li&gt;
&lt;li&gt;Set the &lt;strong&gt;Throttling&lt;/strong&gt; drop-down list back to &lt;strong&gt;Offline&lt;/strong&gt; again.&lt;/li&gt;
&lt;li&gt;Enter a search query, and click the &lt;strong&gt;Search&lt;/strong&gt; button again.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The fallback HTML page is shown:&lt;/p&gt;
&lt;img alt=&quot;A screenshot of the custom offline UX in the browser.&quot; decoding=&quot;async&quot; height=&quot;456&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/2o0feM6Ib4GnLdKQqV9G.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/2o0feM6Ib4GnLdKQqV9G.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/2o0feM6Ib4GnLdKQqV9G.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/2o0feM6Ib4GnLdKQqV9G.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/2o0feM6Ib4GnLdKQqV9G.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/2o0feM6Ib4GnLdKQqV9G.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/2o0feM6Ib4GnLdKQqV9G.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/2o0feM6Ib4GnLdKQqV9G.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/2o0feM6Ib4GnLdKQqV9G.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/2o0feM6Ib4GnLdKQqV9G.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/2o0feM6Ib4GnLdKQqV9G.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/2o0feM6Ib4GnLdKQqV9G.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/2o0feM6Ib4GnLdKQqV9G.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/2o0feM6Ib4GnLdKQqV9G.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/2o0feM6Ib4GnLdKQqV9G.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/2o0feM6Ib4GnLdKQqV9G.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/2o0feM6Ib4GnLdKQqV9G.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/2o0feM6Ib4GnLdKQqV9G.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h2 id=&quot;request-notification-permission&quot;&gt;Request notification permission &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-building-resilient-search-experiences/#request-notification-permission&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For simplicity, the offline page at &lt;code&gt;views/index_offline.html&lt;/code&gt; already contains the code to request notification permissions in a script block at the bottom:&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;requestNotificationPermission&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  Notification&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;requestPermission&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&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 parameter&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;token punctuation&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;showOfflineText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The code does the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When the user clicks &lt;strong&gt;subscribe to notifications&lt;/strong&gt; the &lt;code&gt;requestNotificationPermission()&lt;/code&gt; function is called, which calls &lt;code&gt;Notification.requestPermission()&lt;/code&gt;, to show the default browser permission prompt. The promise resolves with the permission picked by the user, which can be either &lt;code&gt;granted&lt;/code&gt;, &lt;code&gt;denied&lt;/code&gt;, or &lt;code&gt;default&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Passes the resolved permission to  &lt;code&gt;showOfflineText()&lt;/code&gt; to show the appropriate text to the user.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;persist-offline-queries-and-retry-when-back-online&quot;&gt;Persist offline queries and retry when back online &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-building-resilient-search-experiences/#persist-offline-queries-and-retry-when-back-online&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Next, implement &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-background-sync/&quot; rel=&quot;noopener&quot;&gt;Workbox Background Sync&lt;/a&gt; to persist offline queries, so they can be retried when the browser detects that connectivity has returned.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open &lt;code&gt;public/sw.js&lt;/code&gt; for edit.&lt;/li&gt;
&lt;li&gt;Add the following code at the end of the file:&lt;/li&gt;
&lt;/ol&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; bgSyncPlugin &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;workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;backgroundSync&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Plugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;offlineQueryQueue&#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 literal-property property&quot;&gt;maxRetentionTime&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function-variable function&quot;&gt;onSync&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;queue&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entry &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; queue&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shiftRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&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; cache &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;offline-search-responses&#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; offlineUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;amp;notification=true&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;        cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;offlineUrl&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;offlineUrl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unshiftRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; error&lt;span 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;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The code does the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;workbox.backgroundSync.Plugin&lt;/code&gt; contains the logic to add failed requests to a queue so they can be retried later. These requests will be persisted in &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/IndexedDB_API&quot; rel=&quot;noopener&quot;&gt;IndexedDB&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;maxRetentionTime&lt;/code&gt; indicates the amount of time a request may be retried. In this case we have chosen 60 minutes (after which it will be discarded).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;onSync&lt;/code&gt; is the most important part of this code. This callback will be called when connection is back so that queued requests are retrieved and then fetched from the network.&lt;/li&gt;
&lt;li&gt;The network response is added to the &lt;code&gt;offline-search-responses&lt;/code&gt; cache, appending the &lt;code&gt;&amp;amp;notification=true&lt;/code&gt; query param, so that this cache entry can be picked up when a user clicks on the notification.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To integrate background sync with your service, define a &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-strategies/#network-only&quot; rel=&quot;noopener&quot;&gt;NetworkOnly&lt;/a&gt; strategy for requests to the search URL (&lt;code&gt;/search_action&lt;/code&gt;) and pass the previously defined &lt;code&gt;bgSyncPlugin&lt;/code&gt;. Add the following code to the bottom of &lt;code&gt;public/sw.js&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;matchSearchUrl&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 parameter&quot;&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&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; notificationParam &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;searchParams&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;notification&#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;return&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pathname &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/search_action&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;notificationParam &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;true&#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;br /&gt;workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;routing&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;registerRoute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;  matchSearchUrl&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;workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;strategies&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NetworkOnly&lt;/span&gt;&lt;span class=&quot;token punctuation&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;bgSyncPlugin&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This tells Workbox to always go to the network, and, when requests fail, use the background sync logic.&lt;/p&gt;
&lt;p&gt;Next, add the following code to the bottom of &lt;code&gt;public/sw.js&lt;/code&gt; to define a caching strategy for requests coming from notifications. Use a &lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-strategies/#cache-first-cache-falling-back-to-network&quot; rel=&quot;noopener&quot;&gt;CacheFirst&lt;/a&gt; strategy, so they can be served from the cache.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;matchNotificationUrl&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 parameter&quot;&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&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; notificationParam &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;searchParams&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;notification&#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;return&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;pathname &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/search_action&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;notificationParam &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;true&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;routing&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;registerRoute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;matchNotificationUrl&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;workbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;strategies&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CacheFirst&lt;/span&gt;&lt;span class=&quot;token punctuation&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;cacheName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;offline-search-responses&#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 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;Finally, add the code to show notifications:&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;notificationUrl&lt;/span&gt;&lt;span class=&quot;token punctuation&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;Notification&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;permission&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;     self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Your search is ready!&#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 literal-property property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Click to see you search result&#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;icon&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/img/workbox.jpg&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;           &lt;span class=&quot;token literal-property property&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; notificationUrl&lt;br /&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;notificationclick&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;notification&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitUntil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;     clients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;openWindow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;notification&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;test-the-feature&quot;&gt;Test the feature &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-building-resilient-search-experiences/#test-the-feature&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Go back to the other tab that is running your app.&lt;/li&gt;
&lt;li&gt;Set the &lt;strong&gt;Throttling&lt;/strong&gt; drop-down list back to &lt;strong&gt;Online&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Press Chrome&#39;s &lt;strong&gt;Back&lt;/strong&gt; button to navigate back to the search page.&lt;/li&gt;
&lt;li&gt;Long-press Chrome&#39;s &lt;strong&gt;Reload&lt;/strong&gt; button and select
&lt;a href=&quot;https://stackoverflow.com/q/14969315/1669860&quot; rel=&quot;noopener&quot;&gt;&lt;strong&gt;Empty cache and hard reload&lt;/strong&gt;&lt;/a&gt;
to ensure that your service worker is updated.&lt;/li&gt;
&lt;li&gt;Set the &lt;strong&gt;Throttling&lt;/strong&gt; drop-down list back to &lt;strong&gt;Offline&lt;/strong&gt; again.&lt;/li&gt;
&lt;li&gt;Enter a search query, and click the &lt;strong&gt;Search&lt;/strong&gt; button again.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;subscribe to notifications&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;When Chrome asks you if you want to grant the app permission to send notifications,
click &lt;strong&gt;Allow&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Enter another search query and click the &lt;strong&gt;Search&lt;/strong&gt; button again.&lt;/li&gt;
&lt;li&gt;Set the &lt;strong&gt;Throttling&lt;/strong&gt; drop-down list back to &lt;strong&gt;Online&lt;/strong&gt; again.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once the connection is back a notification will be shown:&lt;/p&gt;
&lt;img alt=&quot;A screenshot of the full offline flow.&quot; decoding=&quot;async&quot; height=&quot;315&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/kvnl2PlazBdppGF4eMi0.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/kvnl2PlazBdppGF4eMi0.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/kvnl2PlazBdppGF4eMi0.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/kvnl2PlazBdppGF4eMi0.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/kvnl2PlazBdppGF4eMi0.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/kvnl2PlazBdppGF4eMi0.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/kvnl2PlazBdppGF4eMi0.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/kvnl2PlazBdppGF4eMi0.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/kvnl2PlazBdppGF4eMi0.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/kvnl2PlazBdppGF4eMi0.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/kvnl2PlazBdppGF4eMi0.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/kvnl2PlazBdppGF4eMi0.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/kvnl2PlazBdppGF4eMi0.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/kvnl2PlazBdppGF4eMi0.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/kvnl2PlazBdppGF4eMi0.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/kvnl2PlazBdppGF4eMi0.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/kvnl2PlazBdppGF4eMi0.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/kvnl2PlazBdppGF4eMi0.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-building-resilient-search-experiences/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Workbox provides many built-in features to make your PWAs more resilient and engaging.
In this codelab you have explored how to implement the Background Sync API by way of the Workbox abstraction, to ensure that offline user queries are not lost, and can be retried once connection is back.
The demo is a simple search app, but you can use a similar implementation for more complex scenarios and use cases, including chat apps, posting messages on a social network, etc.&lt;/p&gt;
</content>
    <author>
      <name>Demian Renzulli</name>
    </author><author>
      <name>Jeff Posnick</name>
    </author>
  </entry>
  
  <entry>
    <title>Prefetching in create-react-app with Quicklink</title>
    <link href="https://web.dev/codelab-quicklink/"/>
    <updated>2020-06-08T00:00:00Z</updated>
    <id>https://web.dev/codelab-quicklink/</id>
    <content type="html" mode="escaped">&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; This codelab uses &lt;a href=&quot;https://www.google.com/chrome/&quot;&gt;Chrome DevTools&lt;/a&gt;. Download Chrome if you don&#39;t already have it. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;This codelab shows you how to implement the &lt;a href=&quot;https://web.dev/quicklink&quot;&gt;Quicklink&lt;/a&gt; library in a React SPA demo to demonstrate how prefetching speeds up subsequent navigations.&lt;/p&gt;
&lt;h2 id=&quot;measure&quot;&gt;Measure &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-quicklink/#measure&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before adding optimizations, it&#39;s always a good idea to first analyze the current state of the application.&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;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;The website is a simple demo built with &lt;a href=&quot;https://reactjs.org/docs/create-a-new-react-app.html&quot; rel=&quot;noopener&quot;&gt;create-react-app&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Complete the following instructions in the new tab that just opened:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Network&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;Disable cache&lt;/strong&gt; checkbox.&lt;/li&gt;
&lt;li&gt;In the &lt;a href=&quot;https://developer.chrome.com/docs/devtools/network/reference/#throttling&quot; rel=&quot;noopener&quot;&gt;Throttling drop-down list&lt;/a&gt;, select &lt;strong&gt;Fast 3G&lt;/strong&gt; to simulate a slow connection type.&lt;/li&gt;
&lt;li&gt;Reload the app.&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;chunk&lt;/code&gt; into the &lt;a href=&quot;https://developer.chrome.com/docs/devtools/network/reference/#filter-by-property&quot; rel=&quot;noopener&quot;&gt;Filter textbox&lt;/a&gt; to hide any resources that do not include &lt;code&gt;chunk&lt;/code&gt; in their name.&lt;/li&gt;
&lt;/ol&gt;
&lt;img alt=&quot;Network panel showing the home page chunks.&quot; decoding=&quot;async&quot; height=&quot;194&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DCl6nkEfsVukurm8gBsy.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DCl6nkEfsVukurm8gBsy.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DCl6nkEfsVukurm8gBsy.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DCl6nkEfsVukurm8gBsy.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DCl6nkEfsVukurm8gBsy.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DCl6nkEfsVukurm8gBsy.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DCl6nkEfsVukurm8gBsy.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DCl6nkEfsVukurm8gBsy.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DCl6nkEfsVukurm8gBsy.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DCl6nkEfsVukurm8gBsy.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DCl6nkEfsVukurm8gBsy.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DCl6nkEfsVukurm8gBsy.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DCl6nkEfsVukurm8gBsy.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DCl6nkEfsVukurm8gBsy.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DCl6nkEfsVukurm8gBsy.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DCl6nkEfsVukurm8gBsy.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DCl6nkEfsVukurm8gBsy.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/DCl6nkEfsVukurm8gBsy.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;The site uses &lt;a href=&quot;https://web.dev/reduce-javascript-payloads-with-code-splitting/&quot;&gt;route-based code splitting&lt;/a&gt;, thanks to which only the necessary code is requested at the beginning.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/devtools/network/reference/#clear&quot; rel=&quot;noopener&quot;&gt;Clear the network requests in DevTools&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Within the app, click the &lt;strong&gt;Blog&lt;/strong&gt; link to navigate to that page.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The JS and CSS chunks for the new route are loaded to render the page.&lt;/p&gt;
&lt;img alt=&quot;Network panel showing the blog page chunks.&quot; decoding=&quot;async&quot; height=&quot;119&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/23QuiFZwo7rouJaqmz9m.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/23QuiFZwo7rouJaqmz9m.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/23QuiFZwo7rouJaqmz9m.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/23QuiFZwo7rouJaqmz9m.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/23QuiFZwo7rouJaqmz9m.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/23QuiFZwo7rouJaqmz9m.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/23QuiFZwo7rouJaqmz9m.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/23QuiFZwo7rouJaqmz9m.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/23QuiFZwo7rouJaqmz9m.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/23QuiFZwo7rouJaqmz9m.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/23QuiFZwo7rouJaqmz9m.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/23QuiFZwo7rouJaqmz9m.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/23QuiFZwo7rouJaqmz9m.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/23QuiFZwo7rouJaqmz9m.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/23QuiFZwo7rouJaqmz9m.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/23QuiFZwo7rouJaqmz9m.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/23QuiFZwo7rouJaqmz9m.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/23QuiFZwo7rouJaqmz9m.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Next, you&#39;ll implement Quicklink in this site, so that these chunks can be prefetched in the home page, making the navigation faster.&lt;/p&gt;
&lt;p&gt;This allows you to combine the best of both techniques:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Route-based code splitting tells the browser to only load the necessary chunks at a higher priority at page load time.&lt;/li&gt;
&lt;li&gt;Prefetching tells the browser to load the chunks for in-viewport links at the lowest priority, during the browser&#39;s idle time.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;configure-webpack-route-manifest&quot;&gt;Configure &lt;code&gt;webpack-route-manifest&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-quicklink/#configure-webpack-route-manifest&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The first step is to install and configure &lt;a href=&quot;https://github.com/lukeed/webpack-route-manifest&quot; rel=&quot;noopener&quot;&gt;webpack-route-manifest&lt;/a&gt;, a webpack plugin that lets you generate a manifest file associating routes with their corresponding chunks.&lt;/p&gt;
&lt;p&gt;Usually, you would need to install the library, but we&#39;ve already done it for you. Here&#39;s the command that you would need to run:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; webpack-route-manifest --save-dev&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;config-overrides.js&lt;/code&gt; is a file placed in your project root directory where you can override existing behaviour of the webpack configuration, without having to &lt;a href=&quot;https://github.com/facebook/create-react-app/blob/master/packages/cra-template/template/README.md#npm-run-eject&quot; rel=&quot;noopener&quot;&gt;eject the project&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To view the source, press &lt;strong&gt;View Source&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Open &lt;code&gt;config-overrides.js&lt;/code&gt; for edit and add the &lt;code&gt;webpack-route-manifest&lt;/code&gt; dependency at the beginning of the file:&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;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;/span&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; RouteManifest &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;webpack-route-manifest&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/ins&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Next, configure the &lt;code&gt;webpack-route-manifest&lt;/code&gt; plugin by adding the following
code to the bottom of &lt;code&gt;config-overrides.js&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;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;override&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;resolve &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 operator&quot;&gt;...&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;alias&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;@assets&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;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;src/assets&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&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 string-property property&quot;&gt;&#39;@pages&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;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;src/pages&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&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 string-property property&quot;&gt;&#39;@components&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;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;src/components&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&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 punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;plugins&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;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RouteManifest&lt;/span&gt;&lt;span class=&quot;token punctuation&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;minify&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;filename&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;rmanifest.json&#39;&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;routes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; out &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; str&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;@pages&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toLowerCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&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;out &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/article&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/blog/:title&#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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;out &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/home&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/&#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;return&lt;/span&gt; out&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The new code does the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;config.resolve&lt;/code&gt; declares variables with the internal routes to pages, assets and components.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;config.plugins.push()&lt;/code&gt; creates a &lt;code&gt;RouteManifest&lt;/code&gt; object and passes it the configuration so that the &lt;code&gt;rmanifest.json&lt;/code&gt; file can be generated based on the site&#39;s routes and chunks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;manifest.json&lt;/code&gt; file will be generated and made available at &lt;code&gt;https://site_url/rmanifest.json&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;configure-quicklink&quot;&gt;Configure quicklink &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-quicklink/#configure-quicklink&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At this point you would need to install the Quicklink library in your project. For simplicity, we already added it to the project. Here&#39;s the command that you would need to run:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; --save quicklink&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Open &lt;code&gt;src/components/App/index.js&lt;/code&gt; for edit.&lt;/p&gt;
&lt;p&gt;First, import the Quicklink higher order component (HOC):&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;import&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; lazy&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Suspense &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;react&#39;&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;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Route &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;react-router-dom&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Footer &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;@components/Footer&#39;&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;import&lt;/span&gt; Hero &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;@components/Hero&#39;&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;import&lt;/span&gt; style &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./index.module.css&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; withQuicklink &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;quicklink/dist/react/hoc.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; Home &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;lazy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* webpackChunkName: &quot;home&quot; */&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;@pages/Home&#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&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; About &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;lazy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* webpackChunkName: &quot;about&quot; */&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;@pages/About&#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&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; Article &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;lazy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* webpackChunkName: &quot;article&quot; */&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;@pages/Article&#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&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; Blog &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;lazy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* webpackChunkName: &quot;blog&quot; */&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;@pages/Blog&#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&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Next, create an &lt;code&gt;options&lt;/code&gt; object after the &lt;code&gt;Blog&lt;/code&gt; variable declaration, to use as an argument when calling &lt;code&gt;quicklink&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;ins class=&quot;highlight-line highlight-line-add&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;	&lt;span class=&quot;token literal-property property&quot;&gt;origins&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;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/ins&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Finally, wrap each route with the &lt;code&gt;withQuicklink()&lt;/code&gt; higher order component, passing it an &lt;code&gt;options&lt;/code&gt; parameter and the target component for that route:&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;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;app&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&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Hero &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&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 operator&quot;&gt;&amp;lt;&lt;/span&gt;main className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;wrapper&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&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Suspense fallback&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 operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Loading&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Route path&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt; exact component&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;withQuicklink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Home&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&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 operator&quot;&gt;&amp;lt;&lt;/span&gt;Route path&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/blog&quot;&lt;/span&gt; exact component&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;withQuicklink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Blog&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&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 operator&quot;&gt;&amp;lt;&lt;/span&gt;Route&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;          path&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/blog/:title&quot;&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;          component&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;withQuicklink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Article&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;        &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Route path&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/about&quot;&lt;/span&gt; exact component&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;withQuicklink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;About&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;Suspense&lt;span class=&quot;token operator&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 operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;main&lt;span class=&quot;token operator&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 operator&quot;&gt;&amp;lt;&lt;/span&gt;Footer &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&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 operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&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 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;The previous code instructs to prefetch chunks for the routes wrapped with &lt;code&gt;withQuicklink()&lt;/code&gt;, when the link comes into the view.&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 Glitch throws an error at this point about the lack of a dependency, try opening the Glitch Terminal (&lt;strong&gt;Tools&lt;/strong&gt; &amp;gt; &lt;strong&gt;Terminal&lt;/strong&gt;), running &lt;code&gt;refresh&lt;/code&gt; in the Terminal, then running &lt;code&gt;npm run build&lt;/code&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;measure-again&quot;&gt;Measure again &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-quicklink/#measure-again&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Repeat the first 6 steps from &lt;a href=&quot;https://web.dev/codelab-quicklink/#measure&quot;&gt;Measure&lt;/a&gt;. Don&#39;t navigate to the blog page yet.&lt;/p&gt;
&lt;p&gt;When the home page loads the chunks for that route are loaded. After that, Quicklink prefetches the route&#39;s chunks for the in-viewport links:&lt;/p&gt;
&lt;img alt=&quot;Network panel showing the home page prefetching chunks.&quot; decoding=&quot;async&quot; height=&quot;286&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lClOmOYDx8Cg3RZakAu2.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lClOmOYDx8Cg3RZakAu2.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lClOmOYDx8Cg3RZakAu2.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lClOmOYDx8Cg3RZakAu2.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lClOmOYDx8Cg3RZakAu2.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lClOmOYDx8Cg3RZakAu2.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lClOmOYDx8Cg3RZakAu2.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lClOmOYDx8Cg3RZakAu2.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lClOmOYDx8Cg3RZakAu2.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lClOmOYDx8Cg3RZakAu2.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lClOmOYDx8Cg3RZakAu2.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lClOmOYDx8Cg3RZakAu2.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lClOmOYDx8Cg3RZakAu2.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lClOmOYDx8Cg3RZakAu2.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lClOmOYDx8Cg3RZakAu2.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lClOmOYDx8Cg3RZakAu2.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lClOmOYDx8Cg3RZakAu2.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/lClOmOYDx8Cg3RZakAu2.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;These chunks are requested at the lowest priority and without blocking the page.&lt;/p&gt;
&lt;p&gt;Next:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Clear the Network log again.&lt;/li&gt;
&lt;li&gt;Disable the &lt;strong&gt;Disable cache&lt;/strong&gt; checkbox.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Blog&lt;/strong&gt; link to navigate to that page.&lt;/li&gt;
&lt;/ol&gt;
&lt;img alt=&quot;Network panel showing the blog page with chunks picked up from cache.&quot; decoding=&quot;async&quot; height=&quot;95&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bJJYT3gDIWU9PeeXSdOE.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bJJYT3gDIWU9PeeXSdOE.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bJJYT3gDIWU9PeeXSdOE.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bJJYT3gDIWU9PeeXSdOE.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bJJYT3gDIWU9PeeXSdOE.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bJJYT3gDIWU9PeeXSdOE.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bJJYT3gDIWU9PeeXSdOE.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bJJYT3gDIWU9PeeXSdOE.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bJJYT3gDIWU9PeeXSdOE.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bJJYT3gDIWU9PeeXSdOE.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bJJYT3gDIWU9PeeXSdOE.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bJJYT3gDIWU9PeeXSdOE.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bJJYT3gDIWU9PeeXSdOE.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bJJYT3gDIWU9PeeXSdOE.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bJJYT3gDIWU9PeeXSdOE.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bJJYT3gDIWU9PeeXSdOE.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bJJYT3gDIWU9PeeXSdOE.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bJJYT3gDIWU9PeeXSdOE.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;The &lt;strong&gt;Size&lt;/strong&gt; column indicates that these chunks were retrieved from the &amp;quot;prefetch cache&amp;quot;, instead of the network. Loading these chunks without a Quicklink took approximately &lt;strong&gt;580ms&lt;/strong&gt;. Using the library it now takes &lt;strong&gt;2ms&lt;/strong&gt;, which represents a &lt;strong&gt;99% reduction&lt;/strong&gt;!&lt;/p&gt;
</content>
    <author>
      <name>Addy Osmani</name>
    </author><author>
      <name>Demian Renzulli</name>
    </author><author>
      <name>Anton Karlovskiy</name>
    </author>
  </entry>
  
  <entry>
    <title>Speed up navigations in React with Quicklink</title>
    <link href="https://web.dev/quicklink/"/>
    <updated>2020-06-08T00:00:00Z</updated>
    <id>https://web.dev/quicklink/</id>
    <content type="html" mode="escaped">&lt;p&gt;&lt;a href=&quot;https://web.dev/link-prefetch/&quot;&gt;Prefetching&lt;/a&gt; is a technique to speed up navigations by downloading the resources for the next page, ahead of time. &lt;a href=&quot;https://github.com/GoogleChromeLabs/quicklink&quot; rel=&quot;noopener&quot;&gt;Quicklink&lt;/a&gt; is a library that allows you to implement this technique at scale, by automatically prefetching links as they come into the view.&lt;/p&gt;
&lt;p&gt;In multi-page apps the library prefetches documents (e.g. &lt;code&gt;/article.html&lt;/code&gt;), for in-viewport links, so that when the user clicks on these links they can be picked up from the &lt;a href=&quot;https://web.dev/http-cache/&quot;&gt;HTTP cache&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Single-page_application&quot; rel=&quot;noopener&quot;&gt;Single-page apps&lt;/a&gt; commonly use a technique called &lt;a href=&quot;https://web.dev/reduce-javascript-payloads-with-code-splitting/&quot;&gt;route-based code splitting&lt;/a&gt;. This allows the site to load the code for a given route only when the user navigates to it. These files (JS, CSS) are commonly referred to as &amp;quot;chunks&amp;quot;.&lt;/p&gt;
&lt;p&gt;With that said, in these sites, instead of prefetching documents the biggest performance gains come from prefetching these chunks before the page needs them.&lt;/p&gt;
&lt;p&gt;Achieving this presents some challenges:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It&#39;s not trivial to determine which chunks (e.g. &lt;code&gt;article.chunk.js&lt;/code&gt;) are associated with a given route (e.g. &lt;code&gt;/article&lt;/code&gt;) before landing on it.&lt;/li&gt;
&lt;li&gt;The final URL names of these chunks can&#39;t be predicted, as modern module bundlers typically use long-term hashing for versioning (e.g. &lt;code&gt;article.chunk.46e51.js&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This guide explains how Quicklink solves these challenges and allows you to achieve prefetching at scale in React single page apps.&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; At the moment this solution is only compatible with &lt;a href=&quot;https://www.npmjs.com/package/react-router&quot;&gt;react-router&lt;/a&gt;. &lt;/div&gt;&lt;/aside&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; Check out the &lt;a href=&quot;https://web.dev/codelab-quicklink/&quot;&gt;Prefetching in create-react-app with Quicklink&lt;/a&gt; codelab for a guided, hands-on demonstration of Quicklink. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;determine-chunks-associated-with-each-route&quot;&gt;Determine chunks associated with each route &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/quicklink/#determine-chunks-associated-with-each-route&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One of the core components of &lt;code&gt;quicklink&lt;/code&gt; is &lt;a href=&quot;https://github.com/lukeed/webpack-route-manifest&quot; rel=&quot;noopener&quot;&gt;webpack-route-manifest&lt;/a&gt;, a &lt;a href=&quot;https://webpack.js.org/&quot; rel=&quot;noopener&quot;&gt;webpack&lt;/a&gt; plugin that lets you generate a JSON dictionary of routes and chunks.
This allows the library to know which files are going to be needed by each route of the application and prefetch them as the routes come into the view.&lt;/p&gt;
&lt;p&gt;After &lt;a href=&quot;https://github.com/lukeed/webpack-route-manifest#install&quot; rel=&quot;noopener&quot;&gt;integrating the plugin&lt;/a&gt; with the project, it will produce a JSON manifest file associating each route with its corresponding chunks:&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;br /&gt;  &lt;span class=&quot;token string-property property&quot;&gt;&#39;/about&#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;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;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;style&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/static/css/about.f6fd7d80.chunk.css&#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 punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;script&#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;href&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/static/js/about.1cdfef3b.chunk.js&#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 punctuation&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;/blog&#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;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;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;style&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/static/css/blog.85e80e75.chunk.css&#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 punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;script&#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;href&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/static/js/blog.35421503.chunk.js&#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 punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This manifest file can be requested in two ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;By URL, e.g. &lt;code&gt;https://site_url/rmanifest.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Through the window object, at &lt;code&gt;window.__rmanifest&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;prefetch-chunks-for-in-viewport-routes&quot;&gt;Prefetch chunks for in-viewport routes &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/quicklink/#prefetch-chunks-for-in-viewport-routes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once the manifest file is available, the next step is to install Quicklink by running &lt;code&gt;npm install quicklink&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then, the &lt;a href=&quot;https://reactjs.org/docs/higher-order-components.html&quot; rel=&quot;noopener&quot;&gt;higher order component (HOC)&lt;/a&gt; &lt;code&gt;withQuicklink()&lt;/code&gt; can be used to indicate that a given route should be prefetched when the link comes into the view.&lt;/p&gt;
&lt;p&gt;The following code belongs to an &lt;code&gt;App&lt;/code&gt; component of a React app that renders a top menu with four links:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token 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;className&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;}&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 plain-text&quot;&gt;&lt;br /&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;&lt;span class=&quot;token class-name&quot;&gt;Hero&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 plain-text&quot;&gt;&lt;br /&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;main&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;wrapper&lt;span class=&quot;token punctuation&quot;&gt;}&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 plain-text&quot;&gt;&lt;br /&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;&lt;span class=&quot;token class-name&quot;&gt;Suspense&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fallback&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&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;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Loading…&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;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&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;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&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;&lt;span class=&quot;token class-name&quot;&gt;Route&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;exact&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Home&lt;span class=&quot;token punctuation&quot;&gt;}&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 plain-text&quot;&gt;&lt;br /&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;&lt;span class=&quot;token class-name&quot;&gt;Route&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/blog&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;exact&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Blog&lt;span class=&quot;token punctuation&quot;&gt;}&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 plain-text&quot;&gt;&lt;br /&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;&lt;span class=&quot;token class-name&quot;&gt;Route&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/blog/:title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Article&lt;span class=&quot;token punctuation&quot;&gt;}&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 plain-text&quot;&gt;&lt;br /&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;&lt;span class=&quot;token class-name&quot;&gt;Route&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/about&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;exact&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;About&lt;span class=&quot;token punctuation&quot;&gt;}&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 plain-text&quot;&gt;&lt;br /&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;&lt;span class=&quot;token class-name&quot;&gt;Suspense&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 plain-text&quot;&gt;&lt;br /&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;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&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;&lt;span class=&quot;token class-name&quot;&gt;Footer&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 plain-text&quot;&gt;&lt;br /&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;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 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;To tell Quicklink that these routes should be prefetched as they come into the view:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Import the &lt;code&gt;quicklink&lt;/code&gt; HOC at the beginning of the component.&lt;/li&gt;
&lt;li&gt;Wrap each route with the &lt;code&gt;withQuicklink()&lt;/code&gt; HOC, passing the page component and options parameter to it.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;const options = {&lt;br /&gt;  origins: [],&lt;br /&gt;};&lt;br /&gt;const App = () =&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;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;{style.app}&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;Hero&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;main&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;{style.wrapper}&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;Suspense&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fallback&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;{&amp;lt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Loading…&lt;span class=&quot;token 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;}&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;Route&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;exact&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;{withQuicklink(Home,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;options)}&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;Route&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/blog&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;exact&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;{withQuicklink(Blog,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;options)}&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;Route&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token attr-name&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/blog/:title&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;component&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;{withQuicklink(Article,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;options)}&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Route&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/about&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;exact&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;{withQuicklink(About,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;options)}&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;Suspense&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;main&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;Footer&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The &lt;code&gt;withQuicklink()&lt;/code&gt; HOC uses the path of the route as a key to obtain its associated chunks from &lt;code&gt;rmanifest.json&lt;/code&gt;.
Under the hood, as links come into the view, the library injects a &lt;code&gt;&amp;lt;link rel=&amp;quot;prefetch&amp;quot;&amp;gt;&lt;/code&gt; tag in the page for each chunk so they can be prefetched.
Prefetched resources will be requested at the lowest priority by the browser and kept in the &lt;a href=&quot;https://web.dev/http-cache/&quot;&gt;HTTP Cache&lt;/a&gt; for 5 minutes, after which point, the &lt;code&gt;cache-control&lt;/code&gt; rules of the resource apply.
As a result of this, when a user clicks on a link and moves to a given route, the chunks will be retrieved from the cache, greatly improving the time it takes to render that route.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/quicklink/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Prefetching can greatly improve load times for future navigations. In React single-page apps, this can be achieved by loading the chunks associated with each route, before the user lands in them.
Quicklink&#39;s solution for React SPA uses &lt;code&gt;webpack-route-manifest&lt;/code&gt; to create a map of routes and chunks, in order to determine which files to prefetch, when a link comes into the view.
Implementing this technique throughout your site can greatly improve navigations to the point of making them appear instant.&lt;/p&gt;
</content>
    <author>
      <name>Addy Osmani</name>
    </author><author>
      <name>Demian Renzulli</name>
    </author><author>
      <name>Anton Karlovskiy</name>
    </author>
  </entry>
  
  <entry>
    <title>How to define your install strategy</title>
    <link href="https://web.dev/define-install-strategy/"/>
    <updated>2020-05-12T00:00:00Z</updated>
    <id>https://web.dev/define-install-strategy/</id>
    <content type="html" mode="escaped">&lt;p&gt;In the past, app installation was only possible in the context of platform-specific applications. Today, modern web apps offer installable experiences that provide the same level of integration and reliability as platform-specific apps.&lt;/p&gt;
&lt;p&gt;You can achieve this in different ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Installing the PWA &lt;a href=&quot;https://web.dev/customize-install/&quot;&gt;from the browser&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Installing the PWA &lt;a href=&quot;https://developer.chrome.com/docs/android/trusted-web-activity/&quot; rel=&quot;noopener&quot;&gt;from the app store&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Having different distribution channels is a powerful way of reaching a broad number of users, but choosing the right strategy to promote your the installation of your PWA can be challenging.&lt;/p&gt;
&lt;p&gt;This guide explores best practices for combining different installation options to increase installation rates and avoid platform competition and cannibalization. The installation offerings covered include PWAs installed from both the browser and the App Store, as well as platform-specific apps.&lt;/p&gt;
&lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;6R9pupbDXYw&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
&lt;h2 id=&quot;why-make-your-web-app-installable&quot;&gt;Why make your web app installable? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/define-install-strategy/#why-make-your-web-app-installable&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Installed Progressive Web Apps run in a standalone window instead of a browser tab. They&#39;re launchable from the user&#39;s home screen, dock, taskbar, or shelf. It&#39;s possible to search for them on a device and jump between them with the app switcher, making them feel like part of the device they&#39;re installed on.&lt;/p&gt;
&lt;p&gt;But having both an installable web app and a platform-specific app can be confusing for users. For some users platform-specific apps may be the best choice, but for others they can present some drawbacks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Storage constraints:&lt;/strong&gt; installing a new app might mean deleting others, or cleaning up space, by removing valuable content. This is especially disadvantageous for users on low-end devices.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Available bandwidth:&lt;/strong&gt; downloading an app can be a costly and slow process, even more so for users on slow connections and expensive data plans.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Friction:&lt;/strong&gt; leaving a website and moving to a store to download an app creates additional friction and delays a user action that could be performed directly in the web.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Update cycle:&lt;/strong&gt; making changes in platform-specific apps might require going through an app review process, which can slow down changes and experiments (A/B tests, for example).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In some cases, the percentage of users who won&#39;t download your platform-specific app might be large, for example: those that think that they won&#39;t use the app very often, or can&#39;t justify spending several megabytes of storage or data. You can determine the size of this segment in several ways, for example by using an analytics setup to track the percentage of &amp;quot;mobile web only&amp;quot; users.&lt;/p&gt;
&lt;p&gt;If the size of this segment is considerable, that&#39;s a good indication that you need to provide alternative ways of installing your experiences.&lt;/p&gt;
&lt;h2 id=&quot;promoting-installation-of-your-pwa-through-the-browser&quot;&gt;Promoting installation of your PWA through the browser &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/define-install-strategy/#promoting-installation-of-your-pwa-through-the-browser&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you have a high quality PWA, it may be better to promote its installation over your platform-specific app. For example, if the platform-specific app is missing functionality offered by your PWA, or if it hasn&#39;t been updated in a while. It can also be helpful to promote installation of your PWA if the platform-specific app wasn&#39;t optimized for bigger screens, such as on ChromeOS.&lt;/p&gt;
&lt;p&gt;For some apps, driving platform-specific app installations is a key part of the business model, in that case, it makes business sense to promote installation of your platform-specific app. But, some users might be more comfortable staying on the web. If that segment can be identified, the PWA prompt can be shown only to them (what we call &amp;quot;PWA as fallback&amp;quot;).&lt;/p&gt;
&lt;h3 id=&quot;pwa-as-primary-installable-experience&quot;&gt;PWA as primary installable experience &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/define-install-strategy/#pwa-as-primary-installable-experience&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Once a PWA meets the &lt;a href=&quot;https://web.dev/install-criteria/&quot;&gt;installability criteria&lt;/a&gt;, most browsers show an indication that the PWA is installable. For example, desktop Chrome shows an installable icon in the address bar, and on mobile, it shows a mini-infobar:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Standard Chrome install prompt, called a mini-infobar&quot; decoding=&quot;async&quot; height=&quot;208&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 400px) 400px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=800 800w&quot; width=&quot;400&quot; /&gt;
  &lt;figcaption&gt;
    The mini-infobar.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;While that may be enough for some experiences, if your goal is to drive installations of your PWA, we highly recommend you listen for the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/BeforeInstallPromptEvent&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;BeforeInstallPromptEvent&lt;/code&gt;&lt;/a&gt;, and follow the &lt;a href=&quot;https://web.dev/promote-install/&quot;&gt;patterns for promoting the installation&lt;/a&gt; of your PWA.&lt;/p&gt;
&lt;h2 id=&quot;prevent-your-pwa-from-cannibalizing-your-platform-specific-app-install-rate&quot;&gt;Prevent your PWA from cannibalizing your platform-specific app install rate &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/define-install-strategy/#prevent-your-pwa-from-cannibalizing-your-platform-specific-app-install-rate&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In some cases, you may choose to promote the installation of your platform-specific app over your PWA, but in this case, we still recommend you provide a mechanism to allow users to install your PWA. This fallback option makes it possible for users who can&#39;t, or don&#39;t want to install your platform-specific app to get a similar, installed experience.&lt;/p&gt;
&lt;p&gt;The first step to implement this strategy is to define a heuristic for when you&#39;ll show the user an install promotion for your PWA.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For example:&lt;/strong&gt; A PWA user is a user that has seen the platform-specific app install prompt and not installed the platform-specific app. They have returned to the site at least five times, or they have clicked the app banner, but have continued using the website instead.&lt;/p&gt;
&lt;p&gt;Then, the heuristic can be implemented in the following way:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Show the platform-specific app install banner.&lt;/li&gt;
&lt;li&gt;If a user dismisses the banner, set a cookie with that information (e.g. &lt;code&gt;document.cookie = &amp;quot;app-install-banner=dismissed&amp;quot;&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Use another cookie to track the number of user visits to the site (e.g. &lt;code&gt;document.cookie = &amp;quot;user-visits=1&amp;quot;&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Write a function, such as &lt;code&gt;isPWAUser()&lt;/code&gt;, that uses the information previously stored in the cookies along with the &lt;a href=&quot;https://web.dev/get-installed-related-apps/&quot;&gt;&lt;code&gt;getInstalledRelatedApps()&lt;/code&gt;&lt;/a&gt; API to determine if a user is considered a &amp;quot;PWA user&amp;quot;.&lt;/li&gt;
&lt;li&gt;When the user performs a meaningful action, call &lt;code&gt;isPWAUser()&lt;/code&gt;. If the function returns true and the PWA install prompt was saved previously, you can show the PWA install button.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;promoting-installation-of-your-pwa-through-an-app-store&quot;&gt;Promoting installation of your PWA through an app store &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/define-install-strategy/#promoting-installation-of-your-pwa-through-an-app-store&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Apps for App stores can be built with different technologies, including PWA techniques. In &lt;a href=&quot;https://youtu.be/V7YX4cZ_Cto&quot; rel=&quot;noopener&quot;&gt;Blending PWA into native environments&lt;/a&gt; you can find a summary of the technologies that can be used to that end.&lt;/p&gt;
&lt;p&gt;In this section we&#39;ll classify apps in the store in two groups:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Platform-specific apps:&lt;/strong&gt; these apps are mostly built with platform-specific code. Their sizes depend on the platform, but they&#39;re usually above 10MB on Android, and 30MB on iOS. You might want to promote your platform-specific app if you don&#39;t have a PWA, or if the platform-specific app presents a more complete feature set.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lightweight apps:&lt;/strong&gt; these apps can be built with platform-specific code as well, but they are commonly built with web technology, packaged in a platform-specific wrapper. Full PWAs can be uploaded to the stores as well. (This is discussed &lt;a href=&quot;https://web.dev/define-install-strategy/#promoting-lightweight-apps&quot;&gt;later&lt;/a&gt; in this article.) Some companies opt to provide these as &amp;quot;lite&amp;quot; experiences, and others have used this approach for their flagship (core) apps as well.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;promoting-lightweight-apps&quot;&gt;Promoting Lightweight Apps &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/define-install-strategy/#promoting-lightweight-apps&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;According to a &lt;a href=&quot;https://medium.com/googleplaydev/shrinking-apks-growing-installs-5d3fcba23ce2&quot; rel=&quot;noopener&quot;&gt;Google Play study&lt;/a&gt;, for every 6 MB increase to an APK&#39;s size, the install conversion rate decreases by 1%. This means that the download completion rate of an app of 10 MB could be approximately &lt;strong&gt;30% higher than an app of 100 MB!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To address this, some companies are leveraging their PWA to provide a lightweight version of their app in the Play Store using &lt;a href=&quot;https://developer.chrome.com/docs/android/trusted-web-activity/&quot; rel=&quot;noopener&quot;&gt;Trusted Web Activities&lt;/a&gt; (TWA). TWAs wrap your PWA in a webview like component, and the resulting app size is usually only a few megabytes.&lt;/p&gt;
&lt;p&gt;Oyo, one of India&#39;s largest hospitality companies, built a &lt;a href=&quot;https://web.dev/oyo-lite-twa/&quot;&gt;Lite version of their app&lt;/a&gt;, and made it available in the Play Store using a TWA. At the time this article was written, the Oyo app was only 850 KB, just 7% the size of their Android app. And once installed, it&#39;s indistinguishable from their Android app:&lt;/p&gt;
&lt;figure&gt;
  &lt;video controls=&quot;&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot;&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/oyo-case-study/oyo-lite.webm&quot; type=&quot;video/webm; codecs=vp8&quot; /&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/oyo-case-study/oyo-lite.mp4&quot; type=&quot;video/mp4; codecs=h264&quot; /&gt;
  &lt;/video&gt;
 &lt;figcaption&gt;
    OYO Lite in action.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Oyo kept both the flagship and &amp;quot;lite&amp;quot; app versions in the store, providing a choice to their users.&lt;/p&gt;
&lt;h4 id=&quot;providing-a-lightweight-web-experience&quot;&gt;Providing a lightweight web experience &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/define-install-strategy/#providing-a-lightweight-web-experience&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Intuitively, users on low-end devices, might be more inclined to download lightweight versions of apps than users on high-end phones. Therefore, if it&#39;s possible to identify a user&#39;s device, you could prioritize the lightweight app install banner over the heavier platform-specific app version.&lt;/p&gt;
&lt;p&gt;On the web, it&#39;s possible to obtain device signals and approximately map them to device categories (e.g. &amp;quot;high&amp;quot;, &amp;quot;mid&amp;quot;, or &amp;quot;low&amp;quot;). You can obtain this information in different ways, using either JavaScript APIs or client hints.&lt;/p&gt;
&lt;h4 id=&quot;using-javascript&quot;&gt;Using JavaScript &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/define-install-strategy/#using-javascript&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Using JavaScript properties such as &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/NavigatorConcurrentHardware/hardwareConcurrency&quot; rel=&quot;noopener&quot;&gt;navigator.hardwareConcurrency&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Navigator/deviceMemory&quot; rel=&quot;noopener&quot;&gt;navigator.deviceMemory&lt;/a&gt;, and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Navigator/connection&quot; rel=&quot;noopener&quot;&gt;navigator.connection&lt;/a&gt; you can get information about the device CPU, memory and network status respectively. For example:&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; deviceCategory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Device-Memory&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;lite&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;full&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;`&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h4 id=&quot;using-client-hints&quot;&gt;Using client hints &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/define-install-strategy/#using-client-hints&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Device signals can be also inferred in HTTP request headers, through &lt;a href=&quot;https://developer.mozilla.org/docs/Glossary/Client_hints&quot; rel=&quot;noopener&quot;&gt;client hints&lt;/a&gt;. Here&#39;s how you can implement the previous code for device memory with client hints:&lt;/p&gt;
&lt;p&gt;First, tell the browser you are interested in receiving device memory hints in the header of the HTTP response for any first-party request:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;HTTP/1.1 200 OK&lt;br /&gt;Content-Type: text/html&lt;br /&gt;Accept-CH: Device-Memory&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Then, you&#39;ll start receiving &lt;code&gt;Device-Memory&lt;/code&gt; information in the request header of HTTP requests:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;GET /main.js HTTP/1.1&lt;br /&gt;Device-Memory: 0.5&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You can use this information in your backends to store a cookie with the category of the user&#39;s device:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/route&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Determine device category&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; deviceCategory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Device-Memory&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;lite&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;full&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Set cookie&lt;/span&gt;&lt;br /&gt;  res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCookie&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Device-Category&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; deviceCategory&lt;span 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 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;Finally, create your own logic to map this information to device categories, and show the corresponding app install prompt on each case:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isDeviceMidOrLowEnd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&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;// show &quot;Lite app&quot; install banner or PWA A2HS prompt&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// show &quot;Core app&quot; install banner&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Covering in depth techniques on how to map device signals to device categories is out of the scope of this guide, but you can check out Addy Osmani&#39;s &lt;a href=&quot;https://dev.to/addyosmani/adaptive-loading-improving-web-performance-on-low-end-devices-1m69&quot;&gt;adaptive loading guide&lt;/a&gt;, Philip Walton&#39;s &lt;a href=&quot;https://developer.chrome.com/blog/device-memory/&quot;&gt;The Device Memory API&lt;/a&gt;  and Jeremy Wagner&#39;s &lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/client-hints/&quot;&gt;Adapting to Users with Client Hints&lt;/a&gt; for more information on best practices around this. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/define-install-strategy/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The ability to have an icon in the user&#39;s home screen is one of the most engaging features of applications. Given that historically this was only possible for apps installed from app stores, companies might think that showing an app store install banner would be enough to convince users to install their experiences. Currently there are more options for letting users install an app, including offering lightweight app experiences in the stores, and letting users add PWAs to the home screen by prompting them to do it directly from the website.&lt;/p&gt;
</content>
    <author>
      <name>Demian Renzulli</name>
    </author><author>
      <name>Pete LePage</name>
    </author>
  </entry>
  
  <entry>
    <title>Optimize CSS background images with media queries</title>
    <link href="https://web.dev/optimize-css-background-images-with-media-queries/"/>
    <updated>2020-03-05T00:00:00Z</updated>
    <id>https://web.dev/optimize-css-background-images-with-media-queries/</id>
    <content type="html" mode="escaped">&lt;p&gt;Many sites request heavy resources, like images, that are not optimized for certain screens, and send large CSS files containing styles that some devices will never use. Using media queries is a popular technique for delivering tailored stylesheets and assets to different screens to reduce the amount of data transferred to users and improve page load performance. This guide shows you how to use media queries to send images that are only as large as they need to be, a technique commonly known as &lt;strong&gt;responsive images&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-css-background-images-with-media-queries/#prerequisites&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This guide assumes that you&#39;re familiar with &lt;a href=&quot;https://developer.chrome.com/docs/devtools/&quot; rel=&quot;noopener&quot;&gt;Chrome DevTools&lt;/a&gt;. You can use another browser&#39;s DevTools instead if you prefer. You&#39;ll just need to map the Chrome DevTools screenshots in this guide back to the relevant features in your browser of choice.&lt;/p&gt;
&lt;h2 id=&quot;understand-responsive-background-images&quot;&gt;Understand responsive background images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-css-background-images-with-media-queries/#understand-responsive-background-images&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;First, analyze the network traffic of the unoptimized demo:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the &lt;a href=&quot;https://use-media-queries-unoptimized.glitch.me/&quot; rel=&quot;noopener&quot;&gt;unoptimized demo&lt;/a&gt; in a new Chrome tab.&lt;/li&gt;
&lt;li&gt;Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Network&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Reload the page.&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; Check out &lt;a href=&quot;https://developer.chrome.com/docs/devtools/network/&quot;&gt;Inspect Network Activity With Chrome DevTools&lt;/a&gt; if you need more help with DevTools. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;You&#39;ll see that the only image that&#39;s being requested is &lt;code&gt;background-desktop.jpg&lt;/code&gt;, which has a size of &lt;strong&gt;1006KB&lt;/strong&gt;:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;DevTools network trace for the unoptimized background image.&quot; decoding=&quot;async&quot; height=&quot;126&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/K8P4MHp2FSnZYTw3ZVkG.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/K8P4MHp2FSnZYTw3ZVkG.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/K8P4MHp2FSnZYTw3ZVkG.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/K8P4MHp2FSnZYTw3ZVkG.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/K8P4MHp2FSnZYTw3ZVkG.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/K8P4MHp2FSnZYTw3ZVkG.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/K8P4MHp2FSnZYTw3ZVkG.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/K8P4MHp2FSnZYTw3ZVkG.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/K8P4MHp2FSnZYTw3ZVkG.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/K8P4MHp2FSnZYTw3ZVkG.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/K8P4MHp2FSnZYTw3ZVkG.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/K8P4MHp2FSnZYTw3ZVkG.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/K8P4MHp2FSnZYTw3ZVkG.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/K8P4MHp2FSnZYTw3ZVkG.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/K8P4MHp2FSnZYTw3ZVkG.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/K8P4MHp2FSnZYTw3ZVkG.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/K8P4MHp2FSnZYTw3ZVkG.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/K8P4MHp2FSnZYTw3ZVkG.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Resize the browser window and notice that the Network Log isn&#39;t showing any new requests being made by the page. This means that the same image background is being used for all screen sizes.&lt;/p&gt;
&lt;p&gt;You can see the styles that control the background image in &lt;a href=&quot;https://use-media-queries-unoptimized.glitch.me/style.css&quot; rel=&quot;noopener&quot;&gt;style.css&lt;/a&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token 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-position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center center&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;background-attachment&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;background-repeat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; no-repeat&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;background-size&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 property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;images/background-desktop.jpg&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;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Here&#39;s the meaning of each of the properties used:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;background-position: center center&lt;/code&gt;: Center the image vertically and horizontally.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;background-repeat: no-repeat&lt;/code&gt;: Show the image only once.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;background-attachment: fixed&lt;/code&gt;: Avoid making the background image scroll.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;background-size: cover&lt;/code&gt;: Resize the image to cover the entire container.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;background-image: url(images/background-desktop.jpg)&lt;/code&gt;: The URL of the image.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When combined, these styles tell the browser to adapt the background image to different screen heights and widths. This is the first step towards achieving a responsive background.&lt;/p&gt;
&lt;p&gt;Using a single background image for all screen sizes has some limitations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The same amount of bytes are sent, regardless of the screen size, even when, for some devices, like phones, a smaller and more lightweight image background would look just as good. In general, you want to send the smallest possible image that still looks good on the user&#39;s screen to improve performance and save user data.&lt;/li&gt;
&lt;li&gt;In smaller devices the image will be stretched or cut to cover the entire screen, potentially hiding relevant parts of the background to users.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the next section, you&#39;ll learn how to apply an optimization to load different background images, according to the user&#39;s device.&lt;/p&gt;
&lt;h2 id=&quot;use-media-queries&quot;&gt;Use media queries &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-css-background-images-with-media-queries/#use-media-queries&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Using media queries is a common technique to declare stylesheets that will only apply to certain media or device types. They are implemented by using &lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@media&quot; rel=&quot;noopener&quot;&gt;@media rules&lt;/a&gt;, which let you define a set of breakpoints, where specific styles are defined.
When the conditions defined by the &lt;code&gt;@media&lt;/code&gt; rule are met (for example, a certain screen width), the group of styles defined inside the breakpoint will be applied.&lt;/p&gt;
&lt;p&gt;The following steps can be used to apply media queries to &lt;a href=&quot;https://use-media-queries-unoptimized.glitch.me/&quot; rel=&quot;noopener&quot;&gt;the site&lt;/a&gt; so that different images are used, depending on the maximum width of the device requesting the page.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In &lt;code&gt;style.css&lt;/code&gt; remove the line that contains the background image URL:&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;span class=&quot;highlight-line&quot;&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;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;background-position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center center&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 property&quot;&gt;background-attachment&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;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;background-repeat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; no-repeat&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;background-size&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;/span&gt;&lt;br /&gt;&lt;del class=&quot;highlight-line highlight-line-remove&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token 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;images/background-desktop.jpg&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;/del&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Next, create a breakpoint for each screen width, based on the common dimensions in pixels that mobile, tablet, and desktop screens usually have:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For mobile:&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;@media&lt;/span&gt; &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; 480px&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;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-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;images/background-mobile.jpg&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;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;For tablets:&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;@media&lt;/span&gt; &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; 481px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;and&lt;/span&gt; &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; 1024px&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;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-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;images/background-tablet.jpg&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;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;For desktop devices:&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;@media&lt;/span&gt; &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; 1025px&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;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-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;images/background-desktop.jpg&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;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;Open the optimized version of &lt;a href=&quot;https://use-media-queries-optimized.glitch.me/style.css&quot; rel=&quot;noopener&quot;&gt;style.css&lt;/a&gt; in your browser to see the changes made.&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 images used in the optimized demo are already resized to fit into different screen sizes. Showing how to resize images is out of the scope of this guide, but if you want to know more about this, the &lt;a href=&quot;https://web.dev/serve-responsive-images/&quot;&gt;Serve responsive images guide&lt;/a&gt; covers some useful tools, like the &lt;a href=&quot;https://www.npmjs.com/package/sharp&quot;&gt;sharp npm package&lt;/a&gt; and the &lt;a href=&quot;https://www.imagemagick.org/script/index.php&quot;&gt;ImageMagick CLI&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;measure-for-different-devices&quot;&gt;Measure for different devices &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-css-background-images-with-media-queries/#measure-for-different-devices&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Next visualize the resulting site in different screen sizes and in simulated mobile devices:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the &lt;a href=&quot;https://use-media-queries-optimized.glitch.me/&quot; rel=&quot;noopener&quot;&gt;optimized site&lt;/a&gt; in a new Chrome tab.&lt;/li&gt;
&lt;li&gt;Make your viewport narrow (less than &lt;code&gt;480px&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Network&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Reload the page.
Notice how the &lt;code&gt;background-mobile.jpg&lt;/code&gt; image was requested.&lt;/li&gt;
&lt;li&gt;Make your viewport wider. Once it&#39;s wider than &lt;code&gt;480px&lt;/code&gt; notice how &lt;code&gt;background-tablet.jpg&lt;/code&gt; is requested. Once it&#39;s wider than &lt;code&gt;1025px&lt;/code&gt; notice how &lt;code&gt;background-desktop.jpg&lt;/code&gt; is requested.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When the width of the browser screen is changed, new images are requested.&lt;/p&gt;
&lt;p&gt;In particular when the width is below the value defined in the mobile breakpoint (480px), you see the following Network Log:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;DevTools network trace for the optimized background image.&quot; decoding=&quot;async&quot; height=&quot;125&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/jd2kHIefYf91udpFEmvx.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/jd2kHIefYf91udpFEmvx.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/jd2kHIefYf91udpFEmvx.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/jd2kHIefYf91udpFEmvx.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/jd2kHIefYf91udpFEmvx.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/jd2kHIefYf91udpFEmvx.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/jd2kHIefYf91udpFEmvx.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/jd2kHIefYf91udpFEmvx.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/jd2kHIefYf91udpFEmvx.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/jd2kHIefYf91udpFEmvx.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/jd2kHIefYf91udpFEmvx.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/jd2kHIefYf91udpFEmvx.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/jd2kHIefYf91udpFEmvx.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/jd2kHIefYf91udpFEmvx.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/jd2kHIefYf91udpFEmvx.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/jd2kHIefYf91udpFEmvx.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/jd2kHIefYf91udpFEmvx.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/jd2kHIefYf91udpFEmvx.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;The size of the new mobile background is &lt;strong&gt;67% smaller&lt;/strong&gt; than the desktop one.&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/optimize-css-background-images-with-media-queries/#effects-on-largest-contentful-paint-lcp&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Elements with CSS background images are considered a candidate for &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt;, however, CSS background images &lt;a href=&quot;https://web.dev/preload-scanner/#css-background-images&quot;&gt;are not discoverable by the browser preload scanner&lt;/a&gt;, which means you could be delaying your site&#39;s LCP if you&#39;re not careful.&lt;/p&gt;
&lt;p&gt;The first option you should consider is whether your LCP candidate image could work in an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; element with &lt;code&gt;srcset&lt;/code&gt; and &lt;code&gt;sizes&lt;/code&gt; attributes for responsiveness. The browser preload scanner &lt;em&gt;will&lt;/em&gt; discover &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements, and send requests for them while the parser is blocked on rendering.&lt;/p&gt;
&lt;p&gt;If you can&#39;t (or don&#39;t want to) avoid using a CSS background image, the second option would be to &lt;a href=&quot;https://web.dev/preload-responsive-images/&quot;&gt;preload responsive images&lt;/a&gt; to ensure you preload the right image for the proper viewport size. The &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; elements &lt;code&gt;media&lt;/code&gt;, &lt;code&gt;imagesrcset&lt;/code&gt;, and &lt;code&gt;imagesizes&lt;/code&gt; attributes hint to the browser that a given resource hint only applies in certain viewport conditions, avoiding multiple wasted preloads when you only want to load the one resource that&#39;s a fit for the current viewport.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/optimize-css-background-images-with-media-queries/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this guide you&#39;ve learned to apply media queries to request background images tailored to specific screen sizes and save bytes when accessing the site on smaller devices, like mobile phones.
You used the &lt;code&gt;@media&lt;/code&gt; rule to implement a responsive background. This technique is widely supported by all browsers.
A new CSS feature: &lt;a href=&quot;https://www.w3.org/TR/css-images-4/#image-set-notation&quot; rel=&quot;noopener&quot;&gt;image-set()&lt;/a&gt;, can be used for the same purpose with fewer lines of code. At the time of this writing, this feature is not supported in all browsers, but you might want to keep an eye on how adoption evolves, as it can represent an interesting alternative to this technique.&lt;/p&gt;
</content>
    <author>
      <name>Demian Renzulli</name>
    </author>
  </entry>
  
  <entry>
    <title>Prefetch resources to speed up future navigations</title>
    <link href="https://web.dev/link-prefetch/"/>
    <updated>2019-09-12T00:00:00Z</updated>
    <id>https://web.dev/link-prefetch/</id>
    <content type="html" mode="escaped">&lt;p&gt;Research shows that &lt;a href=&quot;https://wpostats.com/&quot; rel=&quot;noopener&quot;&gt;faster load times result in higher conversion rates&lt;/a&gt; and better user experiences. If you have insight into how users move through your website and which pages they will likely visit next, you can improve load times of future navigations by downloading the resources for those pages ahead of time.&lt;/p&gt;
&lt;p&gt;This guide explains how to achieve that with &lt;code&gt;&amp;lt;link rel=prefetch&amp;gt;&lt;/code&gt;, a &lt;a href=&quot;https://www.w3.org/TR/resource-hints/&quot; rel=&quot;noopener&quot;&gt;resource hint&lt;/a&gt; that enables you to implement prefetching in an easy and efficient way.&lt;/p&gt;
&lt;h2 id=&quot;improve-navigations-with-rel=prefetch&quot;&gt;Improve navigations with &lt;code&gt;rel=prefetch&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/link-prefetch/#improve-navigations-with-rel=prefetch&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Adding &lt;code&gt;&amp;lt;link rel=prefetch&amp;gt;&lt;/code&gt; to a web page tells the browser to download entire pages, or some of the resources (like scripts or CSS files), that the user might need in the future:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;prefetch&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/articles/&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;img alt=&quot;A diagram showing how link prefetch works.&quot; decoding=&quot;async&quot; height=&quot;413&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/djLGrbmj5eovwa6qhlm1.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;The &lt;code&gt;prefetch&lt;/code&gt; hint consumes extra bytes for resources that are not immediately needed, so this technique needs to be applied thoughtfully; only prefetch resources when you are confident that users will need them. Consider not prefetching when users are on slow connections. You can detect that with the &lt;a href=&quot;https://web.dev/adaptive-serving-based-on-network-quality/&quot;&gt;Network Information API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are different ways to determine which links to prefetch. The simplest one is to prefetch the first link or the first few links on the current page. There are also libraries that use more sophisticated approaches, explained later in this post.&lt;/p&gt;
&lt;h2 id=&quot;use-cases&quot;&gt;Use cases &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/link-prefetch/#use-cases&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;prefetching-subsequent-pages&quot;&gt;Prefetching subsequent pages &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/link-prefetch/#prefetching-subsequent-pages&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Prefetch HTML documents when subsequent pages are predictable, so that when a link is clicked, the page is loaded instantly.&lt;/p&gt;
&lt;p&gt;For example, in a product listing page, you can prefetch the page for the most popular product in the list. In some cases, the next navigation is even easier to anticipate—on a shopping cart page, the likelihood of a user visiting the checkout page is usually high which makes it a good candidate for prefetching.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; eBay implemented prefetching for the first five results on a search page to speed up future pages loads and saw a positive impact on conversion rates. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;While prefetching resources does use additional bandwidth, it can improve most performance metrics. &lt;a href=&quot;https://web.dev/ttfb/&quot;&gt;Time to First Byte (TTFB)&lt;/a&gt; will often be much lower, as the document request results in a cache hit. Because TTFB will be lower, subsequent time-based metrics will often be lower as well, including &lt;a href=&quot;https://web.dev/lcp/&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt; and &lt;a href=&quot;https://web.dev/fcp/&quot;&gt;First Contentful Paint (FCP)&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;prefetching-static-assets&quot;&gt;Prefetching static assets &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/link-prefetch/#prefetching-static-assets&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Prefetch static assets, like scripts or stylesheets, when subsequent sections the user might visit can be predicted. This is especially useful when those assets are shared across many pages.&lt;/p&gt;
&lt;p&gt;For example, Netflix takes advantage of the time users spend on logged-out pages, to prefetch React, which will be used once users log in. Thanks to this, they &lt;a href=&quot;https://medium.com/dev-channel/a-netflix-web-performance-case-study-c0bcde26a9d9&quot; rel=&quot;noopener&quot;&gt;reduced Time to Interactive by 30% for future navigations&lt;/a&gt;.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Gotchas&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; At the time of this writing, it is possible to share prefetched resources among pages served from different origins. When &lt;a href=&quot;https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/6KKXv1PqPZ0/oguPntMGDgAJ&quot;&gt;Double-keyed HTTP cache&lt;/a&gt; ships, this will only work for top-level navigations and same-origin subresources, but it won&#39;t be possible to reuse prefetched subresources among different origins. This means that, if &lt;code&gt;a.com&lt;/code&gt; prefetches the resource &lt;code&gt;b.com/library.js&lt;/code&gt;, it won&#39;t be available in &lt;code&gt;c.com&lt;/code&gt; cache. Some browsers, such as WebKit-based ones, already &lt;a href=&quot;https://webkit.org/blog/7675/intelligent-tracking-prevention/&quot;&gt;partition caches and HTML5 storage&lt;/a&gt; for all third-party domains. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The effect of prefetching static assets on performance metrics depends on the resource being prefetched:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prefetching images can significantly lower LCP times for LCP image elements.&lt;/li&gt;
&lt;li&gt;Prefetching stylesheets can improve both FCP and LCP, as the network time to download the stylesheet will be eliminated. Since stylesheets are render blocking, they can reduce LCP when prefetched. In cases where the subsequent page&#39;s LCP element is a CSS background image requested via the &lt;code&gt;background-image&lt;/code&gt; property, the image will also be prefetched as a dependent resource of the prefetched stylesheet.&lt;/li&gt;
&lt;li&gt;Prefetching JavaScript will allow the processing of the prefetched script to occur much sooner than if it were required to be fetched by the network first during navigation. This can have an effect on responsiveness metrics such as &lt;a href=&quot;https://web.dev/fid/&quot;&gt;First Input Delay (FID)&lt;/a&gt; and &lt;a href=&quot;https://web.dev/inp/&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt;. In cases where markup is rendered on the client via JavaScript, LCP can be improved through reduced resource load delays, and client-side rendering of markup containing a page&#39;s LCP element can occur sooner.&lt;/li&gt;
&lt;li&gt;Prefetching web fonts that are not already used by the current page, can eliminate layout shifts. In cases where &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/font-display/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;font-display: swap;&lt;/code&gt; is used&lt;/a&gt;, the swap period for the font is eliminated, resulting in faster rendering of the text and eliminating layout shifts. If a future page uses the prefetched font and the page&#39;s LCP element is a block of text using a web font, LCP for that element will also be faster.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;prefetching-on-demand-javascript-chunks&quot;&gt;Prefetching on-demand JavaScript chunks &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/link-prefetch/#prefetching-on-demand-javascript-chunks&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://web.dev/reduce-javascript-payloads-with-code-splitting&quot;&gt;Code-splitting&lt;/a&gt; your JavaScript bundles allows you to initially load only parts of an app and lazy-load the rest. If you&#39;re using this technique, you can apply prefetch to routes or components that are not immediately necessary but will likely be requested soon.&lt;/p&gt;
&lt;p&gt;For example, if you have a page that contains a button that opens a dialog box which contains an emoji picker, you can divide it into three JavaScript chunks—home, dialog and picker. Home and dialog could be initially loaded, while the picker could be loaded on-demand. Tools like webpack allow you to instruct the browser to prefetch these on-demand chunks.&lt;/p&gt;
&lt;h2 id=&quot;how-to-implement-rel=prefetch&quot;&gt;How to implement &lt;code&gt;rel=prefetch&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/link-prefetch/#how-to-implement-rel=prefetch&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The simplest way to implement &lt;code&gt;prefetch&lt;/code&gt; is adding a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag to the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of the document:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  ...&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;prefetch&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/articles/&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  ...&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The &lt;code&gt;as&lt;/code&gt; attribute helps the browser set the right headers, and determine whether the resource is already in the cache. Example values for this attribute include: &lt;code&gt;document&lt;/code&gt;, &lt;code&gt;script&lt;/code&gt;, &lt;code&gt;style&lt;/code&gt;, &lt;code&gt;font&lt;/code&gt;, &lt;code&gt;image&lt;/code&gt;, and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/link#Attributes&quot; rel=&quot;noopener&quot;&gt;others&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can also initiate prefetching via the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Link&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Link&lt;/code&gt; HTTP header&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Link: &amp;lt;/css/style.css&amp;gt;; rel=prefetch&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;A benefit of specifying a prefetch hint in the HTTP Header is that the browser doesn&#39;t need to parse the document to find the resource hint, which can offer small improvements in some cases.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; &lt;code&gt;prefetch&lt;/code&gt; is supported in &lt;a href=&quot;https://caniuse.com/#search=prefetch&quot;&gt;all modern browsers except Safari&lt;/a&gt;. You can implement a fallback technique for Safari with &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/XMLHttpRequest&quot;&gt;XHR&lt;/a&gt; requests or the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Fetch_API&quot;&gt;Fetch API&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;prefetching-javascript-modules-with-webpack-magic-comments&quot;&gt;Prefetching JavaScript modules with webpack magic comments &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/link-prefetch/#prefetching-javascript-modules-with-webpack-magic-comments&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;webpack enables you to prefetch scripts for routes or functionality you&#39;re reasonably certain users will visit or use soon.&lt;/p&gt;
&lt;p&gt;The following code snippet lazy-loads a sorting functionality from the &lt;a href=&quot;https://lodash.com/&quot; rel=&quot;noopener&quot;&gt;lodash&lt;/a&gt; library to sort a group of numbers that will be submitted by a form:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;submit&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;lodash.sortby&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;default&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sortInput&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Instead of waiting for the &#39;submit&#39; event to take place to load this functionality, you can prefetch this resource to increase the chances of having it available in the cache by the time the user submits the form. webpack allows that using the &lt;a href=&quot;https://webpack.js.org/api/module-methods/#magic-comments&quot; rel=&quot;noopener&quot;&gt;magic comments&lt;/a&gt; inside &lt;code&gt;import()&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;submit&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;   e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;   &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* webpackPrefetch: true */&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;lodash.sortby&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;         &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;default&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;         &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sortInput&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;         &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This tells webpack to inject the &lt;code&gt;&amp;lt;link rel=&amp;quot;prefetch&amp;quot;&amp;gt;&lt;/code&gt; tag into the HTML document:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;prefetch&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;script&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1.bundle.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The performance benefits of prefetching on-demand chunks is a bit nuanced, but generally speaking, you could expect to see faster responses to interactions which depend on those on-demand chunks, as they will be immediately available. Depending on the nature of the interaction, this could impart a benefit to a page&#39;s INP.&lt;/p&gt;
&lt;p&gt;Prefetching in general also factors into overall &lt;a href=&quot;https://web.dev/prioritize-resources/&quot;&gt;resource prioritization&lt;/a&gt;. When a resource is prefetched, it is done so at the lowest possible priority. Thus, any prefetched resources will not contend for bandwidth for resources needed by the current page.&lt;/p&gt;
&lt;h3 id=&quot;smart-prefetching-with-quicklink-and-guessjs&quot;&gt;Smart prefetching with quicklink and Guess.js &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/link-prefetch/#smart-prefetching-with-quicklink-and-guessjs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You can also implement smarter prefetching with libraries that use &lt;code&gt;prefetch&lt;/code&gt; under the hood:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/GoogleChromeLabs/quicklink&quot; rel=&quot;noopener&quot;&gt;quicklink&lt;/a&gt; uses &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Intersection_Observer_API&quot; rel=&quot;noopener&quot;&gt;Intersection Observer API&lt;/a&gt; to detect when links come into the viewport and prefetches linked resources during &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Window/requestIdleCallback&quot; rel=&quot;noopener&quot;&gt;idle time&lt;/a&gt;. Bonus: quicklink weighs less than 1 KB!&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/guess-js&quot; rel=&quot;noopener&quot;&gt;Guess.js&lt;/a&gt; uses analytics reports to build a predictive model that is used to &lt;a href=&quot;https://web.dev/predictive-prefetching/&quot;&gt;smartly prefetch&lt;/a&gt; only what the user is likely to need.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both quicklink and Guess.js use the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Network_Information_API&quot; rel=&quot;noopener&quot;&gt;Network Information API&lt;/a&gt; to avoid prefetching if a user is on a slow network or has &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Headers/Save-Data&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Save-Data&lt;/code&gt;&lt;/a&gt; turned on.&lt;/p&gt;
&lt;h2 id=&quot;prefetching-under-the-hood&quot;&gt;Prefetching under the hood &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/link-prefetch/#prefetching-under-the-hood&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Resource hints are not mandatory instructions and it&#39;s up to the browser to decide if, and when, they get executed.&lt;/p&gt;
&lt;p&gt;You can use prefetch multiple times in the same page. The browser queues up all hints and requests each resource when it&#39;s &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Link_prefetching_FAQ#How_is_browser_idle_time_determined.3F&quot; rel=&quot;noopener&quot;&gt;idle&lt;/a&gt;. In Chrome, if a prefetch has not finished loading and the user navigates to the destined prefetch resource, the in-flight load is picked up as the navigation by the browser (other browser vendors might implement this differently).&lt;/p&gt;
&lt;p&gt;Prefetching takes place at the &lt;a href=&quot;https://docs.google.com/document/d/1bCDuq9H1ih9iNjgzyAL0gpwNFiEP4TZS-YLRp_RuMlc/edit&quot; rel=&quot;noopener&quot;&gt;&#39;Lowest&#39; priority&lt;/a&gt;, so prefetched resources do not compete for bandwidth with the resources required in the current page.&lt;/p&gt;
&lt;p&gt;Prefetched files are stored in the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Caching&quot; rel=&quot;noopener&quot;&gt;HTTP Cache&lt;/a&gt;, or the &lt;a href=&quot;https://calendar.perfplanet.com/2016/a-tale-of-four-caches/&quot; rel=&quot;noopener&quot;&gt;memory cache&lt;/a&gt; (depending on whether the resource is cacheable or not), for an amount of time that varies by browsers. For example, in Chrome resources are kept around for five minutes, after which the normal &lt;code&gt;Cache-Control&lt;/code&gt; rules for the resource apply.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/link-prefetch/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Using &lt;code&gt;prefetch&lt;/code&gt; can greatly improve load times of future navigations and even make pages appear to load instantly. &lt;code&gt;prefetch&lt;/code&gt; is widely supported in modern browsers, which makes it an attractive technique to improve the navigation experience for many users. This technique requires loading extra bytes that might not be used, so be mindful when you use it; only do it when necessary, and ideally, only on fast networks.&lt;/p&gt;
</content>
    <author>
      <name>Demian Renzulli</name>
    </author><author>
      <name>Jeremy Wagner</name>
    </author>
  </entry>
  
  <entry>
    <title>Two ways to prefetch: `&lt;link&gt;` tags and HTTP headers</title>
    <link href="https://web.dev/codelab-two-ways-to-prefetch/"/>
    <updated>2019-09-12T00:00:00Z</updated>
    <id>https://web.dev/codelab-two-ways-to-prefetch/</id>
    <content type="html" mode="escaped">&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; This codelab uses Chrome DevTools. &lt;a href=&quot;https://www.google.com/chrome&quot;&gt;Download Chrome&lt;/a&gt; if you don&#39;t already have it. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;In this codelab, you&#39;ll implement prefetching in two ways: with &lt;code&gt;&amp;lt;link rel=&amp;quot;prefetch&amp;quot;&amp;gt;&lt;/code&gt; and with HTTP &lt;code&gt;Link&lt;/code&gt; header.&lt;/p&gt;
&lt;p&gt;The sample app is a website that has a promotional landing page with a special discount for the shop&#39;s best selling t-shirt. Since the landing page links to a single product, it&#39;s safe to assume that a high percentage of users will navigate to the product details page. This makes the product page a great candidate to prefetch on the landing page.&lt;/p&gt;
&lt;h2 id=&quot;measure-performance&quot;&gt;Measure performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-two-ways-to-prefetch/#measure-performance&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;First establish the baseline performance:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Click &lt;strong&gt;Remix to Edit&lt;/strong&gt; to make the project editable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To preview the site, press &lt;strong&gt;View App&lt;/strong&gt;. Then press
&lt;strong&gt;Fullscreen&lt;/strong&gt;
&lt;img src=&quot;https://web.dev/images/glitch/fullscreen.svg&quot; alt=&quot;fullscreen&quot; style=&quot;padding: 4px 8px; opacity: .5; border: 1px solid #c3c3c3; border-radius: 5px; margin-top: 0;&quot; /&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the &lt;strong&gt;Network&lt;/strong&gt; tab.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the &lt;strong&gt;Throttling&lt;/strong&gt; drop-down list, select &lt;strong&gt;Fast 3G&lt;/strong&gt; to simulate a slow connection type.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To load the product page, click &lt;strong&gt;Buy now&lt;/strong&gt; in the sample app.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The &lt;code&gt;product-details.html&lt;/code&gt; page takes about 600 ms to load:&lt;/p&gt;
&lt;img alt=&quot;Network panel showing load times for product-details.html&quot; decoding=&quot;async&quot; height=&quot;186&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/MVpybZcY1aF8slLFgJ0n.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/MVpybZcY1aF8slLFgJ0n.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/MVpybZcY1aF8slLFgJ0n.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/MVpybZcY1aF8slLFgJ0n.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/MVpybZcY1aF8slLFgJ0n.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/MVpybZcY1aF8slLFgJ0n.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/MVpybZcY1aF8slLFgJ0n.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/MVpybZcY1aF8slLFgJ0n.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/MVpybZcY1aF8slLFgJ0n.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/MVpybZcY1aF8slLFgJ0n.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/MVpybZcY1aF8slLFgJ0n.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/MVpybZcY1aF8slLFgJ0n.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/MVpybZcY1aF8slLFgJ0n.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/MVpybZcY1aF8slLFgJ0n.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/MVpybZcY1aF8slLFgJ0n.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/MVpybZcY1aF8slLFgJ0n.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/MVpybZcY1aF8slLFgJ0n.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/MVpybZcY1aF8slLFgJ0n.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h2 id=&quot;prefetch-the-product-page-with-lesslink-rel=prefetchgreater&quot;&gt;Prefetch the product page with &lt;code&gt;&amp;lt;link rel=&amp;quot;prefetch&amp;quot;&amp;gt;&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-two-ways-to-prefetch/#prefetch-the-product-page-with-lesslink-rel=prefetchgreater&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To improve navigation, insert a &lt;code&gt;prefetch&lt;/code&gt; tag in the landing page to prefetch the &lt;code&gt;product-details.html&lt;/code&gt; page:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add the following &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; element to the head of the &lt;code&gt;views/index.html&lt;/code&gt; file:&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 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;/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;html&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;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;head&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;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;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;/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;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;viewport&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;width=device-width, initial-scale=1.0&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&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;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/css?family=Montserrat&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;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;prefetch&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/product-details.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;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;	  ...&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token 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;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The &lt;code&gt;as&lt;/code&gt; attribute is optional but recommended; it helps the browser set the right headers and determine whether the resource is already in the cache.  Example values for this attribute include: &lt;code&gt;document&lt;/code&gt;, &lt;code&gt;script&lt;/code&gt;, &lt;code&gt;style&lt;/code&gt;, &lt;code&gt;font&lt;/code&gt;, &lt;code&gt;image&lt;/code&gt;, and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/link#Attributes&quot; rel=&quot;noopener&quot;&gt;others&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To verify that prefetching is working:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;To preview the site, press &lt;strong&gt;View App&lt;/strong&gt;. Then press
&lt;strong&gt;Fullscreen&lt;/strong&gt;
&lt;img src=&quot;https://web.dev/images/glitch/fullscreen.svg&quot; alt=&quot;fullscreen&quot; style=&quot;padding: 4px 8px; opacity: .5; border: 1px solid #c3c3c3; border-radius: 5px; margin-top: 0;&quot; /&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the &lt;strong&gt;Network&lt;/strong&gt; tab.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the &lt;strong&gt;Throttling&lt;/strong&gt; drop-down list, select &lt;strong&gt;Fast 3G&lt;/strong&gt; to simulate a slow connection type.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Clear the Disable cache checkbox.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reload the app.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now when the landing page loads, the &lt;code&gt;product-details.html&lt;/code&gt; page loads too, but at the lowest priority:&lt;/p&gt;
&lt;img alt=&quot;Network panel showing product-details.html prefetched.&quot; decoding=&quot;async&quot; height=&quot;172&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/LDkU6zNbFU7GhPuUcaCR.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/LDkU6zNbFU7GhPuUcaCR.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/LDkU6zNbFU7GhPuUcaCR.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/LDkU6zNbFU7GhPuUcaCR.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/LDkU6zNbFU7GhPuUcaCR.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/LDkU6zNbFU7GhPuUcaCR.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/LDkU6zNbFU7GhPuUcaCR.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/LDkU6zNbFU7GhPuUcaCR.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/LDkU6zNbFU7GhPuUcaCR.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/LDkU6zNbFU7GhPuUcaCR.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/LDkU6zNbFU7GhPuUcaCR.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/LDkU6zNbFU7GhPuUcaCR.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/LDkU6zNbFU7GhPuUcaCR.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/LDkU6zNbFU7GhPuUcaCR.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/LDkU6zNbFU7GhPuUcaCR.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/LDkU6zNbFU7GhPuUcaCR.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/LDkU6zNbFU7GhPuUcaCR.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/LDkU6zNbFU7GhPuUcaCR.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;The page is kept in the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Caching&quot; rel=&quot;noopener&quot;&gt;HTTP cache&lt;/a&gt; for five minutes, after which the normal &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; rules for the document apply. In this case, &lt;code&gt;product-details.html&lt;/code&gt; has  a &lt;code&gt;cache-control&lt;/code&gt; header with a value of &lt;code&gt;public, max-age=0&lt;/code&gt;, which means that the page is kept for a total of five minutes.&lt;/p&gt;
&lt;h3 id=&quot;reevaluate-performance&quot;&gt;Reevaluate performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-two-ways-to-prefetch/#reevaluate-performance&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Reload the app.&lt;/li&gt;
&lt;li&gt;To load the product page, click &lt;strong&gt;Buy now&lt;/strong&gt; in the sample app.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Take a look at the &lt;strong&gt;Network&lt;/strong&gt; panel. There are two differences compared to the initial network trace:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;Size&lt;/strong&gt; column shows &amp;quot;prefetch cache&amp;quot;, which means this resource was retrieved from the browser&#39;s cache rather than the network.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Time&lt;/strong&gt; column shows that the time it takes for the document to load is now about 10 ms.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is approximately a 98% reduction compared to the previous version, which took about 600 ms.&lt;/p&gt;
&lt;img alt=&quot;Network panel showing product-details.html retrieved from prefetch cache.&quot; decoding=&quot;async&quot; height=&quot;223&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/gJZPsifaqFPozkhnMznX.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/gJZPsifaqFPozkhnMznX.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/gJZPsifaqFPozkhnMznX.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/gJZPsifaqFPozkhnMznX.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/gJZPsifaqFPozkhnMznX.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/gJZPsifaqFPozkhnMznX.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/gJZPsifaqFPozkhnMznX.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/gJZPsifaqFPozkhnMznX.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/gJZPsifaqFPozkhnMznX.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/gJZPsifaqFPozkhnMznX.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/gJZPsifaqFPozkhnMznX.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/gJZPsifaqFPozkhnMznX.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/gJZPsifaqFPozkhnMznX.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/gJZPsifaqFPozkhnMznX.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/gJZPsifaqFPozkhnMznX.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/gJZPsifaqFPozkhnMznX.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/gJZPsifaqFPozkhnMznX.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/gJZPsifaqFPozkhnMznX.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h2 id=&quot;extra-credit-use-prefetch-as-a-progressive-enhancement&quot;&gt;Extra credit: Use &lt;code&gt;prefetch&lt;/code&gt; as a progressive enhancement &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-two-ways-to-prefetch/#extra-credit-use-prefetch-as-a-progressive-enhancement&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Prefetching is best implemented as a progressive enhancement for the users who are browsing on fast connections. You can use the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Network_Information_API&quot; rel=&quot;noopener&quot;&gt;Network Information API&lt;/a&gt; to check the network conditions and based on that dynamically inject prefetch tags. That way, you can minimize data consumption and save costs for users on slow or expensive data plans.&lt;/p&gt;
&lt;p&gt;To implement adaptive prefetching, first remove the &lt;code&gt;&amp;lt;link rel=&amp;quot;prefetch&amp;quot;&amp;gt;&lt;/code&gt; tag from &lt;code&gt;views/index.html&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&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;/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;html&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;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;head&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;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;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;/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;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;viewport&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;width=device-width, initial-scale=1.0&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&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;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/css?family=Montserrat&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;/span&gt;&lt;br /&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;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;prefetch&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/product-details.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;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/del&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;	   ...&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;	&lt;span class=&quot;token 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;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Then add the following code to &lt;code&gt;public/script.js&lt;/code&gt; to declare a function that dynamically injects the &lt;code&gt;prefetch&lt;/code&gt; tag when the user is on a fast connection:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;injectLinkPrefetchIn4g&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;effectiveType &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;4g&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;		&lt;span class=&quot;token comment&quot;&gt;//generate link prefetch tag&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; linkTag &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;link&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;		linkTag&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;prefetch&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;		linkTag&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;href &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url&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;		linkTag&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;as &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;document&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;		&lt;span class=&quot;token comment&quot;&gt;//inject tag in the head of the document&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;		document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;head&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;linkTag&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/ins&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The function works as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It checks the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/NetworkInformation/effectiveType&quot; rel=&quot;noopener&quot;&gt;effectiveType&lt;/a&gt; property of the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Network_Information_API&quot; rel=&quot;noopener&quot;&gt;Network Information API&lt;/a&gt; to determine if the user is on a 4G (or faster) connection.&lt;/li&gt;
&lt;li&gt;If that condition is fulfilled, it generates a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag with &lt;code&gt;prefetch&lt;/code&gt; as the type of hint, passes the URL that will be prefetched in the &lt;code&gt;href&lt;/code&gt; attribute, and indicates that the resource is an HTML &lt;code&gt;document&lt;/code&gt; in the &lt;code&gt;as&lt;/code&gt; attribute.&lt;/li&gt;
&lt;li&gt;Finally, it injects the script dynamically in the &lt;code&gt;head&lt;/code&gt; of the page.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next add &lt;code&gt;script.js&lt;/code&gt; to &lt;code&gt;views/index.html&lt;/code&gt;, just before the closing &lt;code&gt;&amp;lt;/body&amp;gt;&lt;/code&gt; tag:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;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;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;      ...&lt;/span&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/script.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token 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;/ins&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;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Requesting &lt;code&gt;script.js&lt;/code&gt; at the end of the page ensures that it will be loaded and executed after the page is parsed and loaded.&lt;/p&gt;
&lt;p&gt;To make sure that the prefetching doesn&#39;t interfere with critical resources for the current page, add the following code snippet to call &lt;code&gt;injectLinkPrefetchIn4g()&lt;/code&gt; on the &lt;code&gt;window.load&lt;/code&gt; event:&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;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;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;      ...&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/script.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token 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 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;ins class=&quot;highlight-line highlight-line-add&quot;&gt;           window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;load&#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;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;                &lt;span class=&quot;token function&quot;&gt;injectLinkPrefetchIn4g&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/product-details.html&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;           &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;      &lt;/span&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;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The landing page now prefetches &lt;code&gt;product-details.html&lt;/code&gt; only on fast connections. To verify that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;To preview the site, press &lt;strong&gt;View App&lt;/strong&gt;. Then press
&lt;strong&gt;Fullscreen&lt;/strong&gt;
&lt;img src=&quot;https://web.dev/images/glitch/fullscreen.svg&quot; alt=&quot;fullscreen&quot; style=&quot;padding: 4px 8px; opacity: .5; border: 1px solid #c3c3c3; border-radius: 5px; margin-top: 0;&quot; /&gt;.&lt;/li&gt;
&lt;li&gt;Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Network&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;In the &lt;strong&gt;Throttling&lt;/strong&gt; drop-down list, select &lt;strong&gt;Online&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Reload the app.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You should see &lt;code&gt;product-details.html&lt;/code&gt; in the Network panel:&lt;/p&gt;
&lt;img alt=&quot;Network panel showing product-details.html prefetched.&quot; decoding=&quot;async&quot; height=&quot;201&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/NCoFDNGs0iSfBkDjiwzd.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/NCoFDNGs0iSfBkDjiwzd.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/NCoFDNGs0iSfBkDjiwzd.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/NCoFDNGs0iSfBkDjiwzd.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/NCoFDNGs0iSfBkDjiwzd.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/NCoFDNGs0iSfBkDjiwzd.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/NCoFDNGs0iSfBkDjiwzd.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/NCoFDNGs0iSfBkDjiwzd.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/NCoFDNGs0iSfBkDjiwzd.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/NCoFDNGs0iSfBkDjiwzd.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/NCoFDNGs0iSfBkDjiwzd.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/NCoFDNGs0iSfBkDjiwzd.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/NCoFDNGs0iSfBkDjiwzd.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/NCoFDNGs0iSfBkDjiwzd.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/NCoFDNGs0iSfBkDjiwzd.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/NCoFDNGs0iSfBkDjiwzd.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/NCoFDNGs0iSfBkDjiwzd.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/NCoFDNGs0iSfBkDjiwzd.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;To verify that the product page isn&#39;t prefetched on slow connections:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In the Throttling drop-down list, select &lt;strong&gt;Slow 3G&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Reload the app.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The &lt;strong&gt;Network&lt;/strong&gt; panel should include only the resources for the landing page without &lt;code&gt;product-details.html&lt;/code&gt;:&lt;/p&gt;
&lt;img alt=&quot;Network panel showing product-details.html not being prefetched.&quot; decoding=&quot;async&quot; height=&quot;171&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/xpHuregNQIEKrVylhG3G.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/xpHuregNQIEKrVylhG3G.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/xpHuregNQIEKrVylhG3G.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/xpHuregNQIEKrVylhG3G.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/xpHuregNQIEKrVylhG3G.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/xpHuregNQIEKrVylhG3G.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/xpHuregNQIEKrVylhG3G.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/xpHuregNQIEKrVylhG3G.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/xpHuregNQIEKrVylhG3G.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/xpHuregNQIEKrVylhG3G.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/xpHuregNQIEKrVylhG3G.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/xpHuregNQIEKrVylhG3G.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/xpHuregNQIEKrVylhG3G.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/xpHuregNQIEKrVylhG3G.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/xpHuregNQIEKrVylhG3G.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/xpHuregNQIEKrVylhG3G.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/xpHuregNQIEKrVylhG3G.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/xpHuregNQIEKrVylhG3G.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h2 id=&quot;prefetch-the-stylesheet-for-the-product-page-with-the-http-link-header&quot;&gt;Prefetch the stylesheet for the product page with the HTTP &lt;code&gt;Link&lt;/code&gt; header &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-two-ways-to-prefetch/#prefetch-the-stylesheet-for-the-product-page-with-the-http-link-header&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The HTTP &lt;code&gt;Link&lt;/code&gt; header can be used to prefetch the same type of resources as the &lt;code&gt;link&lt;/code&gt; tag. Deciding when to use one or the other mostly depends on your preference, as the difference in performance is insignificant. In this case, you&#39;ll use it to prefetch the main CSS of the product page, to further improve its rendering.&lt;/p&gt;
&lt;p&gt;Add an HTTP &lt;code&gt;Link&lt;/code&gt; header for &lt;code&gt;style-product.css&lt;/code&gt; in the server response for the landing page:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the &lt;code&gt;server.js&lt;/code&gt; file and look for the &lt;code&gt;get()&lt;/code&gt; handler for the root url: &lt;code&gt;/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add the following line at the beginning of the handler:&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;ins class=&quot;highlight-line highlight-line-add&quot;&gt;	response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Link&#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;/style-product.css&gt;; rel=prefetch&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/ins&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;	response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__dirname &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/views/index.html&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span 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;ol&gt;
&lt;li&gt;To preview the site, press &lt;strong&gt;View App&lt;/strong&gt;. Then press
&lt;strong&gt;Fullscreen&lt;/strong&gt;
&lt;img src=&quot;https://web.dev/images/glitch/fullscreen.svg&quot; alt=&quot;fullscreen&quot; style=&quot;padding: 4px 8px; opacity: .5; border: 1px solid #c3c3c3; border-radius: 5px; margin-top: 0;&quot; /&gt;.&lt;/li&gt;
&lt;li&gt;Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Network&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Reload the app.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The &lt;code&gt;style-product.css&lt;/code&gt; is now prefetched at the lowest priority after the landing page loads:&lt;/p&gt;
&lt;img alt=&quot;Network panel showing style-product.css prefetched.&quot; decoding=&quot;async&quot; height=&quot;205&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/Memd8AIP4Yr5dhGGi240.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/Memd8AIP4Yr5dhGGi240.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/Memd8AIP4Yr5dhGGi240.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/Memd8AIP4Yr5dhGGi240.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/Memd8AIP4Yr5dhGGi240.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/Memd8AIP4Yr5dhGGi240.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/Memd8AIP4Yr5dhGGi240.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/Memd8AIP4Yr5dhGGi240.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/Memd8AIP4Yr5dhGGi240.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/Memd8AIP4Yr5dhGGi240.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/Memd8AIP4Yr5dhGGi240.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/Memd8AIP4Yr5dhGGi240.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/Memd8AIP4Yr5dhGGi240.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/Memd8AIP4Yr5dhGGi240.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/Memd8AIP4Yr5dhGGi240.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/Memd8AIP4Yr5dhGGi240.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/Memd8AIP4Yr5dhGGi240.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/Memd8AIP4Yr5dhGGi240.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;To navigate to the product page, click &lt;strong&gt;Buy now&lt;/strong&gt;. Take a look at the &lt;strong&gt;Network&lt;/strong&gt; panel:&lt;/p&gt;
&lt;img alt=&quot;Network panel showing style-product.css retrieved from prefetch cache.&quot; decoding=&quot;async&quot; height=&quot;223&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/12tQkKKPqx4JjaWofEYK.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/12tQkKKPqx4JjaWofEYK.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/12tQkKKPqx4JjaWofEYK.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/12tQkKKPqx4JjaWofEYK.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/12tQkKKPqx4JjaWofEYK.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/12tQkKKPqx4JjaWofEYK.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/12tQkKKPqx4JjaWofEYK.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/12tQkKKPqx4JjaWofEYK.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/12tQkKKPqx4JjaWofEYK.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/12tQkKKPqx4JjaWofEYK.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/12tQkKKPqx4JjaWofEYK.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/12tQkKKPqx4JjaWofEYK.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/12tQkKKPqx4JjaWofEYK.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/12tQkKKPqx4JjaWofEYK.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/12tQkKKPqx4JjaWofEYK.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/12tQkKKPqx4JjaWofEYK.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/12tQkKKPqx4JjaWofEYK.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/12tQkKKPqx4JjaWofEYK.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;The &lt;code&gt;style-product.css&lt;/code&gt; file is retrieved from the &amp;quot;prefetch cache&amp;quot; and it took only 12 ms to load.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; When using HTTP &lt;code&gt;Link&lt;/code&gt; header, you can decide whether to prefetch depending on the network conditions based on the information available in &lt;a href=&quot;https://web.dev/performance-optimizing-content-efficiency-client-hints//&quot;&gt;client hints&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
</content>
    <author>
      <name>Demian Renzulli</name>
    </author>
  </entry>
  
  <entry>
    <title>Progressive Web Apps in multi-origin sites</title>
    <link href="https://web.dev/multi-origin-pwas/"/>
    <updated>2019-08-19T00:00:00Z</updated>
    <id>https://web.dev/multi-origin-pwas/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;background&quot;&gt;Background &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-origin-pwas/#background&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the past, there were some advantages to using multi-origin architectures, but for Progressive Web Apps, that approach presents many challenges. In particular, the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Security/Same-origin_policy&quot; rel=&quot;noopener&quot;&gt;same-origin policy&lt;/a&gt;, imposes restrictions for sharing things like service workers and caches, permissions, and for achieving a standalone experience across multiple origins. This article will describe the good and bad uses of multiple origins, and explain the challenges and workarounds for building Progressive Web Apps in multi-origin sites.&lt;/p&gt;
&lt;h2 id=&quot;good-and-bad-uses-of-multiple-origins&quot;&gt;Good and bad uses of multiple origins &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-origin-pwas/#good-and-bad-uses-of-multiple-origins&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are a few legitimate reasons for sites to employ a multi-origin architecture, mostly related to providing an independent set of web applications, or to create experiences that are completely isolated from each other. There are also uses that should be avoided.&lt;/p&gt;
&lt;h3 id=&quot;the-good&quot;&gt;The good &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-origin-pwas/#the-good&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Let&#39;s look at the useful reasons first:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Localization / Language:&lt;/strong&gt; Using a &lt;a href=&quot;https://developer.mozilla.org/docs/Glossary/TLD&quot; rel=&quot;noopener&quot;&gt;country-code top-level domain&lt;/a&gt;, to separate sites to be served in different countries (e.g. &lt;code&gt;https://www.google.com.ar&lt;/code&gt;), or using subdomains to divide sites targeted to different locations (e.g.: &lt;code&gt;https://newyork.craigslist.org&lt;/code&gt;) or to offer content for a specific language (e.g. &lt;code&gt;https://en.wikipedia.org&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Independent webapps:&lt;/strong&gt; Using different subdomains to provide experiences whose purpose differs considerably from the site on the main origin. For example, in a news site, the crosswords webapp could be intentionally served from &lt;code&gt;https://crosswords.example.com&lt;/code&gt;, and installed and used as an independent PWA, without having to share any resources or functionality with the main website.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-bad&quot;&gt;The bad &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-origin-pwas/#the-bad&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you&#39;re not doing any of these things, it&#39;s likely that using a multi-origin architecture will be a disadvantage when building Progressive Web Apps.&lt;/p&gt;
&lt;p&gt;Despite this, many sites continue being structured this way for no particular reason, or for &#39;legacy&#39; reasons. One example is using subdomains to arbitrarily separate parts of a site that should be part of a unified experience.&lt;/p&gt;
&lt;p&gt;The following patterns, for example, are highly discouraged:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Site sections:&lt;/strong&gt; Separating different sections of a site on subdomains. In news sites, it&#39;s not uncommon to see the home page at: &lt;code&gt;https://www.example.com&lt;/code&gt;, while the sports section lives at &lt;code&gt;https://sports.example.com&lt;/code&gt;, politics at &lt;code&gt;https://politics.example.com&lt;/code&gt;, and so forth. In the case of an e-commerce site, using something like &lt;code&gt;https://category.example.com&lt;/code&gt; for product categories, &lt;code&gt;https://product.example.com&lt;/code&gt; for product pages, etc.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;User Flow:&lt;/strong&gt; Another approach that&#39;s discouraged is to separate different smaller parts of the site, like pages for the login or purchase flows in subdomains. For example, using &lt;code&gt;https://login.example.com&lt;/code&gt;, and &lt;code&gt;https://checkout.example.com&lt;/code&gt;.&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; When building a site from scratch it&#39;s highly recommended to avoid dividing it into subdomains. For existing sites, migrating to a single origin is the best approach. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;For those cases where migrating to a single origin is not possible, what follows is a list of challenges, and (where possible), workarounds that can be considered when building Progressive Web Apps.&lt;/p&gt;
&lt;h2 id=&quot;challenges-and-workarounds-for-pwas-across-different-origins&quot;&gt;Challenges and Workarounds for PWAs across different origins &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-origin-pwas/#challenges-and-workarounds-for-pwas-across-different-origins&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When building a website on multiple origins, providing a unified PWA experience is challenging, mostly because of the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Security/Same-origin_policy&quot; rel=&quot;noopener&quot;&gt;same-origin policy&lt;/a&gt;, which imposes a number of constraints. Let&#39;s look at them one at a time.&lt;/p&gt;
&lt;h3 id=&quot;service-workers&quot;&gt;Service workers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-origin-pwas/#service-workers&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The origin of the service worker script URL has to be the same as the origin of the page calling &lt;a href=&quot;https://w3c.github.io/ServiceWorker/#navigator-service-worker-register&quot; rel=&quot;noopener&quot;&gt;register()&lt;/a&gt;. This means that, for example, a page at &lt;code&gt;https://www.example.com&lt;/code&gt; can&#39;t call &lt;code&gt;register()&lt;/code&gt; with a service worker url at &lt;code&gt;https://section.example.com&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Another consideration is that a service worker can only control pages hosted under the origin and path it belongs to. This means that, if the service worker is hosted at &lt;code&gt;https://www.example.com&lt;/code&gt; it can only control URLs from that origin (according to the path defined in the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/ServiceWorkerContainer/register#Parameters&quot; rel=&quot;noopener&quot;&gt;scope parameter&lt;/a&gt;), but won&#39;t control any page in other subdomains such as, for example, those in &lt;code&gt;https://section.example.com&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In this case, the only workaround is to use multiple service workers (one per origin).&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; Registering, and having multiple active service workers consumes additional resources (memory, CPU, etc.), so use your best judgement on how many active service workers a user will likely need to navigate across the site. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;caching&quot;&gt;Caching &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-origin-pwas/#caching&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Cache object, indexedDB, and localStorage are also constrained to a single origin. This means it&#39;s not possible to access the caches that belong to &lt;code&gt;https://www.example.com&lt;/code&gt;, from, for example: &lt;code&gt;https://www.section.example.com&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here are some things you can do to manage caches properly in scenarios like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Leverage browser caching:&lt;/strong&gt; Using &lt;a href=&quot;https://webkit.org/blog/8090/workers-at-your-service/&quot; rel=&quot;noopener&quot;&gt;traditional browser caching best practices&lt;/a&gt; is always recommended. This technique provides the added benefit of reusing cached resources across origins, which can&#39;t be done with the service worker&#39;s cache. For best practices on how to use HTTP Cache with service workers, you can take a look at &lt;a href=&quot;https://jakearchibald.com/2016/caching-best-practices/#the-service-worker-the-http-cache-play-well-together-dont-make-them-fight&quot; rel=&quot;noopener&quot;&gt;this post&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Keep service worker installation lightweight:&lt;/strong&gt; If you are maintaining multiple service workers, avoid making users pay a big installation cost every time they navigate to a new origin. In other words: only pre-cache resources that are absolutely necessary.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Gotchas&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Once the service worker is active and running, the same-origin policy also restricts cross-origin requests made &lt;strong&gt;&lt;em&gt;inside&lt;/em&gt;&lt;/strong&gt; service workers. Fortunately this has a recommended workaround, which is to use &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/CORS&quot;&gt;CORS&lt;/a&gt; (as explained &lt;a href=&quot;https://developers.google.com/web/ilt/pwa/working-with-the-fetch-api#cross-origin_requests&quot;&gt;here&lt;/a&gt;). Using the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters&quot;&gt;no-cors mode&lt;/a&gt; when fetching resources inside the service worker is not recommended. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;permissions&quot;&gt;Permissions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-origin-pwas/#permissions&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Permissions are also scoped to origins. This means that if a user granted a given permission to the origin &lt;code&gt;https://section.example.com&lt;/code&gt;, it won&#39;t carry over to other origins, like &lt;code&gt;https://www.example.com&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Since there&#39;s no way to share permissions across origins, the only solution here is to ask for permission on each of subdomain where a given feature is required (e.g. location). For things like web push, you can maintain a cookie to track if the permission has been accepted by the user in another subdomain, to avoid requesting it again.&lt;/p&gt;
&lt;h3 id=&quot;installation&quot;&gt;Installation &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-origin-pwas/#installation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To install a PWA, each origin must have its own manifest with a &lt;code&gt;start_url&lt;/code&gt; that&#39;s &lt;a href=&quot;https://w3c.github.io/manifest/#start_url-member&quot; rel=&quot;noopener&quot;&gt;relative to itself&lt;/a&gt;. This means that a user receiving the installation prompt on a given origin (i.e: &lt;code&gt;https://section.example.com&lt;/code&gt;) won&#39;t be able to install the PWA with a &lt;code&gt;start_url&lt;/code&gt; on a different one (i.e: &lt;code&gt;https://www.example.com&lt;/code&gt;).
In other words, users receiving the installation prompt in a subdomain will only be able to install PWAs for the subpages, not for the main URL of the app.&lt;/p&gt;
&lt;p&gt;There&#39;s also the issue that the same user could receive multiple installation prompts when navigating the site, if each subdomain meets the &lt;a href=&quot;https://developers.google.com/web/fundamentals/app-install-banners/#criteria&quot; rel=&quot;noopener&quot;&gt;installation criteria&lt;/a&gt;, and prompts the user to install the PWA.&lt;/p&gt;
&lt;p&gt;To mitigate this problem you can make sure that the prompt is shown only on the main origin. When a user visits a subdomain that passes the installation criteria:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://developers.google.com/web/fundamentals/app-install-banners/#listen_for_beforeinstallprompt&quot; rel=&quot;noopener&quot;&gt;Listen for &lt;code&gt;beforeinstallprompt&lt;/code&gt; event&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developers.google.com/web/fundamentals/app-install-banners/#preventing_the_mini-infobar_from_appearing&quot; rel=&quot;noopener&quot;&gt;Prevent the prompt from appearing&lt;/a&gt;, calling &lt;code&gt;event.preventDefault()&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That way, you make sure the prompt is not shown in unintended parts of the site, while you can continue showing it, for example, in the main origin (e.g. Home page).&lt;/p&gt;
&lt;h3 id=&quot;standalone-mode&quot;&gt;Standalone Mode &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-origin-pwas/#standalone-mode&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;While navigating in a standalone window, the browser will behave differently when the user moves outside of the scope set by the PWA&#39;s manifest. The behavior depends on each browser version and vendor. For example, the latest Chrome versions open a &lt;a href=&quot;https://developer.chrome.com/multidevice/android/customtabs&quot; rel=&quot;noopener&quot;&gt;Chrome Custom Tab&lt;/a&gt;, when a user moves out of the scope in standalone mode.&lt;/p&gt;
&lt;p&gt;In most cases, there&#39;s no solution for this, but a workaround can be applied for small parts of the experience that are hosted in subdomains (for example: login workflows):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The new URL, &lt;code&gt;https://login.example.com&lt;/code&gt;, could open inside a full screen iframe.&lt;/li&gt;
&lt;li&gt;Once the task is completed inside the iframe (for example, the login process), &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Window/postMessage&quot; rel=&quot;noopener&quot;&gt;postMessage()&lt;/a&gt; can be used, to pass any resulting information from the iframe back to the parent page.&lt;/li&gt;
&lt;li&gt;As a final step, once the message is received by the main page, the listeners can be unregistered, and the iframe finally be removed from the DOM.&lt;/li&gt;
&lt;/ol&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 previous technique can help mitigating the potential UI change in a small part of the site, where the user can perform an action in a subdomain and return to the main origin (like in a login flow), but won&#39;t be an efficient technique to implement for entire paths, including many pages hosted in subdomains (like entire site sections). &lt;/div&gt;&lt;/aside&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; In the context of &lt;a href=&quot;https://developer.chrome.com/docs/android/trusted-web-activity/&quot;&gt;Trusted Web Actitivies&lt;/a&gt;, there&#39;s a recommended way of avoiding this issue, by &lt;a href=&quot;https://developers.google.com/web/updates/2020/01/twa-multi-origin&quot;&gt;validating all origins using Digital Asset Links&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/multi-origin-pwas/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Same-origin policy imposes many restrictions for sites built on top of multiple origins that want to achieve a coherent PWA experience. For that reason, to provide the best experience to users, we strongly recommend against dividing sites into different origins.&lt;/p&gt;
&lt;p&gt;For existing sites that are already built in this way, it can be challenging to make multi-origin PWAs work correctly, but we have explored some potential workarounds. Each can come with tradeoffs, so use your judgement when deciding which approach to take on your website.&lt;/p&gt;
&lt;p&gt;When evaluating a long-term strategy or site redesign, consider migrating to a single origin, unless there&#39;s an important reason to keep the multi-origin architecture.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;With many thanks for their technical reviews and suggestions: Penny Mclachlan, Paul Covell, Dominick Ng, Alberto Medina, Pete LePage, Joe Medley, Cheney Tsai, Martin Schierle, and Andre Bandarra.&lt;/em&gt;&lt;/p&gt;
</content>
    <author>
      <name>Demian Renzulli</name>
    </author>
  </entry>
  
  <entry>
    <title>Minify CSS</title>
    <link href="https://web.dev/minify-css/"/>
    <updated>2019-05-02T00:00:00Z</updated>
    <id>https://web.dev/minify-css/</id>
    <content type="html" mode="escaped">&lt;p&gt;CSS files can contain unnecessary characters, such as comments, whitespaces, and indentation.
In production, these characters can be safely removed, to reduce file size without affecting how the browser processes the styles. This technique is called &lt;strong&gt;minification&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;loading-unminified-css&quot;&gt;Loading unminified CSS &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/minify-css/#loading-unminified-css&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Take a look at the following CSS block:&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;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;Benton Sans&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Helvetica Neue&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; helvetica&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arial&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;/* all titles need to have the same font, color and background */&lt;/span&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-style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; italic&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #373fff&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; #000000&lt;span 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;h2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; italic&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #373fff&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; #000000&lt;span 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 content is easy to read, at the cost of producing a larger than necessary file:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It uses spaces for indentation purposes and contains comments, which are ignored by the browser, so they can be removed.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; elements have the same styles: instead of declaring them separately: &amp;quot;&lt;code&gt;h1 {...} h2 {...}&lt;/code&gt;&amp;quot; they could be expressed as &amp;quot;&lt;code&gt;h1, h2{...}&lt;/code&gt;&amp;quot;.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;background-color&lt;/strong&gt;, &lt;code&gt;#000000&lt;/code&gt; could be expressed as just &lt;code&gt;#000&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After making these changes, you would obtain a more compact version of the same styles:&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;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&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;Benton Sans&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Helvetica Neue&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;helvetica&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;arial&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;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;2em&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token selector&quot;&gt;h1,h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;font-style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;italic&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;#373fff&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&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;#000&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You probably don&#39;t want to write CSS like that. Instead, you can write CSS as usual, and add a minification step to your build process. In this guide, you&#39;ll learn how to do it by using a popular build tool: &lt;a href=&quot;https://webpack.js.org/&quot; rel=&quot;noopener&quot;&gt;webpack&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;measure&quot;&gt;Measure &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/minify-css/#measure&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You&#39;ll apply CSS minification to a site that has been used in other guides: &lt;a href=&quot;https://fav-kitties-animated.glitch.me/&quot; rel=&quot;noopener&quot;&gt;Fav Kitties&lt;/a&gt;. This version of the site uses a cool CSS library: &lt;a href=&quot;https://github.com/daneden/animate.css&quot; rel=&quot;noopener&quot;&gt;animate.css&lt;/a&gt;, to animate different page elements when a user votes for a cat 😺.&lt;/p&gt;
&lt;p&gt;As a first step, you need to understand what would be the opportunity after minifying this file:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open &lt;a href=&quot;https://web.dev/measure&quot;&gt;the measure page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Enter the URL: &lt;code&gt;https://fav-kitties-animated.glitch.me&lt;/code&gt; and click &lt;strong&gt;Run Audit&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;View report&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;Performance&lt;/strong&gt; and go the &lt;strong&gt;Opportunities&lt;/strong&gt; section.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The resulting report shows that up to &lt;strong&gt;16 KB&lt;/strong&gt; can be saved from the &lt;strong&gt;animate.css&lt;/strong&gt; file:&lt;/p&gt;
&lt;img alt=&quot;Lighthouse: Minify CSS opportunity.&quot; class=&quot;screenshot&quot; decoding=&quot;async&quot; height=&quot;172&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/RFMk5OMAIvOlkUZJTsh4.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/RFMk5OMAIvOlkUZJTsh4.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/RFMk5OMAIvOlkUZJTsh4.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/RFMk5OMAIvOlkUZJTsh4.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/RFMk5OMAIvOlkUZJTsh4.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/RFMk5OMAIvOlkUZJTsh4.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/RFMk5OMAIvOlkUZJTsh4.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/RFMk5OMAIvOlkUZJTsh4.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/RFMk5OMAIvOlkUZJTsh4.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/RFMk5OMAIvOlkUZJTsh4.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/RFMk5OMAIvOlkUZJTsh4.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/RFMk5OMAIvOlkUZJTsh4.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/RFMk5OMAIvOlkUZJTsh4.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/RFMk5OMAIvOlkUZJTsh4.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/RFMk5OMAIvOlkUZJTsh4.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/RFMk5OMAIvOlkUZJTsh4.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/RFMk5OMAIvOlkUZJTsh4.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/RFMk5OMAIvOlkUZJTsh4.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Now inspect the content of the CSS:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the &lt;a href=&quot;https://fav-kitties-animated.glitch.me/&quot; rel=&quot;noopener&quot;&gt;Fav Kitties site&lt;/a&gt; in Chrome. (It might take a while for Glitch servers to respond the first time.)&lt;/li&gt;
&lt;li&gt;Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Network&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;CSS&lt;/strong&gt; filter.&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;Disable cache&lt;/strong&gt; checkbox.&lt;/li&gt;
&lt;li&gt;Reload the app.&lt;/li&gt;
&lt;/ol&gt;
&lt;img alt=&quot;DevTools CSS unoptimized trace&quot; decoding=&quot;async&quot; height=&quot;138&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/WgneNAyftk8jneyXxMih.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/WgneNAyftk8jneyXxMih.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/WgneNAyftk8jneyXxMih.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/WgneNAyftk8jneyXxMih.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/WgneNAyftk8jneyXxMih.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/WgneNAyftk8jneyXxMih.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/WgneNAyftk8jneyXxMih.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/WgneNAyftk8jneyXxMih.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/WgneNAyftk8jneyXxMih.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/WgneNAyftk8jneyXxMih.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/WgneNAyftk8jneyXxMih.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/WgneNAyftk8jneyXxMih.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/WgneNAyftk8jneyXxMih.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/WgneNAyftk8jneyXxMih.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/WgneNAyftk8jneyXxMih.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/WgneNAyftk8jneyXxMih.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/WgneNAyftk8jneyXxMih.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/WgneNAyftk8jneyXxMih.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;The page is requesting two CSS files, of &lt;strong&gt;1.9KB&lt;/strong&gt; and &lt;strong&gt;76.2KB&lt;/strong&gt; respectively.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;animate.css&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Response&lt;/strong&gt; tab, to see the file contents.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Note that the stylesheet contains characters for whitespaces and indentation:&lt;/p&gt;
&lt;img alt=&quot;DevTools CSS unoptimized response&quot; decoding=&quot;async&quot; height=&quot;286&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/UEB5Xxe5IHhGtMx3XfKD.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/UEB5Xxe5IHhGtMx3XfKD.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/UEB5Xxe5IHhGtMx3XfKD.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/UEB5Xxe5IHhGtMx3XfKD.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/UEB5Xxe5IHhGtMx3XfKD.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/UEB5Xxe5IHhGtMx3XfKD.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/UEB5Xxe5IHhGtMx3XfKD.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/UEB5Xxe5IHhGtMx3XfKD.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/UEB5Xxe5IHhGtMx3XfKD.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/UEB5Xxe5IHhGtMx3XfKD.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/UEB5Xxe5IHhGtMx3XfKD.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/UEB5Xxe5IHhGtMx3XfKD.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/UEB5Xxe5IHhGtMx3XfKD.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/UEB5Xxe5IHhGtMx3XfKD.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/UEB5Xxe5IHhGtMx3XfKD.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/UEB5Xxe5IHhGtMx3XfKD.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/UEB5Xxe5IHhGtMx3XfKD.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/UEB5Xxe5IHhGtMx3XfKD.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Next, you&#39;ll add some webpack plugins to your build process to minify these files.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Note:&lt;/strong&gt; the previous Lighthouse report only lists &lt;code&gt;animate.css&lt;/code&gt; as an opportunity for minification. Minifying &lt;code&gt;style.css&lt;/code&gt; will also save some bytes, but not enough for Lighthouse to consider it a significant savings. However, minifying CSS is a general best practice; so it makes sense to minify all of your CSS files. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;css-minification-with-webpack&quot;&gt;CSS Minification with webpack &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/minify-css/#css-minification-with-webpack&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before jumping into the optimizations, take some time understanding how build process for the &lt;a href=&quot;https://glitch.com/edit/#!/fav-kitties-animated?path=webpack.config.js:1:0%5D&quot; rel=&quot;noopener&quot;&gt;Fav Kitties site&lt;/a&gt; works:&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 420px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/fav-kitties-animated?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=webpack.config.js&amp;previewSize=0&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;fav-kitties-animated on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;By default, the resulting JS bundle that webpack produces would contain the content of the CSS files inlined. Since we want to maintain separate CSS files, we are using two complementary plugins:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/webpack-contrib/mini-css-extract-plugin&quot; rel=&quot;noopener&quot;&gt;mini-css-extract-plugin&lt;/a&gt; will extract each style sheet into its own file, as one of the steps of the build process.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/fqborges/webpack-fix-style-only-entries&quot; rel=&quot;noopener&quot;&gt;webpack-fix-style-only-entries&lt;/a&gt; is used to correct an issue in wepback 4, to avoid generating an extra JS file for each CSS file listed in &lt;strong&gt;webpack-config.js&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You will now make some changes in the project:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open &lt;a href=&quot;https://glitch.com/~fav-kitties-animated&quot; rel=&quot;noopener&quot;&gt;the Fav Kitties project in Glitch&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;To view the source, press &lt;strong&gt;View Source&lt;/strong&gt;.&lt;/li&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;/ol&gt;
&lt;p&gt;To minify the resulting CSS, you&#39;ll use the &lt;a href=&quot;https://github.com/NMFR/optimize-css-assets-webpack-plugin&quot; rel=&quot;noopener&quot;&gt;optimize-css-assets-webpack-plugin&lt;/a&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In Glitch console, run &lt;code&gt;npm install --save-dev optimize-css-assets-webpack-plugin&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;refresh&lt;/code&gt;, so the changes are synchronized with the Glitch editor.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Next, go back to the Glitch editor, open the &lt;strong&gt;webpack.config.js&lt;/strong&gt; file, and make the following modifications:&lt;/p&gt;
&lt;p&gt;Load the module at the beginning of the file:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; OptimizeCSSAssetsPlugin &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;&quot;optimize-css-assets-webpack-plugin&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;Then, pass an instance of the plugin to the &lt;strong&gt;plugins&lt;/strong&gt; array:&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;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;HtmlWebpackPlugin&lt;/span&gt;&lt;span class=&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;template&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./src/index.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MiniCssExtractPlugin&lt;/span&gt;&lt;span class=&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;filename&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[name].css&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FixStyleOnlyEntriesPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&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;OptimizeCSSAssetsPlugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&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;After making the changes a rebuild of the project will be triggered.
This is how the resulting &lt;strong&gt;webpack.config.js&lt;/strong&gt; will look like:&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 420px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/fav-kitties-animated-min?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=webpack.config.js&amp;previewSize=0&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;fav-kitties-animated-min on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Next, you&#39;ll check the result of this optimization with performance tools.&lt;/p&gt;
&lt;h2 id=&quot;verify&quot;&gt;Verify &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/minify-css/#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 you got lost in any previous step, you can click
&lt;a href=&quot;https://fav-kitties-animated-min.glitch.me/&quot; rel=&quot;noopener&quot;&gt;here&lt;/a&gt;, to open an optimized
version of the site.&lt;/p&gt;
&lt;p&gt;To inspect the size and content of the files:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Network&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;CSS&lt;/strong&gt; filter.&lt;/li&gt;
&lt;li&gt;Select the &lt;strong&gt;Disable cache&lt;/strong&gt; checkbox if it isn&#39;t already.&lt;/li&gt;
&lt;li&gt;Reload the app.&lt;/li&gt;
&lt;/ol&gt;
&lt;img alt=&quot;DevTools CSS unoptimized response&quot; decoding=&quot;async&quot; height=&quot;130&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/id5kWwB3NilmVPWPTM59.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/id5kWwB3NilmVPWPTM59.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/id5kWwB3NilmVPWPTM59.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/id5kWwB3NilmVPWPTM59.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/id5kWwB3NilmVPWPTM59.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/id5kWwB3NilmVPWPTM59.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/id5kWwB3NilmVPWPTM59.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/id5kWwB3NilmVPWPTM59.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/id5kWwB3NilmVPWPTM59.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/id5kWwB3NilmVPWPTM59.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/id5kWwB3NilmVPWPTM59.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/id5kWwB3NilmVPWPTM59.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/id5kWwB3NilmVPWPTM59.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/id5kWwB3NilmVPWPTM59.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/id5kWwB3NilmVPWPTM59.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/id5kWwB3NilmVPWPTM59.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/id5kWwB3NilmVPWPTM59.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/id5kWwB3NilmVPWPTM59.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;You can inspect these files, and see that the new versions don&#39;t contain any whitespaces. Both files are much smaller, in particular, the &lt;a href=&quot;http://fav-kitties-animated-min.glitch.me/animate.css&quot; rel=&quot;noopener&quot;&gt;animate.css&lt;/a&gt; has been reduced in &lt;strong&gt;~26%&lt;/strong&gt;, saving &lt;strong&gt;~20KB&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;As a final step:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open &lt;a href=&quot;https://web.dev/measure&quot;&gt;the measure page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Enter the URL of the optimized site.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;View report&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;Performance&lt;/strong&gt; and find the &lt;strong&gt;Opportunities&lt;/strong&gt; section.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The report doesn&#39;t show &amp;quot;Minify CSS&amp;quot; as &amp;quot;Opportunity&amp;quot; anymore, and has now moved to &amp;quot;Passed Audits&amp;quot; section:&lt;/p&gt;
&lt;img alt=&quot;Lighthouse Passed Audits for optimized page.&quot; decoding=&quot;async&quot; height=&quot;163&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/zegn2qIHYYK58w1GhgYd.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/zegn2qIHYYK58w1GhgYd.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/zegn2qIHYYK58w1GhgYd.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/zegn2qIHYYK58w1GhgYd.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/zegn2qIHYYK58w1GhgYd.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/zegn2qIHYYK58w1GhgYd.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/zegn2qIHYYK58w1GhgYd.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/zegn2qIHYYK58w1GhgYd.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/zegn2qIHYYK58w1GhgYd.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/zegn2qIHYYK58w1GhgYd.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/zegn2qIHYYK58w1GhgYd.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/zegn2qIHYYK58w1GhgYd.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/zegn2qIHYYK58w1GhgYd.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/zegn2qIHYYK58w1GhgYd.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/zegn2qIHYYK58w1GhgYd.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/zegn2qIHYYK58w1GhgYd.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/zegn2qIHYYK58w1GhgYd.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/zegn2qIHYYK58w1GhgYd.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Since CSS files are &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/render-blocking-resources&quot; rel=&quot;noopener&quot;&gt;render-blocking resources&lt;/a&gt;, if you apply minification on sites that use large CSS files, you can see improvements on metrics like &lt;a href=&quot;https://web.dev/fcp/&quot;&gt;First Contentful Paint&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;next-steps-and-resources&quot;&gt;Next steps and resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/minify-css/#next-steps-and-resources&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this guide, we&#39;ve covered CSS Minification with webpack, but the same approach can be followed with other build tools, like &lt;a href=&quot;https://www.npmjs.com/package/gulp-clean-css&quot; rel=&quot;noopener&quot;&gt;gulp-clean-css&lt;/a&gt; for &lt;a href=&quot;https://gulpjs.com/&quot; rel=&quot;noopener&quot;&gt;Gulp&lt;/a&gt;, or
&lt;a href=&quot;https://www.npmjs.com/package/grunt-contrib-cssmin&quot; rel=&quot;noopener&quot;&gt;grunt-contrib-cssmin&lt;/a&gt; for &lt;a href=&quot;https://gruntjs.com/&quot; rel=&quot;noopener&quot;&gt;Grunt&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Minification can also be applied to other types of files. Check out the &lt;a href=&quot;https://web.dev/fast/reduce-network-payloads-using-text-compression&quot;&gt;Minify and compress network payloads guide&lt;/a&gt; to learn more about tools to minify JS, and some complementary techniques, like compression.&lt;/p&gt;
</content>
    <author>
      <name>Demian Renzulli</name>
    </author>
  </entry>
  
  <entry>
    <title>Defer non-critical CSS</title>
    <link href="https://web.dev/defer-non-critical-css/"/>
    <updated>2019-02-17T00:00:00Z</updated>
    <id>https://web.dev/defer-non-critical-css/</id>
    <content type="html" mode="escaped">&lt;p&gt;CSS files are &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/render-blocking-resources/&quot; rel=&quot;noopener&quot;&gt;render-blocking resources&lt;/a&gt;:
they must be loaded and processed before the browser renders the page. Web pages that contain unnecessarily large styles
take longer to render.&lt;/p&gt;
&lt;p&gt;In this guide, you&#39;ll learn how to defer non-critical CSS with the goal of optimizing the &lt;a href=&quot;https://web.dev/critical-rendering-path/&quot;&gt;Critical Rendering Path&lt;/a&gt;, and improving &lt;a href=&quot;https://web.dev/fcp/&quot;&gt;First Contentful Paint (FCP)&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;loading-css-in-a-suboptimal-way&quot;&gt;Loading CSS in a suboptimal way &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/defer-non-critical-css/#loading-css-in-a-suboptimal-way&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The following example contains an accordion with three hidden paragraphs of text, each of which is styled with a different class:&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 420px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/defer-css-unoptimized?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=index.html&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;defer-css-unoptimized on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;This page requests a CSS file with eight classes, but not all of them are
necessary to render the &amp;quot;visible&amp;quot; content.&lt;/p&gt;
&lt;p&gt;The goal of this guide is to optimize this page, so only the &lt;strong&gt;critical&lt;/strong&gt; styles
are loaded synchronously, while the rest (like the ones applied to paragraphs),
are loaded in a non-blocking way.&lt;/p&gt;
&lt;h2 id=&quot;measure&quot;&gt;Measure &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/defer-non-critical-css/#measure&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run &lt;a href=&quot;https://web.dev/discover-performance-opportunities-with-lighthouse/#run-lighthouse-from-chrome-devtools&quot;&gt;Lighthouse&lt;/a&gt; on &lt;a href=&quot;https://defer-css-unoptimized.glitch.me/&quot; rel=&quot;noopener&quot;&gt;the page&lt;/a&gt; and go to the &lt;strong&gt;Performance&lt;/strong&gt; section.&lt;/p&gt;
&lt;p&gt;The report shows the &lt;strong&gt;First Contentful Paint&lt;/strong&gt; metric with a value of &amp;quot;1s&amp;quot;, and
the opportunity &lt;strong&gt;Eliminate render-blocking resources&lt;/strong&gt;, pointing to the
&lt;strong&gt;style.css&lt;/strong&gt; file:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Lighthouse report for unoptimized page, showing FCP of &amp;#x27;1s&amp;#x27; and &amp;#x27;Eliminate blocking resources&amp;#x27; under &amp;#x27;Opportunities&amp;#x27;&quot; decoding=&quot;async&quot; height=&quot;640&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/eZtuQ2IwL3Mtnmz09bmp.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/eZtuQ2IwL3Mtnmz09bmp.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/eZtuQ2IwL3Mtnmz09bmp.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/eZtuQ2IwL3Mtnmz09bmp.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/eZtuQ2IwL3Mtnmz09bmp.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/eZtuQ2IwL3Mtnmz09bmp.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/eZtuQ2IwL3Mtnmz09bmp.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/eZtuQ2IwL3Mtnmz09bmp.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/eZtuQ2IwL3Mtnmz09bmp.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/eZtuQ2IwL3Mtnmz09bmp.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/eZtuQ2IwL3Mtnmz09bmp.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/eZtuQ2IwL3Mtnmz09bmp.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/eZtuQ2IwL3Mtnmz09bmp.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/eZtuQ2IwL3Mtnmz09bmp.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/eZtuQ2IwL3Mtnmz09bmp.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/eZtuQ2IwL3Mtnmz09bmp.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/eZtuQ2IwL3Mtnmz09bmp.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/eZtuQ2IwL3Mtnmz09bmp.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The CSS we are using for this demo site is quite small. If you were requesting larger CSS files (which are not uncommon in production scenarios), and if Lighthouse detects that a page has at least 2048 bytes of CSS rules that weren&#39;t used while rendering the &lt;strong&gt;above the fold&lt;/strong&gt; content, you&#39;ll also receive a suggestion called &lt;strong&gt;Remove Unused CSS&lt;/strong&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;To visualize how this CSS blocks rendering:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open &lt;a href=&quot;https://defer-css-unoptimized.glitch.me/&quot; rel=&quot;noopener&quot;&gt;the page&lt;/a&gt; in Chrome.&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;Performance&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;In the Performance panel, click &lt;strong&gt;Reload&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In the resulting trace, you&#39;ll see that the &lt;strong&gt;FCP&lt;/strong&gt; marker is placed immediately
after the CSS finishes loading:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;DevTools performance trace for unoptimized page, showing FCP starting after CSS loads.&quot; decoding=&quot;async&quot; height=&quot;352&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/WhpaDYb98Rf03JmuPenp.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/WhpaDYb98Rf03JmuPenp.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/WhpaDYb98Rf03JmuPenp.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/WhpaDYb98Rf03JmuPenp.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/WhpaDYb98Rf03JmuPenp.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/WhpaDYb98Rf03JmuPenp.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/WhpaDYb98Rf03JmuPenp.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/WhpaDYb98Rf03JmuPenp.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/WhpaDYb98Rf03JmuPenp.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/WhpaDYb98Rf03JmuPenp.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/WhpaDYb98Rf03JmuPenp.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/WhpaDYb98Rf03JmuPenp.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/WhpaDYb98Rf03JmuPenp.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/WhpaDYb98Rf03JmuPenp.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/WhpaDYb98Rf03JmuPenp.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/WhpaDYb98Rf03JmuPenp.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/WhpaDYb98Rf03JmuPenp.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/WhpaDYb98Rf03JmuPenp.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;This means that the browser needs to wait for all CSS to load and get processed
before painting a single pixel on the screen.&lt;/p&gt;
&lt;h2 id=&quot;optimize&quot;&gt;Optimize &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/defer-non-critical-css/#optimize&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To optimize this page, you need to know which classes are considered &amp;quot;critical&amp;quot;.
You&#39;ll use the &lt;a href=&quot;https://developer.chrome.com/docs/devtools/css/reference/#coverage&quot; rel=&quot;noopener&quot;&gt;Coverage Tool&lt;/a&gt; for that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In DevTools, open the &lt;a href=&quot;https://developer.chrome.com/docs/devtools/command-menu/&quot; rel=&quot;noopener&quot;&gt;Command Menu&lt;/a&gt;, by pressing &lt;code&gt;Control+Shift+P&lt;/code&gt; or &lt;code&gt;Command+Shift+P&lt;/code&gt; (Mac).&lt;/li&gt;
&lt;li&gt;Type &amp;quot;Coverage&amp;quot; and select &lt;strong&gt;Show Coverage&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Reload&lt;/strong&gt; button, to reload the page and start capturing the
coverage.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Coverage for CSS file, showing 55.9% unused bytes.&quot; decoding=&quot;async&quot; height=&quot;82&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/JTFK7wjhlTzd2cCfkpps.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/JTFK7wjhlTzd2cCfkpps.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/JTFK7wjhlTzd2cCfkpps.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/JTFK7wjhlTzd2cCfkpps.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/JTFK7wjhlTzd2cCfkpps.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/JTFK7wjhlTzd2cCfkpps.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/JTFK7wjhlTzd2cCfkpps.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/JTFK7wjhlTzd2cCfkpps.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/JTFK7wjhlTzd2cCfkpps.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/JTFK7wjhlTzd2cCfkpps.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/JTFK7wjhlTzd2cCfkpps.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/JTFK7wjhlTzd2cCfkpps.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/JTFK7wjhlTzd2cCfkpps.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/JTFK7wjhlTzd2cCfkpps.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/JTFK7wjhlTzd2cCfkpps.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/JTFK7wjhlTzd2cCfkpps.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/JTFK7wjhlTzd2cCfkpps.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/JTFK7wjhlTzd2cCfkpps.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Double-click on the report, to see classes marked in two colors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Green (&lt;strong&gt;critical&lt;/strong&gt;): These are the classes the browser needs to render the
visible content (like the title, subtitle, and accordion buttons).&lt;/li&gt;
&lt;li&gt;Red (&lt;strong&gt;non-critical&lt;/strong&gt;): These styles apply to content that&#39;s not immediately
visible (like the paragraphs inside the accordions).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With this information, optimize your CSS so that the browser starts processing
critical styles immediately after page loads, while deferring non-critical CSS
for later:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Extract the class definitions marked with green in the report obtained from
the coverage tool, and put those classes inside a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block at the head
of the page:&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;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;.accordion-btn&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&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; #ADD8E6&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #444&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pointer&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; 18px&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;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;text-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; left&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;outline&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;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 15px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.4s&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &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; 0 18px&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; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&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; white&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&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;span class=&quot;token property&quot;&gt;word-spacing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; blue&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&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; bold&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;text-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center&lt;span class=&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Then, load the rest of the classes asynchronously, by applying the following pattern:&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;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;styles.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;onload&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value javascript language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onload&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rel&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;stylesheet&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;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;noscript&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;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;styles.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;noscript&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This is not the standard way of loading CSS. Here&#39;s how it works:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;link rel=&amp;quot;preload&amp;quot; as=&amp;quot;style&amp;quot;&lt;/code&gt; requests the stylesheet asynchronously. You can learn more about &lt;code&gt;preload&lt;/code&gt; in the &lt;a href=&quot;https://web.dev/preload-critical-assets&quot;&gt;Preload critical assets guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;onload&lt;/code&gt; attribute in the &lt;code&gt;link&lt;/code&gt; allows the CSS to be processed when it finishes loading.&lt;/li&gt;
&lt;li&gt;&amp;quot;nulling&amp;quot; the &lt;code&gt;onload&lt;/code&gt; handler once it is used helps some browsers avoid re-calling the handler upon switching the rel attribute.&lt;/li&gt;
&lt;li&gt;The reference to the stylesheet inside of a &lt;code&gt;noscript&lt;/code&gt; element works as a fallback for browsers that don&#39;t execute JavaScript.&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;p&gt; In this guide, you used vanilla code to implement this optimization. In a real production scenario, it&#39;s a good practice to use functions like &lt;a href=&quot;https://github.com/filamentgroup/loadCSS/blob/master/README.md&quot;&gt;loadCSS&lt;/a&gt;, that can encapsulate this behavior and work well across browsers, and can support a &lt;a href=&quot;https://web.dev/csp/&quot;&gt;Content Security Policy&lt;/a&gt; that may not allow inline &lt;code&gt;onload&lt;/code&gt; JavaScript. &lt;/p&gt; &lt;p&gt; Alternatively, placing the CSS link at the bottom of the page, allows content above this to be rendered in Firefox and Chrome—though not in Safari—without waiting for the stylesheet. However, it still prioritizes that stylesheet, so it can still block other, more critical, content in all browsers. &lt;/p&gt; &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The &lt;a href=&quot;https://defer-css-optimized.glitch.me/&quot; rel=&quot;noopener&quot;&gt;resulting page&lt;/a&gt; looks exactly like the previous version, even when most styles load asynchronously. Here&#39;s how the inlined styles and asynchronous request to the CSS file look like in the HTML file:&lt;/p&gt;
&lt;!-- Copy and Paste Me --&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 420px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/defer-css-optimized?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=index.html&amp;previewSize=0&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;defer-css-optimized on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id=&quot;monitor&quot;&gt;Monitor &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/defer-non-critical-css/#monitor&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use DevTools to run another &lt;strong&gt;Performance&lt;/strong&gt; trace on the &lt;a href=&quot;https://defer-css-optimized.glitch.me/&quot; rel=&quot;noopener&quot;&gt;optimized
page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;FCP&lt;/strong&gt; marker appears before the page requests the CSS, which means the
browser doesn&#39;t need to wait for the CSS to load before rendering the page:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;DevTools performance trace for unoptimized page, showing FCP starting before CSS loads.&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/admin/0mVq3q760y37JSn2MmCP.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/0mVq3q760y37JSn2MmCP.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/0mVq3q760y37JSn2MmCP.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/0mVq3q760y37JSn2MmCP.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/0mVq3q760y37JSn2MmCP.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/0mVq3q760y37JSn2MmCP.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/0mVq3q760y37JSn2MmCP.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/0mVq3q760y37JSn2MmCP.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/0mVq3q760y37JSn2MmCP.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/0mVq3q760y37JSn2MmCP.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/0mVq3q760y37JSn2MmCP.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/0mVq3q760y37JSn2MmCP.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/0mVq3q760y37JSn2MmCP.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/0mVq3q760y37JSn2MmCP.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/0mVq3q760y37JSn2MmCP.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/0mVq3q760y37JSn2MmCP.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/0mVq3q760y37JSn2MmCP.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/0mVq3q760y37JSn2MmCP.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;As a final step, run Lighthouse on the optimized page.&lt;/p&gt;
&lt;p&gt;In the report you&#39;ll see that the FCP page has been reduced by &lt;strong&gt;0.2s&lt;/strong&gt; (a 20%
improvement!):&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Lighthouse report, showing an FCP value of &amp;#x27;0.8s&amp;#x27;.&quot; decoding=&quot;async&quot; height=&quot;324&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/oTDQFSlfQwS9SbqE0D0K.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/oTDQFSlfQwS9SbqE0D0K.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/oTDQFSlfQwS9SbqE0D0K.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/oTDQFSlfQwS9SbqE0D0K.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/oTDQFSlfQwS9SbqE0D0K.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/oTDQFSlfQwS9SbqE0D0K.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/oTDQFSlfQwS9SbqE0D0K.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/oTDQFSlfQwS9SbqE0D0K.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/oTDQFSlfQwS9SbqE0D0K.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/oTDQFSlfQwS9SbqE0D0K.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/oTDQFSlfQwS9SbqE0D0K.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/oTDQFSlfQwS9SbqE0D0K.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/oTDQFSlfQwS9SbqE0D0K.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/oTDQFSlfQwS9SbqE0D0K.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/oTDQFSlfQwS9SbqE0D0K.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/oTDQFSlfQwS9SbqE0D0K.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/oTDQFSlfQwS9SbqE0D0K.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/oTDQFSlfQwS9SbqE0D0K.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;The &lt;strong&gt;Eliminate render-blocking resources&lt;/strong&gt; suggestion is no longer under
&lt;strong&gt;Opportunities&lt;/strong&gt;, and now belongs to the &lt;strong&gt;Passed Audits&lt;/strong&gt; section:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Lighthouse report, showing &amp;#x27;Eliminate blocing resources&amp;#x27; on the &amp;#x27;Passed Audits&amp;#x27; section.&quot; decoding=&quot;async&quot; height=&quot;237&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/yDjEvZAcjPouC6I3I7qB.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/yDjEvZAcjPouC6I3I7qB.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/yDjEvZAcjPouC6I3I7qB.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/yDjEvZAcjPouC6I3I7qB.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/yDjEvZAcjPouC6I3I7qB.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/yDjEvZAcjPouC6I3I7qB.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/yDjEvZAcjPouC6I3I7qB.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/yDjEvZAcjPouC6I3I7qB.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/yDjEvZAcjPouC6I3I7qB.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/yDjEvZAcjPouC6I3I7qB.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/yDjEvZAcjPouC6I3I7qB.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/yDjEvZAcjPouC6I3I7qB.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/yDjEvZAcjPouC6I3I7qB.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/yDjEvZAcjPouC6I3I7qB.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/yDjEvZAcjPouC6I3I7qB.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/yDjEvZAcjPouC6I3I7qB.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/yDjEvZAcjPouC6I3I7qB.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/yDjEvZAcjPouC6I3I7qB.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;next-steps-and-references&quot;&gt;Next steps &amp;amp; references &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/defer-non-critical-css/#next-steps-and-references&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this  guide, you learned how to defer non-critical CSS by manually extracting the unused code in the page.
As a complement to this, the &lt;a href=&quot;https://web.dev/extract-critical-css/&quot;&gt;extract critical CSS guide&lt;/a&gt;
covers some of the most popular tools to extract critical CSS and includes
&lt;a href=&quot;https://web.dev/codelab-extract-and-inline-critical-css/&quot;&gt;a codelab&lt;/a&gt; to see how
they work in practice.&lt;/p&gt;
</content>
    <author>
      <name>Demian Renzulli</name>
    </author>
  </entry>
</feed>
