<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://web.dev/</id>
  <title>Martin Schierle on web.dev</title>
  <updated>2026-04-15T23:21:06Z</updated>
  <author>
    <name>Martin Schierle</name>
  </author>
  <link href="https://web.dev/authors/martinschierle/feed.xml" rel="self"/>
  <link href="https://web.dev/"/>
  <icon>https://web-dev.imgix.net/image/admin/DheRfImH6FxRNajMslIk.jpg?auto=format</icon>
  <logo>https://web.dev/images/shared/rss-banner.png</logo>
  <subtitle>Our latest news, updates, and stories by Martin Schierle.</subtitle>
  
  
  <entry>
    <title>How committing to Core Web Vitals increased Netzwelt&#39;s advertising revenues by 18%</title>
    <link href="https://web.dev/netzwelt/"/>
    <updated>2021-07-19T00:00:00Z</updated>
    <id>https://web.dev/netzwelt/</id>
    <content type="html" mode="escaped">&lt;p&gt;When Google announced the Core Web Vitals initiative,
German publisher &lt;a href=&quot;https://www.netzwelt.de/&quot; rel=&quot;noopener&quot;&gt;Netzwelt&lt;/a&gt; quickly realized the potential of these new metrics
towards a great page experience and improved advertising-based monetization.
They went on a journey to relaunch their website,
applying common performance best practices while optimizing ad tags and placements in parallel.
Committing to great UX and fast pages proved to be a path
for optimizing engagement and ad revenues hand in hand,
with page views up by 27%, ad viewability over 75%, and advertising revenues improving by 18%.&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;27&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;Increase in page views&lt;/p&gt;
  &lt;/div&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;18&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;Increase in ad revenue&lt;/p&gt;
  &lt;/div&gt;
  &lt;div class=&quot;stats__item&quot;&gt;
    &lt;p class=&quot;stats__figure&quot;&gt;75&lt;sub&gt;%&lt;/sub&gt;&lt;/p&gt;
    &lt;p&gt;Ad viewability&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&quot;the-challenge&quot;&gt;The challenge &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/netzwelt/#the-challenge&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Like many other news publishers,
Netzwelt struggled to find the right balance between optimizing user experience and page speed
while maintaining high ad revenues.
The main challenges they encountered were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;High &lt;a href=&quot;https://web.dev/cls&quot;&gt;Cumulative Layout Shift&lt;/a&gt; (CLS) due to layout shifts triggered by ads,
especially from multisize slots and top banners.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/lcp&quot;&gt;Largest Contentful Paint&lt;/a&gt; (LCP) coming in late due to ads being the largest paint
or by taking bandwidth from hero image loading.&lt;/li&gt;
&lt;li&gt;High &lt;a href=&quot;https://web.dev/fid&quot;&gt;First Input Delay&lt;/a&gt; (FID) caused by third-party JavaScript needed for advertising,
header bidding, and other purposes.&lt;/li&gt;
&lt;li&gt;Side effects on Core Web Vitals from consent dialogs controlled by third-party vendors,
which also added to layout shifts and might be detected as late largest paints.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;optimizing-a-news-website-for-core-web-vitals&quot;&gt;Optimizing a news website for Core Web Vitals &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/netzwelt/#optimizing-a-news-website-for-core-web-vitals&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When Netzwelt started working on core web vitals,
they quickly noticed that optimizing Core Web Vitals doesn&#39;t need to affect advertisement negatively
but can be used as an opportunity to improve ad viewability.
Therefore, the Netzwelt team optimized Core Web Vitals to lift ad viewability
above 75% to attract higher-value advertising
(the global average for display ads
&lt;a href=&quot;https://www.thinkwithgoogle.com/feature/viewability/state-of-viewability&quot; rel=&quot;noopener&quot;&gt;is less than 50%&lt;/a&gt;).&lt;/p&gt;
&lt;h3 id=&quot;optimizing-cls&quot;&gt;Optimizing CLS &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/netzwelt/#optimizing-cls&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Advertisements often load late (sometimes consciously through lazy loading),
and their real size is often not known in advance due to multisize and fluid ad placements.&lt;/p&gt;
&lt;p&gt;The problem can be divided into two—ads above and below the fold.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ads above the fold&lt;/strong&gt; can cause massive layout jumps due to loading late with unknown sizes.
Blocking the largest space they might need can lead to bad UX,
while asking advertisers for fixed sizes may reduce ad income.
Netzwelt solved this problem by making the top ad sticky and removing some of the bigger allowed banner sizes.
The overlaid ad avoids triggering layout jumps for ads of different sizes.
Although the reduced eligible sizes do impact ad sales, the better viewability offsets this easily.&lt;/p&gt;
&lt;p&gt;Historical data and A/B tests helped Netzwelt find the right size
and positioning for different devices and screen sizes,
and CSS media rules used to reserve space.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ads below the fold&lt;/strong&gt; offer room for significant improvement:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A banner that is not seen but loaded creates poor ad viewability and worsens the page experience.&lt;/li&gt;
&lt;li&gt;A banner that is loaded at the time a user scrolls over it can introduce additional content jumps.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To maintain a good page experience and high ad viewability,
Netzwelt implemented lazy loading and reserved space for the size of the lowest common denominator.
In a multisize zone the banners requested in the sizes 300x250 to 300x600, reserved 250px in height.
This eliminated layout shifts for the smaller sizes; and reduced them massively for larger banners.
This approach is not optimal, but it is a start and a good compromise.&lt;/p&gt;
&lt;p&gt;To optimize further,
Netzwelt used
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Intersection_Observer_API&quot; rel=&quot;noopener&quot;&gt;Intersection Observer&lt;/a&gt; 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 control ad placements and reduce layout shifts.
Different ad positions and lazy loading strategies are used depending on
scroll position and network connection quality,
and ads may be changed from multiple to fixed sizes.
The aim of the algorithm is always to maximize ad viewability while minimizing layout shifts.
Browsers not supporting the NetworkInfo API use a proxy value based on a
combination of device and IP derived network type.
This
&lt;a href=&quot;https://addyosmani.com/blog/adaptive-loading/&quot; rel=&quot;noopener&quot;&gt;adaptive loading&lt;/a&gt;
strategy especially reduces CLS for users with slow internet connections.&lt;/p&gt;
&lt;h3 id=&quot;optimizing-fid&quot;&gt;Optimizing FID &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/netzwelt/#optimizing-fid&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;First Input Delay might appear to be a problem for news publishers,
as advertisement often comes with many additional JavaScript libraries.
And there does seem to be a negative impact when looking at lab data like Lighthouse.
However, looking at field data in the
&lt;a href=&quot;https://almanac.httparchive.org/en/2020/performance#fid-by-device&quot; rel=&quot;noopener&quot;&gt;2020 Web Almanac&lt;/a&gt;,
many websites have sufficiently fast response.
Part of this is due to advertising JavaScript loading late (after first input),
caching well (for example getting the treatment of
&lt;a href=&quot;https://v8.dev/blog/code-caching-for-devs&quot; rel=&quot;noopener&quot;&gt;v8 code caching&lt;/a&gt;),
or being optimized well by the ad vendors.
Vendor viewability trackers make sure to avoid
long tasks—so even when the sum of the runtime is long
&lt;a href=&quot;https://web.dev/tbt/&quot;&gt;Total Blocking Time&lt;/a&gt; (TBT) and FID are not affected.
While FID was not a huge problem for Netzwelt, there were still some optimizations to make:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reducing ad scripts and providers, concentrating on a single stack if possible.&lt;/li&gt;
&lt;li&gt;Loading all scripts with defer or async.&lt;/li&gt;
&lt;li&gt;Using webpack or similar technologies for treeshaking and unbundling.&lt;/li&gt;
&lt;li&gt;Using simple BEM-like CSS rules.&lt;/li&gt;
&lt;li&gt;Avoiding long-running tasks, and using the
&lt;a href=&quot;https://philipwalton.com/articles/idle-until-urgent/&quot; rel=&quot;noopener&quot;&gt;idle-until-urgent&lt;/a&gt; pattern.&lt;/li&gt;
&lt;li&gt;Working with
&lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/rendering/optimize-javascript-execution#use_requestanimationframe_for_visual_changes&quot; rel=&quot;noopener&quot;&gt;RequestAnimationFrame&lt;/a&gt;
wherever layout is affected.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;optimizing-lcp&quot;&gt;Optimizing LCP &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/netzwelt/#optimizing-lcp&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Largest Contentful Paint can be influenced by advertisements in
two ways—explicitly by recognizing an ad as the largest paint,
and indirectly by congesting network bandwidth or affecting critical path so that the actual largest paint
(for example a hero image) can&#39;t load in fast enough.&lt;/p&gt;
&lt;p&gt;Netzwelt had already removed medium rectangle ads from the top ad slots while optimizing for CLS.
This had the additional benefit of ads not becoming the largest element.&lt;/p&gt;
&lt;p&gt;Netzwelt follows a strict policy to prioritize content above ads.
Netzwelt prioritized the hero images and fonts used above the fold
and optimized the critical path to take precedence over advertising scripts and adverts.
This prioritization helped LCP to be unaffected by the ads.&lt;/p&gt;
&lt;p&gt;Besides these optimizations, Netzwelt followed other best practices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Separated CSS for critical rendering path and put it in the header.&lt;/li&gt;
&lt;li&gt;Preloaded the most important fonts, scripts, and images.&lt;/li&gt;
&lt;li&gt;Avoided lazy loading images above the fold.&lt;/li&gt;
&lt;li&gt;Used &lt;code&gt;font-display=&amp;quot;swap&amp;quot;&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;gdpr-consent-and-core-web-vitals&quot;&gt;GDPR Consent and Core Web Vitals &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/netzwelt/#gdpr-consent-and-core-web-vitals&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Consent dialogs often negatively impact Core Web Vitals.
As with advertisements, there are two ways in which a consent dialog can influence CWV:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Explicitly if it is detected as the largest paint, or causes layout shifts.&lt;/li&gt;
&lt;li&gt;Implicitly, by stealing bandwidth from the actual largest paint,
blocking the critical path to the largest paint,
or delaying ads until consent is in, which can, in turn, trigger layout shifts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Netzwelt works with a third-party consent provider,
which also implemented optimizations.
First Netzwelt made sure to avoid the straightforward pitfalls:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Consent scripts are loaded async, so as not to block critical resources.&lt;/li&gt;
&lt;li&gt;Consent is formatted so that large elements are not eligible as the largest element within LCP.&lt;/li&gt;
&lt;li&gt;Consent overlay uses &lt;code&gt;position: fixed&lt;/code&gt; to avoid shifts.&lt;/li&gt;
&lt;li&gt;While ads are shown only after consent is given,
adequate whitespace is preserved beforehand to avoid layout shifts when ads load in.&lt;/li&gt;
&lt;li&gt;Making sure the display and positioning of the consent dialog does not trigger layout shifts.
One problem found and fixed here was the scroll-lock option of the service provider,
which disabled scrolling while consent shows by moving the main content of the article on scroll,
causing layout shifts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After this work, there were still large discrepancies between field and lab CLS.
While lab CLS was close to zero, field values were significantly above thresholds.
After investigation, the culprit was layout shifts within the consent iframe,
which currently are only correctly captured in field data.
Netzwelt continues to work with the third-party consent provider to improve this issue.&lt;/p&gt;
&lt;h3 id=&quot;news-subscription-models-and-core-web-vitals&quot;&gt;News Subscription Models and Core Web Vitals &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/netzwelt/#news-subscription-models-and-core-web-vitals&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;News publishers are moving to subscription models to fund journalism.
This model is relevant for Core Web Vitals as people will not subscribe to websites with poor user experience.
In addition, subscribers do not expect to see ads in the paid content,
but hiding ads may cause layout shifts.
The website needs to check if users are entitled to view the content and whether to display ads.
These checks may cause additional latencies, leading to content shifts or a poor user experience.&lt;/p&gt;
&lt;p&gt;Netzwelt is using a model where content is always free,
but subscribers will not see ads.
They investigated different ways to query and store entitlements to reduce latencies and layout shifts.&lt;/p&gt;
&lt;p&gt;An initial query of entitlement always caused latencies,
and therefore if querying the entitlements takes too long, the site will display the last cached state.
For new subscribers, this means a small risk of a paying user seeing ads once.
Users just ending a subscription might see one load without ads before the locally stored entitlements update.
Once entitlements are known, they are stored locally using the LocalStorage API,
to avoid additional latencies and layout shifts during future navigation.&lt;/p&gt;
&lt;h2 id=&quot;optimization-results&quot;&gt;Optimization Results &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/netzwelt/#optimization-results&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The results of Netzwelt&#39;s optimizations speak for themselves.
Their PageSpeed Insights score is unrivaled within news publishers worldwide:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Screenshot of PageSpeed Insights for the Netzwelt.de site, showing a score of 100.&quot; decoding=&quot;async&quot; height=&quot;622&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/ErZNqWVYCAkOmEu1r7pd.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/ErZNqWVYCAkOmEu1r7pd.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/ErZNqWVYCAkOmEu1r7pd.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/ErZNqWVYCAkOmEu1r7pd.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/ErZNqWVYCAkOmEu1r7pd.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/ErZNqWVYCAkOmEu1r7pd.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/ErZNqWVYCAkOmEu1r7pd.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/ErZNqWVYCAkOmEu1r7pd.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/ErZNqWVYCAkOmEu1r7pd.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/ErZNqWVYCAkOmEu1r7pd.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/ErZNqWVYCAkOmEu1r7pd.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/ErZNqWVYCAkOmEu1r7pd.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/ErZNqWVYCAkOmEu1r7pd.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/ErZNqWVYCAkOmEu1r7pd.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/ErZNqWVYCAkOmEu1r7pd.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/ErZNqWVYCAkOmEu1r7pd.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/ErZNqWVYCAkOmEu1r7pd.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/ErZNqWVYCAkOmEu1r7pd.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;The optimizations improved the page experience
but were made with the business in mind and enhanced ad experience,
ad viewability, and revenue.
After relaunching the optimized page,
Netzwelt saw CPM increases of 20-30%,
with an ad viewability above 75%.
However, Netzwelt assumes the overall revenue uplift to be even higher.
Traffic increased since the relaunch, probably driven by higher user engagement
and lower bounce rates due to improved UX.
These effects are hard to quantify and set in causal relation to the relaunch,
as traffic naturally fluctuates, and there are also effects from the global pandemic.
These indirect effects are one of the reasons why Netzwelt always looks at all numbers when reviewing their site,
not just CPM, which may be misleading. Core Web Vitals and UX metrics,
in combination with all ad-related metrics, provide the real picture.&lt;/p&gt;
&lt;h2 id=&quot;looking-into-the-future&quot;&gt;Looking into the future &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/netzwelt/#looking-into-the-future&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Netzwelt believes that in a future without third-party cookies, contextual targeting via the content,
combined with good UX and page experience (including ad viewability), is the key to success as a news publisher.&lt;/p&gt;
&lt;p&gt;Therefore Netzwelt doesn&#39;t stop with Core Web Vitals
but goes the extra mile by implementing many modern web capabilities such as Progressive Web Apps (PWA),
offline content, dark mode, the Web Share API, and Trusted Web Activities (TWA) using the new
&lt;a href=&quot;https://developer.chrome.com/docs/android/trusted-web-activity/receive-payments-play-billing/&quot; rel=&quot;noopener&quot;&gt;Digital Goods API&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Martin Schierle</name>
    </author>
  </entry>
  
  <entry>
    <title>Measuring offline usage</title>
    <link href="https://web.dev/measuring-offline-usage/"/>
    <updated>2020-10-28T00:00:00Z</updated>
    <id>https://web.dev/measuring-offline-usage/</id>
    <content type="html" mode="escaped">&lt;p&gt;This article shows you how to track offline usage of your site to help you make a case for why your
site needs a better offline mode. It also explains pitfalls and problems to avoid when implementing
offline usage analytics.&lt;/p&gt;
&lt;h2 id=&quot;the-pitfalls-of-the-online-and-offline-browser-events&quot;&gt;The pitfalls of the online and offline browser events &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/measuring-offline-usage/#the-pitfalls-of-the-online-and-offline-browser-events&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The obvious solution for tracking offline usage is to create event listeners for the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Window/online_event&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;online&lt;/code&gt;&lt;/a&gt; and
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Window/offline_event&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;offline&lt;/code&gt;&lt;/a&gt; events (which
&lt;a href=&quot;https://caniuse.com/#feat=online-status&quot; rel=&quot;noopener&quot;&gt;many browsers support&lt;/a&gt;) and to put your analytics tracking
logic in those listeners. Unfortunately, there are several problems and limitations with this
approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In general tracking every network connection status event might be excessive, and is
counter-productive in a privacy-centric world where as little data as possible should be
collected. Additionally the &lt;code&gt;online&lt;/code&gt; and &lt;code&gt;offline&lt;/code&gt; events can fire for just a split second of
network loss, which a user probably wouldn&#39;t even see or notice.&lt;/li&gt;
&lt;li&gt;The analytics tracking of offline activity would never reach the analytics server because
the user is… well, offline.&lt;/li&gt;
&lt;li&gt;Tracking a timestamp locally when a user goes offline and sending the offline activity to
the analytics server when the user goes back online depends on the user revisiting your site.
If the user drops off your site due to a lack of an offline mode and never revisits, you have
no way to track that. The ability to track offline drop-offs is critical data for building a
case about why your site needs a better offline mode.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;online&lt;/code&gt; event is not very reliable as it
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/NavigatorOnLine/onLine&quot; rel=&quot;noopener&quot;&gt;only knows about network access&lt;/a&gt;,
not internet access. Therefore a user might still be offline, and sending the tracking ping can
still fail.&lt;/li&gt;
&lt;li&gt;Even if the user still stays on the current page while being offline, none of the other
analytics events (e.g. scroll events, clicks, etc.) are tracked either, which might be the more
relevant and useful information.&lt;/li&gt;
&lt;li&gt;Being offline in itself is also not too meaningful in general. As a website developer it may
be more important to know what kinds of resources failed to load. This is especially relevant
in the context of SPAs, where a dropped network connection might not lead to a browser offline
error page (which users understand) but more likely to random dynamic parts of the page failing
silently.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can still use this solution to gain a basic understanding of offline usage, but the many
drawbacks and limitations need to be considered carefully.&lt;/p&gt;
&lt;h2 id=&quot;a-better-approach-the-service-worker&quot;&gt;A better approach: the service worker &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/measuring-offline-usage/#a-better-approach-the-service-worker&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The solution that enables offline mode turns out to be the better solution for tracking offline
usage.  The basic idea is to store analytics pings into IndexedDB as long as the user is offline,
and just resend them when the user goes online again. For Google Analytics this is already available
&lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-google-analytics/&quot; rel=&quot;noopener&quot;&gt;off-the-shelf through a Workbox module&lt;/a&gt;,
but keep in mind that hits sent more than
&lt;a href=&quot;https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#qt&quot; rel=&quot;noopener&quot;&gt;four hours deferred&lt;/a&gt;
may not be processed. In its simplest form, it can be activated within a Workbox-based service
worker with these two lines:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; googleAnalytics &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;workbox-google-analytics&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;googleAnalytics&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&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 tracks all existing events and pageview pings while being offline, but you wouldn&#39;t know that
they happened offline (as they are just replayed as-is). For this
&lt;a href=&quot;https://developer.chrome.com/docs/workbox/modules/workbox-google-analytics/#using-a-custom-dimension-to-track-online-vs-offline-interactions&quot; rel=&quot;noopener&quot;&gt;you can manipulate tracking requests with Workbox&lt;/a&gt;
by adding an &lt;code&gt;offline&lt;/code&gt; flag to the analytics ping, using a custom dimension (&lt;code&gt;cd1&lt;/code&gt; in the code
sample below):&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; googleAnalytics &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;workbox-google-analytics&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;googleAnalytics&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;token punctuation&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;parameterOverrides&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;cd1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;offline&#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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;What if the user drops out of the page due to being offline, before an internet connection comes
back? Even though this normally puts the service worker to sleep (because it&#39;s unable to send the data
when the connection comes back), the Workbox Google Analytics module uses 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 sends the analytics
data later when the connection comes back, even if the user closes the tab or browser.&lt;/p&gt;
&lt;p&gt;There is still a drawback: while this makes existing tracking offline-capable, you would most likely
not see much relevant data coming in until you implement a basic offline mode. Users would still
drop off your site quickly when the connection breaks away. But now you can at least measure and
quantify this, by comparing average session length and user engagement for users with the offline
dimension applied versus your regular users.&lt;/p&gt;
&lt;h2 id=&quot;spas-and-lazy-loading&quot;&gt;SPAs and lazy loading &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/measuring-offline-usage/#spas-and-lazy-loading&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If users visiting a page built as a multi-page website go offline and try to navigate, the browser&#39;s
default offline page shows up, helping users understand what is happening. However, pages built as
single-page applications work differently. The user stays on the same page, and new content is
loaded dynamically through AJAX without any browser navigation. Users do not see the browser error
page when going offline. Instead, the dynamic parts of the page render with errors, go into
undefined states, or just stop being dynamic.&lt;/p&gt;
&lt;p&gt;Similar effects can happen within multi-page websites due to lazy loading. For example, maybe the
initial load happened online, but the user went offline before scrolling. All lazy loaded content
below the fold will silently fail and be missing.&lt;/p&gt;
&lt;p&gt;As these cases are really irritating to users, it makes sense to track them. Service workers are the
perfect spot to catch network errors, and eventually track them using analytics. With Workbox, a
&lt;a href=&quot;https://developer.chrome.com/docs/workbox/managing-fallback-responses/#comprehensive-fallbacks&quot; rel=&quot;noopener&quot;&gt;global catch handler&lt;/a&gt;
can be configured to inform the page about failed requests by sending a message event:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; setCatchHandler &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;br /&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 comment&quot;&gt;// https://developer.mozilla.org/docs/Web/API/Client/postMessage&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 keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Exit early if we don&#39;t have access to the client.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Eg, if it&#39;s cross-origin.&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;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clientId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Get the client.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; clients&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;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clientId&lt;span class=&quot;token punctuation&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;// Exit early if we don&#39;t get the client.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Eg, if it closed.&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;client&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Send a message to the client.&lt;/span&gt;&lt;br /&gt;    client&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;action&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;network_fail&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;url&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;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;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; 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;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span 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; 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;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&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;Rather than listening to all failed requests, another way is to catch errors on specific routes
only. As an example, if we want to report errors happening on routes to &lt;code&gt;/products/*&lt;/code&gt; only, we can
add a check in &lt;code&gt;setCatchHandler&lt;/code&gt; which filters the URI with a regular expression.
A cleaner solution is to implement registerRoute with a custom handler. This encapsulates the
business logic into a separate route, with better maintainability in more complex service workers:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 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;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; networkOnly &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;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;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;https:\/\/example\.com\/products\/.+&#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;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;params&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;try&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;// Attempt a network request.&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; networkOnly&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;params&lt;span class=&quot;token punctuation&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 comment&quot;&gt;// If it fails, report the error.&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; event &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clientId&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;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; clients&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;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clientId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;client&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;      client&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;action&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;network_fail&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;url&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;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;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;products&quot;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&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;br /&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 a final step, the page needs to listen to the &lt;code&gt;message&lt;/code&gt; event, and send out the analytics ping.
Again, make sure to buffer analytics requests that happen offline within the service worker. As
described before, initialize the &lt;code&gt;workbox-google-analytics&lt;/code&gt; plugin for built-in Google Analytics
support.&lt;/p&gt;
&lt;p&gt;The following example uses Google Analytics, but can be applied in the same way for other analytics
vendors.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;serviceWorker&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// ... SW registration here&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// track offline error events&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&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;message&quot;&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 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;gtag &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 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;action &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;network_fail&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token function&quot;&gt;gtag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;event&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;network_fail&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;event_category&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;destination&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token comment&quot;&gt;// event_label: event.data.url,&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token comment&quot;&gt;// value: event.data.value&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This will track failed resource loads in Google Analytics, where they can be analyzed with
&lt;a href=&quot;https://support.google.com/analytics/answer/1033068?hl=en&quot; rel=&quot;noopener&quot;&gt;reporting&lt;/a&gt;. The derived insight can be
used to improve service worker caching and error handling in general, to make the page more robust
and reliable under unstable network conditions.&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/measuring-offline-usage/#next-steps&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This article showed different ways of tracking offline usage with their advantages and shortcomings.
While this can help to quantify how many of your users go offline and run into problems due to it,
it&#39;s still just a start. As long as your website does not offer a well-built offline mode, you
obviously won&#39;t see much offline usage in analytics.&lt;/p&gt;
&lt;p&gt;We recommend to get the full tracking in place, and then extend your offline capabilities in
iterations with an eye on tracking numbers. Start with a simple offline error page first–with
&lt;a href=&quot;https://developer.chrome.com/docs/workbox/managing-fallback-responses/#offline-page-only&quot; rel=&quot;noopener&quot;&gt;Workbox it&#39;s trivial to
do&lt;/a&gt;–and
should be considered a UX best practice similar to custom 404 pages anyway. Then work your way
&lt;a href=&quot;https://developer.chrome.com/docs/workbox/managing-fallback-responses/#comprehensive-fallbacks&quot; rel=&quot;noopener&quot;&gt;towards more advanced offline fallbacks&lt;/a&gt;
and finally towards real offline content. Make sure you advertise and explain this to your users
well, and you will see increasing usage. After all, everyone goes offline every once in a while.&lt;/p&gt;
&lt;p&gt;Check out &lt;a href=&quot;https://web.dev/how-to-report-metrics/&quot;&gt;How to report metrics and build a performance culture&lt;/a&gt;
and &lt;a href=&quot;https://web.dev/fixing-website-speed-cross-functionally/&quot;&gt;Fixing website speed cross-functionally&lt;/a&gt; for tips
on persuading cross-functional stakeholders to invest more in your website. Although those posts
are focused on performance, they should help you get general ideas about how to engage
stakeholders.&lt;/p&gt;
&lt;p&gt;Hero photo by &lt;a href=&quot;https://unsplash.com/@jcgellidon?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot; rel=&quot;noopener&quot;&gt;JC Gellidon&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/subway-people?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Stephan Giesau</name>
    </author><author>
      <name>Martin Schierle</name>
    </author>
  </entry>
  
  <entry>
    <title>How ZDF created a video PWA with offline and dark mode</title>
    <link href="https://web.dev/zdf/"/>
    <updated>2020-10-07T00:00:00Z</updated>
    <id>https://web.dev/zdf/</id>
    <content type="html" mode="escaped">&lt;p&gt;When broadcaster ZDF was considering redesigning their frontend technology
stack, they decided to take a closer look at &lt;a href=&quot;https://web.dev/pwa/&quot;&gt;Progressive Web Apps&lt;/a&gt; for their
streaming site &lt;a href=&quot;https://pwa.zdf.de/&quot; rel=&quot;noopener&quot;&gt;ZDFmediathek&lt;/a&gt;. Development agency
&lt;a href=&quot;https://www.cellular.de/&quot; rel=&quot;noopener&quot;&gt;Cellular&lt;/a&gt; took on the challenge to build a web-based
experience that is on par with ZDF&#39;s platform-specific iOS and Android apps. The
PWA offers installability, offline video playback, transition animations, and a
dark mode.&lt;/p&gt;
&lt;h2 id=&quot;adding-a-service-worker&quot;&gt;Adding a service worker &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/zdf/#adding-a-service-worker&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A key feature of a PWA is offline support. For ZDF most of the heavy lifting is done by
&lt;a href=&quot;https://web.dev/workbox/&quot;&gt;Workbox&lt;/a&gt;, a set of libraries
and Node modules that make it easy to support different caching strategies. The
ZDF PWA is built with TypeScript and React, so it uses the Workbox library
already built into
&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; to
precache static assets. This lets the application focus on making the dynamic
content available offline, in this case the videos and their metadata.&lt;/p&gt;
&lt;p&gt;The basic idea is quite simple: fetch the video and store it as a blob in
IndexedDB. Then during playback, listen for online/offline events, and switch to
the downloaded version when the device goes offline.&lt;/p&gt;
&lt;p&gt;Unfortunately things turned out to be a little more complex. One of the project
requirements was to use the official ZDF web player which doesn&#39;t provide any
offline support. The player takes a content ID as input, talks to the ZDF API,
and plays back the associated video.&lt;/p&gt;
&lt;p&gt;This is where one of the web&#39;s most powerful features comes to the rescue:
&lt;a href=&quot;https://web.dev/service-worker-mindset/&quot;&gt;service workers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The service worker can intercept the various requests done by the player and
respond with the data from IndexedDB. This transparently adds offline
capabilities without having to change a single line of the player&#39;s code.&lt;/p&gt;
&lt;p&gt;Since offline videos tend to be quite large, a big question is how many of them
can actually be stored on a device. With the help of the &lt;a href=&quot;https://web.dev/storage-for-the-web/#how-much&quot;&gt;StorageManager
API&lt;/a&gt; the app can estimate the
available space and inform the user when there is insufficient space before even
starting the download. Unfortunately Safari isn&#39;t on the list of browsers
implementing this API and at the time of writing there wasn&#39;t much up-to-date
information about how other browsers applied quotas. Therefore, the team wrote a
&lt;a href=&quot;https://cellular.github.io/quota&quot; rel=&quot;noopener&quot;&gt;small utility&lt;/a&gt; to test the behavior on
various devices. By now a &lt;a href=&quot;https://web.dev/storage-for-the-web/&quot;&gt;comprehensive
article&lt;/a&gt; exists that sums up all the
details.&lt;/p&gt;
&lt;h2 id=&quot;adding-a-custom-install-prompt&quot;&gt;Adding a custom install prompt &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/zdf/#adding-a-custom-install-prompt&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The ZDF PWA offers a custom in-app installation flow and prompts users to
install the app as soon as they want to download their first video. This is a
good point in time to prompt for install because the user has expressed a clear intention to
use the app offline.&lt;/p&gt;
&lt;figure&gt;
  &lt;div class=&quot;switcher&quot;&gt;
    &lt;img alt=&quot;Custom invitation to install.&quot; decoding=&quot;async&quot; height=&quot;1595&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sj4J2JMlYdgf4BrhaRsT.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sj4J2JMlYdgf4BrhaRsT.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sj4J2JMlYdgf4BrhaRsT.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sj4J2JMlYdgf4BrhaRsT.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sj4J2JMlYdgf4BrhaRsT.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sj4J2JMlYdgf4BrhaRsT.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sj4J2JMlYdgf4BrhaRsT.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sj4J2JMlYdgf4BrhaRsT.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sj4J2JMlYdgf4BrhaRsT.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sj4J2JMlYdgf4BrhaRsT.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sj4J2JMlYdgf4BrhaRsT.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sj4J2JMlYdgf4BrhaRsT.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sj4J2JMlYdgf4BrhaRsT.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sj4J2JMlYdgf4BrhaRsT.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sj4J2JMlYdgf4BrhaRsT.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sj4J2JMlYdgf4BrhaRsT.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sj4J2JMlYdgf4BrhaRsT.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/sj4J2JMlYdgf4BrhaRsT.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
    &lt;img alt=&quot;Custom install prompt being triggered when downloading a video for offline consumption.&quot; decoding=&quot;async&quot; height=&quot;1595&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FT4Xt5xpjCp57C8BwLtn.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FT4Xt5xpjCp57C8BwLtn.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FT4Xt5xpjCp57C8BwLtn.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FT4Xt5xpjCp57C8BwLtn.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FT4Xt5xpjCp57C8BwLtn.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FT4Xt5xpjCp57C8BwLtn.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FT4Xt5xpjCp57C8BwLtn.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FT4Xt5xpjCp57C8BwLtn.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FT4Xt5xpjCp57C8BwLtn.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FT4Xt5xpjCp57C8BwLtn.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FT4Xt5xpjCp57C8BwLtn.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FT4Xt5xpjCp57C8BwLtn.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FT4Xt5xpjCp57C8BwLtn.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FT4Xt5xpjCp57C8BwLtn.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FT4Xt5xpjCp57C8BwLtn.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FT4Xt5xpjCp57C8BwLtn.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FT4Xt5xpjCp57C8BwLtn.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FT4Xt5xpjCp57C8BwLtn.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;/div&gt;
  &lt;figcaption&gt;Custom install prompt being triggered when downloading a video for offline consumption.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;building-an-offline-page-to-access-downloads&quot;&gt;Building an offline page to access downloads &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/zdf/#building-an-offline-page-to-access-downloads&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When the device is not connected to the internet and the user navigates to a
page that is not available in offline mode, a special page is shown instead that
lists all videos that have previously been downloaded or (in case no content has
been downloaded yet) a short explanation of the offline feature.&lt;/p&gt;
&lt;figure&gt;
  &lt;div class=&quot;switcher&quot;&gt;
    &lt;img alt=&quot;Offline page showing all content available for watching offline.&quot; decoding=&quot;async&quot; height=&quot;1418&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FcWDhtuSSpHg04krFqUD.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FcWDhtuSSpHg04krFqUD.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FcWDhtuSSpHg04krFqUD.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FcWDhtuSSpHg04krFqUD.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FcWDhtuSSpHg04krFqUD.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FcWDhtuSSpHg04krFqUD.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FcWDhtuSSpHg04krFqUD.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FcWDhtuSSpHg04krFqUD.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FcWDhtuSSpHg04krFqUD.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FcWDhtuSSpHg04krFqUD.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FcWDhtuSSpHg04krFqUD.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FcWDhtuSSpHg04krFqUD.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FcWDhtuSSpHg04krFqUD.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FcWDhtuSSpHg04krFqUD.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FcWDhtuSSpHg04krFqUD.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FcWDhtuSSpHg04krFqUD.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FcWDhtuSSpHg04krFqUD.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FcWDhtuSSpHg04krFqUD.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
    &lt;img alt=&quot;Offline page showing that no content is available for watching offline.&quot; decoding=&quot;async&quot; height=&quot;1423&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PUvFyyaVfhh7PFyXDwCo.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PUvFyyaVfhh7PFyXDwCo.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PUvFyyaVfhh7PFyXDwCo.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PUvFyyaVfhh7PFyXDwCo.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PUvFyyaVfhh7PFyXDwCo.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PUvFyyaVfhh7PFyXDwCo.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PUvFyyaVfhh7PFyXDwCo.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PUvFyyaVfhh7PFyXDwCo.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PUvFyyaVfhh7PFyXDwCo.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PUvFyyaVfhh7PFyXDwCo.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PUvFyyaVfhh7PFyXDwCo.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PUvFyyaVfhh7PFyXDwCo.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PUvFyyaVfhh7PFyXDwCo.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PUvFyyaVfhh7PFyXDwCo.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PUvFyyaVfhh7PFyXDwCo.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PUvFyyaVfhh7PFyXDwCo.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PUvFyyaVfhh7PFyXDwCo.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/PUvFyyaVfhh7PFyXDwCo.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;/div&gt;
  &lt;figcaption&gt;Offline page showing all content available for watching offline.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;using-frame-loading-rate-for-adaptive-features&quot;&gt;Using frame loading rate for adaptive features &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/zdf/#using-frame-loading-rate-for-adaptive-features&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To offer a rich user experience the ZDF PWA includes some subtle transitions
that happen when the user scrolls or navigates. On low-end devices such
animations usually have the opposite effect and make the app feel sluggish and
less responsive if they don&#39;t run at 60 frames per second. To take this into
account the app measures the actual frame rate via &lt;code&gt;requestAnimationFrame()&lt;/code&gt; while
the application loads and disables all animations when the value drops below a
certain threshold.&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; frameRate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lastTick&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; samples &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;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;measure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&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; tick &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token 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;lastTick&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; samples&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;tick &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; lastTick&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    lastTick &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tick&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;samples&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;requestAnimationFrame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;measure&lt;span class=&quot;token punctuation&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;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; avg &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; samples&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&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;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&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; a &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; samples&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&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; fps &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; avg&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fps&lt;span class=&quot;token punctuation&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 function&quot;&gt;measure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&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;Even if this measurement provides only a rough indication of the device&#39;s
performance and varies on each load, it was still a good basis for
decision-making. It&#39;s worth mentioning that depending on the use case there are
&lt;a href=&quot;https://web.dev/adaptive-loading-with-service-workers/&quot;&gt;other techniques for adaptive loading&lt;/a&gt;
that developers can implement. One great advantage of this approach is that it
is available on all platforms.&lt;/p&gt;
&lt;h2 id=&quot;dark-mode&quot;&gt;Dark mode &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/zdf/#dark-mode&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A popular feature for modern mobile experiences is
&lt;a href=&quot;https://web.dev/prefers-color-scheme/&quot;&gt;dark mode&lt;/a&gt;.
Especially when
watching videos in low ambient light many people prefer a dimmed UI. The ZDF PWA
not only provides a switch that allows users to toggle between a light and a
dark theme, it also reacts to changes of the OS-wide color preferences. This way
the app will automatically change its appearance on devices that have set up a
schedule to change the theme base on the time of day.&lt;/p&gt;
&lt;h2 id=&quot;results&quot;&gt;Results &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/zdf/#results&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The new progressive web app was silently launched as a public beta in March 2020
and has received a lot of positive feedback since then. While the beta phase
continues, the PWA still runs under its own temporary domain. Even though the
PWA wasn&#39;t publicly promoted there is a steadily growing number of users. Many
of these are from the Microsoft Store which allows Windows 10 users to discover
PWAs and install them like platform-specific apps.&lt;/p&gt;
&lt;h2 id=&quot;whats-next&quot;&gt;What&#39;s next? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/zdf/#whats-next&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;ZDF plans to continue adding features to their PWA, including login for
personalization, cross-device and platform viewing, and push notifications.&lt;/p&gt;
</content>
    <author>
      <name>Scott Friesen</name>
    </author><author>
      <name>Martin Schierle</name>
    </author>
  </entry>
  
  <entry>
    <title>Relating site speed and business metrics</title>
    <link href="https://web.dev/site-speed-and-business-metrics/"/>
    <updated>2020-07-17T00:00:00Z</updated>
    <id>https://web.dev/site-speed-and-business-metrics/</id>
    <content type="html" mode="escaped">&lt;p&gt;Over the last few years it has been well established that site speed performance
is a significant part of the user experience and that improving it benefits
different business metrics such as conversion rates and bounce rates. Multiple
articles and case studies have been published to back this up, including
Cloudflare&#39;s &lt;a href=&quot;https://www.cloudflare.com/learning/performance/more/website-performance-conversion-rates/&quot; rel=&quot;noopener&quot;&gt;How Website Performance Affects Conversion
Rates&lt;/a&gt;,
Deloitte&#39;s &lt;a href=&quot;https://www2.deloitte.com/ie/en/pages/consulting/articles/milliseconds-make-millions.html&quot; rel=&quot;noopener&quot;&gt;Milliseconds Make
Millions&lt;/a&gt;,
and &lt;a href=&quot;https://web.dev/shopping-for-speed-on-ebay/&quot;&gt;Shopping for Speed on eBay.com&lt;/a&gt;, to name a
few.&lt;/p&gt;
&lt;p&gt;Even though the case for speed is clear, many companies still struggle with
prioritizing work that will improve their site speed since they do not know
exactly how it affects &lt;strong&gt;their&lt;/strong&gt; users and as a result &lt;strong&gt;their&lt;/strong&gt; business.&lt;/p&gt;
&lt;p&gt;In the absence of data, it is easy to delay site speed work and focus on other
tasks. A common scenario is having some people in the company recognize the
importance of site speed and yet not be able to build a case for it and convince
multiple stakeholders to invest accordingly.&lt;/p&gt;
&lt;p&gt;This article provides high-level guidance on how to leverage A/B testing to
evaluate the impact of site speed on business metrics therefore enabling more
effective decision-making on the matter.&lt;/p&gt;
&lt;h2 id=&quot;step-1-pick-a-page-to-ab-test&quot;&gt;Step 1: Pick a page to A/B test &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/site-speed-and-business-metrics/#step-1-pick-a-page-to-ab-test&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Your aim is to test the hypothesis that page speed relates to your business
metrics. For simplicity&#39;s sake, you can initially limit yourself to identifying
a single page for analysis. Future work can expand to multiple pages of the same
type to verify findings, and then to other areas of the site. Some suggestions
for where to start are provided at the bottom of this step. Several requirements
drive the page selection process:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The A/B test should only be run on the devices of mobile users. Globally,
the partner sites we assist see an average of &lt;a href=&quot;https://gs.statcounter.com/platform-market-share/desktop-mobile-tablet/worldwide&quot; rel=&quot;noopener&quot;&gt;more than
50%&lt;/a&gt;
(and growing!) of their traffic coming from mobile, but this can increase
significantly based on
&lt;a href=&quot;https://gs.statcounter.com/platform-market-share/desktop-mobile-tablet/india&quot; rel=&quot;noopener&quot;&gt;region&lt;/a&gt;
and
&lt;a href=&quot;https://www.salecycle.com/blog/stats/2019-ecommerce-year-in-review-infographic/&quot; rel=&quot;noopener&quot;&gt;vertical&lt;/a&gt;.
Mobile devices are more sensitive to slower websites due to processing and
memory constraints and less stable networks. Also, on-the-go usage patterns
mean expectations for speed are higher.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The page you choose for testing should be an important part of your
conversion funnel. Every site has a different purpose, so every site tracks
different success metrics. These metrics are usually related to a user
journey which is analyzed using a funnel. For example, users on an
e-commerce website will have to navigate through a home page, category
pages, product pages, and a checkout page to complete a purchase. If you&#39;re
optimizing for conversions, one of those pages would be a good candidate.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The page should have a singular purpose. Unless your site has a very
specific mission, it&#39;s usually best to avoid using the homepage for your
test. Many commercial sites&#39; homepages are portals to a wide variety of
functionality that will make your analysis noisy. For example, if you&#39;re
optimizing for pageviews per session on a news site, it might be best to
exclude the non-commercial portions of the site and focus on monetized
sections and articles.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The page chosen should get enough traffic so that you don&#39;t have to wait
long before getting a statistically significant result.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The selected page should be relatively slow. In fact, the slower the better.
This not only means that you will likely have an easier time improving the
page, it also means that the data should be much clearer. You can do a quick
scan through your &lt;a href=&quot;https://support.google.com/analytics/answer/1205784&quot; rel=&quot;noopener&quot;&gt;Google Analytics Speed
Report&lt;/a&gt; or &lt;a href=&quot;https://support.google.com/webmasters/answer/9205520&quot; rel=&quot;noopener&quot;&gt;Search
Console Core Web Vitals
report&lt;/a&gt; to see which
of your pages are slowest.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The page should be relatively stable. Don&#39;t update pages (anything that
would impact business metrics) until the test is complete. The fewer
external factors there are to consider, the cleaner the analysis will be.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Using the above as a guide it should be a bit more clear which pages are good
candidates for your test. Ad landing pages are also a good choice, because
you&#39;re likely to have built-in business metrics, A/B testing,  and analysis at
your disposal. In case you&#39;re still unsure, here are some ideas per vertical:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Content Websites: sections, articles&lt;/li&gt;
&lt;li&gt;Storefronts: category pages, product pages&lt;/li&gt;
&lt;li&gt;Media Players: video discovery/search pages, video play page&lt;/li&gt;
&lt;li&gt;Services &amp;amp; Discovery (travel, rental cars, service registration, etc.):
initial form-entry page&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;step-2-measure-performance&quot;&gt;Step 2: Measure performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/site-speed-and-business-metrics/#step-2-measure-performance&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are two general ways to measure metrics: &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/#how-metrics-are-measured&quot;&gt;in the lab and in the
field&lt;/a&gt;. We
personally have found measuring metrics in the field (also known as Real User
Monitoring (RUM)) to be more useful because it reflects the experience of real
users. Examples of libraries and services that can help you report RUM data
include &lt;a href=&quot;https://zizzamia.github.io/perfume/&quot; rel=&quot;noopener&quot;&gt;Perfume&lt;/a&gt;, &lt;a href=&quot;https://firebase.google.com/docs/perf-mon&quot; rel=&quot;noopener&quot;&gt;Firebase Performance
Monitoring&lt;/a&gt;, and &lt;a href=&quot;https://developers.google.com/analytics/devguides/collection/analyticsjs/events&quot; rel=&quot;noopener&quot;&gt;Google Analytics
Events&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/&quot;&gt;many metrics to choose from&lt;/a&gt;
because they aim to capture different aspects of the user experience. Remember
that your goal is to eventually determine if there&#39;s a direct correlation
between your speed and business metrics, so it might be useful to track a few
speed metrics in order to determine which one has the strongest correlation with
your business success. In general we recommend starting with the &lt;a href=&quot;https://web.dev/vitals/#core-web-vitals&quot;&gt;Core Web
Vitals&lt;/a&gt;. The
&lt;a href=&quot;https://github.com/GoogleChrome/web-vitals&quot; rel=&quot;noopener&quot;&gt;web-vitals.js&lt;/a&gt; library can help you
measure some of the Core Web Vitals in the field, although note that &lt;a href=&quot;https://github.com/GoogleChrome/web-vitals#browser-support&quot; rel=&quot;noopener&quot;&gt;browser
support isn&#39;t 100%&lt;/a&gt;.
Beyond the Core Web Vitals, the &lt;a href=&quot;https://web.dev/vitals/#other-web-vitals&quot;&gt;Other Web Vitals&lt;/a&gt;
are also worth checking out. You can also define &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/#custom-metrics&quot;&gt;custom
metrics&lt;/a&gt;, such as &amp;quot;Time To
First Ad Click&amp;quot;.&lt;/p&gt;
&lt;h2 id=&quot;step-3-create-speed-performance-variants&quot;&gt;Step 3: Create speed performance variants &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/site-speed-and-business-metrics/#step-3-create-speed-performance-variants&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this stage you will implement changes to create a faster version of the page
to be tested against the current version.&lt;/p&gt;
&lt;p&gt;A couple of things to keep in mind:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Avoid making any changes to the UI or UX. Aside from one page being faster
than the other, changes must be invisible to users.&lt;/li&gt;
&lt;li&gt;Measuring is also a key aspect of this stage. While developing, lab testing
tools such as Lighthouse should be used to indicate the effect your changes
have on performance. Keep in mind that changes to one metric can often
influence another. Once the pages are live, stick to RUM for a more accurate
evaluation.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Creating performance variants can be done in different ways. For the purpose of
the test, you would want to do so as simply as possible. Below are a few
options.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Create a faster page&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use a tool like &lt;a href=&quot;https://squoosh.app/&quot; rel=&quot;noopener&quot;&gt;Squoosh&lt;/a&gt; to manually optimize the
images on your test page&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/devtools/coverage/&quot; rel=&quot;noopener&quot;&gt;Use DevTools code
coverage&lt;/a&gt;
to manually eliminate unused JavaScript or CSS just for that one page&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/efficiently-load-third-party-javascript/&quot;&gt;Efficiently load third-party
scripts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Use a tool like &lt;a href=&quot;https://github.com/addyosmani/critical&quot; rel=&quot;noopener&quot;&gt;Critical&lt;/a&gt; to break
out and inline critical CSS&lt;/li&gt;
&lt;li&gt;Remove non-critical JavaScript code that does not impact the user experience
and which you can do without for the purpose of the test (for example,
certain third-party libraries)&lt;/li&gt;
&lt;li&gt;Implement &lt;a href=&quot;https://web.dev/browser-level-image-lazy-loading/&quot;&gt;browser-level lazy loading&lt;/a&gt; which isn&#39;t supported
by all browsers but may still improve performance significantly where
supported&lt;/li&gt;
&lt;li&gt;Remove non-critical analytics tags or load them asynchronously&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Additional optimizations to consider can be found at &lt;a href=&quot;https://web.dev/fast/&quot;&gt;Fast load times&lt;/a&gt;
and &lt;a href=&quot;https://www.smashingmagazine.com/2020/01/front-end-performance-checklist-2020-pdf-pages/&quot; rel=&quot;noopener&quot;&gt;Frontend Performance
Checklist&lt;/a&gt;.
You can also use &lt;a href=&quot;https://pagespeed.web.dev/&quot; rel=&quot;noopener&quot;&gt;PageSpeed
Insights&lt;/a&gt; to run
Lighthouse, which identifies opportunities to improve performance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Slow down the page&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This may be the easiest way to create variants and can be achieved by adding a
simple script, slowing down server response times, loading larger images, etc.
The Financial Times opted for this option when testing how performance impacted
their business metrics: see &lt;a href=&quot;https://medium.com/ft-product-technology/a-faster-ft-com-10e7c077dc1c&quot; rel=&quot;noopener&quot;&gt;A faster
FT.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Speed up page load&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For cases where the test page (let&#39;s say a product detail page) is mostly linked
out from a different page (let&#39;s say the homepage),
&lt;a href=&quot;https://web.dev/link-prefetch/&quot;&gt;prefetching&lt;/a&gt; or
&lt;a href=&quot;https://developer.chrome.com/blog/nostate-prefetch&quot; rel=&quot;noopener&quot;&gt;prerendering&lt;/a&gt;
the product page directly from the homepage for the test group will speed up the
subsequent load of the page. Note that in this case the A/B test split (step 4)
is done on the homepage. In addition, all of this may slow down the first page
so be sure to measure that and take it under consideration when analyzing the
test results (step 5).&lt;/p&gt;
&lt;h2 id=&quot;step-4-create-an-ab-test&quot;&gt;Step 4: Create an A/B test &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/site-speed-and-business-metrics/#step-4-create-an-ab-test&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once you have two versions of the same page where one is faster than the other,
the next step is to split traffic to measure impact. In general there are many
techniques and tools to perform A/B tests, but be aware that not all methods are
well suited to measure speed performance impact.&lt;/p&gt;
&lt;p&gt;If you&#39;re using an A/B testing tool like
&lt;a href=&quot;https://www.optimizely.com/de/&quot; rel=&quot;noopener&quot;&gt;Optimizely&lt;/a&gt; or
&lt;a href=&quot;https://optimize.google.com/optimize/home/&quot; rel=&quot;noopener&quot;&gt;Optimize&lt;/a&gt; we strongly recommend
setting up a server-side test instead of a client-side test, as client side A/B
testing often works by hiding page content until the experiment is loaded, which
would mean the A/B testing itself would skew the metrics you wanted to measure.
If you can only do client-side testing, consider setting up the experiment on a
different page and changing the link outs to your test page to split the
traffic. This way the test page itself isn&#39;t dragged down by the client-side
test.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;
  Example
&lt;/summary&gt;
&lt;p&gt;Example of AB testing  performance changes on a given product detail page (PDP)
via server-side testing:&lt;/p&gt;
&lt;img alt=&quot;Server side testing diagram&quot; decoding=&quot;async&quot; height=&quot;230&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/xQWUEAAfowEg0wjmVsrm.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/xQWUEAAfowEg0wjmVsrm.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/xQWUEAAfowEg0wjmVsrm.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/xQWUEAAfowEg0wjmVsrm.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/xQWUEAAfowEg0wjmVsrm.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/xQWUEAAfowEg0wjmVsrm.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/xQWUEAAfowEg0wjmVsrm.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/xQWUEAAfowEg0wjmVsrm.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/xQWUEAAfowEg0wjmVsrm.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/xQWUEAAfowEg0wjmVsrm.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/xQWUEAAfowEg0wjmVsrm.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/xQWUEAAfowEg0wjmVsrm.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/xQWUEAAfowEg0wjmVsrm.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/xQWUEAAfowEg0wjmVsrm.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/xQWUEAAfowEg0wjmVsrm.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/xQWUEAAfowEg0wjmVsrm.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/xQWUEAAfowEg0wjmVsrm.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/xQWUEAAfowEg0wjmVsrm.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;The request goes to the backend, which distributes the users to the two
different versions of the page. While this is in general a good setup, it often
needs IT resources to set up the server-side split.&lt;/p&gt;
&lt;p&gt;Here is an example of a client side testing setup, using the prior page (the
homepage in the diagram below) to run the testing JavaScript:&lt;/p&gt;
&lt;img alt=&quot;Client side testing diagram&quot; decoding=&quot;async&quot; height=&quot;337&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/tX6XL3IoZBVdJXCPAE1E.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/tX6XL3IoZBVdJXCPAE1E.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/tX6XL3IoZBVdJXCPAE1E.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/tX6XL3IoZBVdJXCPAE1E.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/tX6XL3IoZBVdJXCPAE1E.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/tX6XL3IoZBVdJXCPAE1E.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/tX6XL3IoZBVdJXCPAE1E.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/tX6XL3IoZBVdJXCPAE1E.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/tX6XL3IoZBVdJXCPAE1E.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/tX6XL3IoZBVdJXCPAE1E.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/tX6XL3IoZBVdJXCPAE1E.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/tX6XL3IoZBVdJXCPAE1E.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/tX6XL3IoZBVdJXCPAE1E.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/tX6XL3IoZBVdJXCPAE1E.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/tX6XL3IoZBVdJXCPAE1E.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/tX6XL3IoZBVdJXCPAE1E.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/tX6XL3IoZBVdJXCPAE1E.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/tX6XL3IoZBVdJXCPAE1E.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;The testing JavaScript manipulates the outgoing link to give the two test groups
of users links to the two versions of the PDP in question. This is easy to setup
and maintain through common A/B testing tools like Optimizely or Optimize and
does not influence the performance test because the testing JavaScript runs on a
different page.&lt;/p&gt;
&lt;/details&gt;
&lt;p&gt;Alternatively, you can pick two pages which behave and perform very similarly
(for example, for two very similar products). Apply your changes to one of them
and then compare the difference in metrics over time. This will mean you&#39;re not
conducting a proper A/B test, yet it can still be quite insightful.&lt;/p&gt;
&lt;p&gt;In case your test page is used as a landing page for ad campaigns, it can be
handy to use your ad network&#39;s built-in A/B testing tools, like &lt;a href=&quot;https://www.facebook.com/business/help/1159714227408868&quot; rel=&quot;noopener&quot;&gt;Facebook Ads
Split Test&lt;/a&gt; or &lt;a href=&quot;https://support.google.com/google-ads/answer/6318732?hl=en&quot; rel=&quot;noopener&quot;&gt;Google
Ads Drafts &amp;amp;
Experiments&lt;/a&gt;. If
that&#39;s not an option, you can use two campaigns with the same setup, and set
different landing pages as targets.&lt;/p&gt;
&lt;h2 id=&quot;step-5-analyze-ab-test&quot;&gt;Step 5: Analyze A/B test &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/site-speed-and-business-metrics/#step-5-analyze-ab-test&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After you&#39;ve run your test for long enough and have enough data to feel
confident about the results, it&#39;s time to put it all together and run an
analysis. How you do that really depends on how the test has run, so let&#39;s go
through the options.&lt;/p&gt;
&lt;p&gt;If your test was run on ad landing pages using the above-mentioned tools,
analysis should be as straightforward as reading a result. If you&#39;re using
Google&#39;s Drafts &amp;amp; Experiments, take a look at the comparison using the
&lt;a href=&quot;https://support.google.com/google-ads/answer/6318747?hl=en&quot; rel=&quot;noopener&quot;&gt;ScoreCard&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Platforms like Optimizely or Optimize also offer &lt;a href=&quot;https://support.google.com/optimize/answer/6218117?hl=en&quot; rel=&quot;noopener&quot;&gt;easy
ways&lt;/a&gt; to interpret the
results and determine how much of an impact speed has on your pages.&lt;/p&gt;
&lt;p&gt;If you&#39;re using Google Analytics or a similar tool, you will have to pull
together the report yourself. Luckily, Google Analytics makes it pretty easy to
build &lt;a href=&quot;https://support.google.com/analytics/answer/1151300?hl=en&quot; rel=&quot;noopener&quot;&gt;custom
reports&lt;/a&gt;, so that&#39;s
where you should start. If you&#39;ve sent speed data to Google Analytics using a
custom dimension, check out the &lt;a href=&quot;https://support.google.com/analytics/answer/2709828?hl=en#reporting&quot; rel=&quot;noopener&quot;&gt;Reporting
guide&lt;/a&gt; to
learn how to set those up and include them in your custom reports. Make sure
your report covers the date of the experiment and is configured to display both
variants. What should go in this report?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Primarily, you need to include the business metrics that you most care
about: conversions, page views, ads viewed, conversion rate, e-commerce
metrics, click-through rate, etc.&lt;/li&gt;
&lt;li&gt;Additionally, other standard page metrics that also make a good case for
improving site speed are bounce rate, average session duration, and exit
percentage.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You may also need to filter for mobile and make sure to &lt;a href=&quot;https://support.google.com/analytics/thread/16422940?hl=en&amp;amp;msgid=16485536&quot; rel=&quot;noopener&quot;&gt;exclude bots and other
non-user
traffic&lt;/a&gt;.
More advanced analysis would also filter by region, networks, devices, traffic
source, and user profiles and types, such as new users versus repeat visitors.
Each group of users may be more or less sensitive to slower speeds and
identifying these is also quite helpful.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://support.google.com/looker-studio/answer/6283323?hl=en&quot; rel=&quot;noopener&quot;&gt;Looker Studio&lt;/a&gt; (formerly Data Studio) or
other data visualization tools make it easy to integrate various data sources
including Google Analytics. This makes it easy to conduct analysis, and also
create dashboards that are shareable with the many stakeholders involved in
running a modern website for further buy-in. For example, the Guardian created
an &lt;a href=&quot;https://www.theguardian.com/info/developer-blog/2017/mar/06/empowering-our-editorial-teams-to-impact-page-performance&quot; rel=&quot;noopener&quot;&gt;automated alert
system&lt;/a&gt;
that warned the editorial team when recently published content crossed their
page size or speed thresholds and was likely to result in unsatisfied users.&lt;/p&gt;
&lt;h2 id=&quot;step-6-draw-conclusions-and-decide-on-next-steps&quot;&gt;Step 6: Draw conclusions and decide on next steps &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/site-speed-and-business-metrics/#step-6-draw-conclusions-and-decide-on-next-steps&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once you have data that connects performance and business metrics, you can
examine the results and start to draw conclusions.&lt;/p&gt;
&lt;p&gt;If you can clearly see a correlation between improving performance and improving
business metrics, summarize the results and report them across the company. Now
that you can talk about speed performance in a &amp;quot;business language&amp;quot; you are more
likely to grab the attention of different stakeholders and place site speed
performance on everyone&#39;s radar. The next step is to set &lt;a href=&quot;https://web.dev/your-first-performance-budget/&quot;&gt;performance
budgets&lt;/a&gt; based on the results, and plan out
work to meet those budgets. Since you know the value such work will provide,
you&#39;ll be able to prioritize accordingly.&lt;/p&gt;
&lt;p&gt;If you can&#39;t identify a correlation, take a look at the caveats below and assess
whether similar tests should be run elsewhere on the site (for example, through
the entire purchase funnel or on a different type of page).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Caveats&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There might be several reasons for not finding significant correlation between
site speed metrics and business metrics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The page chosen does not have enough influence on the business metrics
you&#39;re examining. For example, a faster product page may not have a big
effect on conversion rates if the checkout page is very unfriendly or slow.
Consider looking at more relevant metrics such as bounce rates,
add-to-basket rates or any other metric that is more directly connected to
the page you&#39;re testing.&lt;/li&gt;
&lt;li&gt;The difference in speed is not significant enough between the two versions.
This should be evaluated according to the RUM metrics you&#39;re measuring.&lt;/li&gt;
&lt;li&gt;There&#39;s a fault with the A/B testing mechanism. Traffic might not be
distributed properly or analytics not reported correctly. In order to rule
this out, consider running an A/A test where you test the same version of a
page using the same testing mechanism and ensure there&#39;s no difference in
results when doing so.&lt;/li&gt;
&lt;li&gt;Site speed really doesn&#39;t influence your business metrics. This is rare but
can occur in cases where your target market is less sensitive to speed (e.g.
the site is mostly accessed from powerful devices on a strong network) or
user demand is very high and choice is limited (e.g. a ticketing service
that exclusively sells tickets for high-demand shows). Note that this does
not mean that a faster site will not improve user experience and as a result
&lt;a href=&quot;https://www.thinkwithgoogle.com/intl/en-154/marketing-collections/mobile/why-mobile-speed-means-loyal-customers-and-how-make-it-happen/&quot; rel=&quot;noopener&quot;&gt;affect brand
reputation&lt;/a&gt;.&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/site-speed-and-business-metrics/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While it is tempting to launch speed optimizations across the entire site, it is
usually more beneficial in the long run to first understand what it means to
your users and your company to have a faster website. It is the difference
between saying &amp;quot;we improved FCP by 1.5 seconds&amp;quot; and &amp;quot;we improved FCP by 1.5
seconds and that improved our conversion rates by 5%&amp;quot;. This will allow you to
prioritize further work, get buy-in from different stakeholders and &lt;a href=&quot;https://web.dev/fixing-website-speed-cross-functionally/&quot;&gt;make site
speed performance a company-wide
effort&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Dikla Cohen</name>
    </author><author>
      <name>Martin Schierle</name>
    </author><author>
      <name>Bart Jarochowski</name>
    </author>
  </entry>
  
  <entry>
    <title>How can performance improve conversion?</title>
    <link href="https://web.dev/how-can-performance-improve-conversion/"/>
    <updated>2019-06-11T00:00:00Z</updated>
    <id>https://web.dev/how-can-performance-improve-conversion/</id>
    <content type="html" mode="escaped">&lt;p&gt;In our other e-commerce guides you have learned about &lt;a href=&quot;https://web.dev/what-should-you-measure-to-improve-performance/&quot;&gt;what you should measure
to improve performance&lt;/a&gt;, and
&lt;a href=&quot;https://web.dev/how-to-report-metrics/&quot;&gt;how to measure and report metrics to build a performance culture&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A conversion funnel going from discover to engage to convert to re-engage.&quot; decoding=&quot;async&quot; height=&quot;399&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    A conversion funnel.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In this guide we&#39;ll address the different ways in which a website should be optimized for performance to yield maximum conversions at the end of the funnel.&lt;/p&gt;
&lt;h2 id=&quot;discovery&quot;&gt;Discovery &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-can-performance-improve-conversion/#discovery&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;New users discover a website in most cases through organic search, social sharing, website links or paid campaigns. Some important discovery mechanisms are directly affected by website performance. Website crawlers may have difficulty indexing sites that are slow to load or have extensive &lt;a href=&quot;https://developers.google.com/search/docs/crawling-indexing/javascript/dynamic-rendering&quot; rel=&quot;noopener&quot;&gt;client side rendering and JavaScript&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Speed can also be a direct ranking factor, for example on &lt;a href=&quot;https://webmasters.googleblog.com/2018/01/using-page-speed-in-mobile-search.html&quot; rel=&quot;noopener&quot;&gt;web search&lt;/a&gt;, &lt;a href=&quot;https://developer.chrome.com/blog/search-ads-speed/#the-mobile-speed-score-for-ads-landing-pages&quot; rel=&quot;noopener&quot;&gt;ad campaigns&lt;/a&gt; or &lt;a href=&quot;https://newsroom.fb.com/news/2017/08/news-feed-fyi-showing-you-stories-that-link-to-faster-loading-webpages/&quot; rel=&quot;noopener&quot;&gt;social networks&lt;/a&gt;.
Keep in mind that new users who discover your website will get an uncached first load, so basically the worst possible experience. This can be especially frustrating if good money was spent to get the user to the website, just to see them dropping off due to a long first load.&lt;/p&gt;
&lt;p&gt;Make sure to use appropriate tools as described in &lt;a href=&quot;https://web.dev/fast&quot;&gt;Fast Load Times&lt;/a&gt; to optimize towards a first load, because first impressions matter—if the first load is too slow, the user may never see the optimized second load or stay around to look at your products. In general &lt;a href=&quot;https://developer.akamai.com/blog/2015/09/01/mobile-web-performance-monitoring-conversion-rate&quot; rel=&quot;noopener&quot;&gt;loading times of a website map very well to bounce rates&lt;/a&gt;, which in turn often correlate well with conversions.&lt;/p&gt;
&lt;h2 id=&quot;engagement&quot;&gt;Engagement &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-can-performance-improve-conversion/#engagement&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After getting users to your site, you need to keep them engaged with your content, which you can verify in your analytics of choice by looking at session length, time-on-page, pages-per-session and general &lt;a href=&quot;https://support.google.com/analytics/answer/1709395?hl=en&quot; rel=&quot;noopener&quot;&gt;user flows&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A Google Analytics dashboard shows the number of users that drop off from starting page to first and second interactions.&quot; decoding=&quot;async&quot; height=&quot;416&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/Apz0cUkUGVR7m9gKepkt.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/Apz0cUkUGVR7m9gKepkt.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/Apz0cUkUGVR7m9gKepkt.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/Apz0cUkUGVR7m9gKepkt.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/Apz0cUkUGVR7m9gKepkt.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/Apz0cUkUGVR7m9gKepkt.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/Apz0cUkUGVR7m9gKepkt.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/Apz0cUkUGVR7m9gKepkt.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/Apz0cUkUGVR7m9gKepkt.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/Apz0cUkUGVR7m9gKepkt.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/Apz0cUkUGVR7m9gKepkt.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/Apz0cUkUGVR7m9gKepkt.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/Apz0cUkUGVR7m9gKepkt.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/Apz0cUkUGVR7m9gKepkt.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/Apz0cUkUGVR7m9gKepkt.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/Apz0cUkUGVR7m9gKepkt.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/Apz0cUkUGVR7m9gKepkt.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/Apz0cUkUGVR7m9gKepkt.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    A user flow through the funnel as seen by Google Analytics.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Besides various UX best practices, a smooth, fast and responsive experience is key here. While optimizing a website for discovery means optimizing for first load, optimizing for engagement means fast navigations and fast repeat loads. Analyze at which steps of the flow users drop out, and then relate back to speed metrics for these navigations. This can be analyzed for example via &lt;a href=&quot;https://github.com/WPO-Foundation/webpagetest-docs/blob/main/src/scripting.md#sample-scripts&quot; rel=&quot;noopener&quot;&gt;WebPageTest&lt;/a&gt;, &lt;a href=&quot;https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md&quot; rel=&quot;noopener&quot;&gt;Puppeteer&lt;/a&gt; or via the Chrome DevTools &lt;a href=&quot;https://developer.chrome.com/docs/devtools/evaluate-performance/#record&quot; rel=&quot;noopener&quot;&gt;Record&lt;/a&gt; feature. We will show you more examples of those in the following guides.&lt;/p&gt;
&lt;h2 id=&quot;conversion&quot;&gt;Conversion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-can-performance-improve-conversion/#conversion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While website conversions are often bound to good discovery paired with great engagement, there are a couple of additional points to remember. Users expect hero images to &lt;a href=&quot;https://web.dev/user-centric-performance-metrics/&quot;&gt;load fast&lt;/a&gt;, call-to-action buttons should be rendered and &lt;a href=&quot;https://developer.chrome.com/blog/lighthouse-load-performance/#all-text-remains-visible-during-web-font-loads&quot; rel=&quot;noopener&quot;&gt;labeled&lt;/a&gt; quickly, page should be &lt;a href=&quot;https://web.dev/fid/&quot;&gt;responsive&lt;/a&gt; and &lt;a href=&quot;https://css-tricks.com/content-jumping-avoid/&quot; rel=&quot;noopener&quot;&gt;layout jumps&lt;/a&gt; should be avoided. A user won&#39;t buy anything if they can&#39;t click the &lt;em&gt;Buy Now&lt;/em&gt; button due to busy CPU or a jumping or unlabeled button.
In general it&#39;s best to measure and track the time-to-action towards a conversion or a subgoal for it, for example the median time it takes shoppers to get from landing on your site, to viewing a product, to completing payment .&lt;/p&gt;
&lt;h2 id=&quot;re-engagement&quot;&gt;Re-Engagement &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-can-performance-improve-conversion/#re-engagement&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It turns out that only 2% of users &lt;a href=&quot;https://retargeter.com/what-is-retargeting-and-how-does-it-work/&quot; rel=&quot;noopener&quot;&gt;convert on first visit&lt;/a&gt;, so it is important to get the other 98% to come back, and re-engage them with your content. Modern websites have different ways to do this, for example via mail, tailored display ads in &lt;a href=&quot;https://support.google.com/google-ads/answer/2453998?hl=en&quot; rel=&quot;noopener&quot;&gt;remarketing&lt;/a&gt; or notifications. This works best if the flow from reengagement to website is as smooth as possible. Unfortunately this is not always the case, as for example mail apps often open links in their in-app webview, slowing down page load and complicating logins through different cache and cookie storage.
Make sure to optimize for fast repeat loads and smooth UX flows to increase chances of reengagement.&lt;/p&gt;
&lt;h2 id=&quot;recap&quot;&gt;Recap &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-can-performance-improve-conversion/#recap&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;E-commerce sites always strive for conversions, which are at the end of a purchase funnel. Every step along the funnel needs to be optimized for website speed to minimize bounce rates and drop-offs, and for each step there are different things to optimize, different pitfalls and culprits:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A conversion funnel going from discover to engage to convert to re-engage.&quot; decoding=&quot;async&quot; height=&quot;399&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/tRFlwIEQJcf52vGBEoDp.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    An e-commerce funnel showing which metric to optimize in which step.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To learn more, be sure to check out the other posts in this series on &lt;a href=&quot;https://web.dev/what-should-you-measure-to-improve-performance/&quot;&gt;measuring
to improve performance&lt;/a&gt;, and
&lt;a href=&quot;https://web.dev/how-to-report-metrics/&quot;&gt;how report metrics to build a performance culture&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hero image by Campaign Creators on Unsplash&lt;/em&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Martin Schierle</name>
    </author>
  </entry>
  
  <entry>
    <title>What should you measure to improve performance?</title>
    <link href="https://web.dev/what-should-you-measure-to-improve-performance/"/>
    <updated>2019-05-31T00:00:00Z</updated>
    <id>https://web.dev/what-should-you-measure-to-improve-performance/</id>
    <content type="html" mode="escaped">&lt;p&gt;The different steps of a purchase funnel are prone to performance issues in
different ways, and therefore need different measurement and optimizations:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A conversion funnel going from discover to engage to convert to re-engage.&quot; decoding=&quot;async&quot; height=&quot;399&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/87cAGdJAnPggf8Yt58ID.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/87cAGdJAnPggf8Yt58ID.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/87cAGdJAnPggf8Yt58ID.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/87cAGdJAnPggf8Yt58ID.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/87cAGdJAnPggf8Yt58ID.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/87cAGdJAnPggf8Yt58ID.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/87cAGdJAnPggf8Yt58ID.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/87cAGdJAnPggf8Yt58ID.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/87cAGdJAnPggf8Yt58ID.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/87cAGdJAnPggf8Yt58ID.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/87cAGdJAnPggf8Yt58ID.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/87cAGdJAnPggf8Yt58ID.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/87cAGdJAnPggf8Yt58ID.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/87cAGdJAnPggf8Yt58ID.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/87cAGdJAnPggf8Yt58ID.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/87cAGdJAnPggf8Yt58ID.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/87cAGdJAnPggf8Yt58ID.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/87cAGdJAnPggf8Yt58ID.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    A conversion funnel.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In this guide we&#39;ll address how the different steps can be measured. For this we
recommend you to look at lab as well as field data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lab data&lt;/strong&gt; is gathered by running tests locally, for example by using
&lt;a href=&quot;https://web.dev/fast/discover-performance-opportunities-with-lighthouse&quot;&gt;Lighthouse&lt;/a&gt;
and other tools.  This can make it possible  to compare website performance over
time and with competitors through a controlled, stable environment, but it might
not be representative of the performance users experience in real life.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Field data&lt;/strong&gt; is gathered via analytics from real users, and is therefore
representative of their experience. However, it can&#39;t easily be compared over
time or with competitors. Network connections and smartphone hardware evolve
over time, and different target audiences might have different devices, thereby
making comparisons with field data tough. See also &lt;em&gt;&lt;a href=&quot;https://web.dev/fast#measure-performance-in-the-field&quot;&gt;Measure Performance In The Field&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;To get a complete picture, both data sources are needed. The following sections
show how to gather lab and field data for the different relevant performance
marks across the funnel.&lt;/p&gt;
&lt;h2 id=&quot;discovery&quot;&gt;Discovery &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/what-should-you-measure-to-improve-performance/#discovery&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Optimizing for discovery means optimizing for the first load, which is what new
users  will get, but also search and social
&lt;a href=&quot;https://developers.google.com/search/docs/guides/rendering&quot; rel=&quot;noopener&quot;&gt;crawlers&lt;/a&gt;.
Lab data for a first load can be easily acquired through
&lt;a href=&quot;https://web.dev/fast/discover-performance-opportunities-with-lighthouse&quot;&gt;Lighthouse&lt;/a&gt;,
while field data (at least for Chrome) is readily available through &lt;a href=&quot;https://web.dev/fast/chrome-ux-report&quot;&gt;Chrome UX
reports&lt;/a&gt;. A convenient combination of
both can be found in
&lt;a href=&quot;https://pagespeed.web.dev/&quot; rel=&quot;noopener&quot;&gt;PageSpeed Insights&lt;/a&gt;.
You should also track relevant metrics from the field yourself:
&lt;a href=&quot;https://web.dev/user-centric-performance-metrics/#in-the-field&quot;&gt;Measuring these metrics on real users&#39; devices&lt;/a&gt;
provides a good overview.&lt;/p&gt;
&lt;p&gt;From a user perspective the most important metrics are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://web.dev/fcp/&quot;&gt;First Contentful Paint (FCP)&lt;/a&gt;:&lt;/strong&gt;
The time the user stares at a blank screen. This is
when most users bounce, as they don&#39;t see progress.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/first-meaningful-paint/&quot; rel=&quot;noopener&quot;&gt;First Meaningful Paint (FMP)&lt;/a&gt;:&lt;/strong&gt;
When the user begins to see the main content they came for. This
is often the hero image, but for a landing page it may even be a call to
action such as a &lt;strong&gt;Buy&lt;/strong&gt; button, since the user may have arrived with a clear
intent (for example, through a targeted ad campaign).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://web.dev/fid/&quot;&gt;First Input Delay (FID)&lt;/a&gt;:&lt;/strong&gt;
The time the website needs to react to the user&#39;s first input.
Excessive JavaScript and other asset loading problems can block this,
leading to failed taps or clicks, erroneous inputs and page abandonment.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are more metrics you can look at, but these are a good baseline. Additionally also make sure to capture bounce rates, conversions and user engagement so that you can set these in relation.&lt;/p&gt;
&lt;h2 id=&quot;engagement-and-conversion&quot;&gt;Engagement And Conversion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/what-should-you-measure-to-improve-performance/#engagement-and-conversion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After the first load of a landing page, a user will proceed through your site,
hopefully towards a successful conversion.&lt;/p&gt;
&lt;p&gt;In this phase it is important to have fast and responsive navigations and
interactions. Unfortunately it is not trivial to measure the complete flow of
navigation and interaction events in the field, as every user takes different
paths through the page. We therefore recommend measuring the time needed towards
conversion or conversion subgoals (&amp;quot;&lt;em&gt;Time-to-Action&lt;/em&gt;&amp;quot;) by scripting and
measuring the flow in a lab test, to compare performance over time or with
competitors.&lt;/p&gt;
&lt;p&gt;There are two convenient ways of doing this:&lt;/p&gt;
&lt;h3 id=&quot;webpagetest&quot;&gt;WebPageTest &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/what-should-you-measure-to-improve-performance/#webpagetest&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.webpagetest.org/&quot; rel=&quot;noopener&quot;&gt;WebPageTest&lt;/a&gt; offers a very flexible &lt;a href=&quot;https://github.com/WPO-Foundation/webpagetest-docs/blob/main/src/scripting.md&quot; rel=&quot;noopener&quot;&gt;scripting
solution&lt;/a&gt;.
The basic idea is to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tell WebPageTest to navigate through the pages of the flow with the
&lt;a href=&quot;https://github.com/WPO-Foundation/webpagetest-docs/blob/main/src/scripting.md#navigate&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;navigate&lt;/code&gt;&lt;/a&gt;
command.&lt;/li&gt;
&lt;li&gt;If needed script the clicking of buttons via
&lt;a href=&quot;https://github.com/WPO-Foundation/webpagetest-docs/blob/main/src/scripting.md#clickandwait&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;clickAndWait&lt;/code&gt;&lt;/a&gt;
commands and fill text fields via
&lt;a href=&quot;https://github.com/WPO-Foundation/webpagetest-docs/blob/main/src/scripting.md#selectvalue&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;setValue&lt;/code&gt;&lt;/a&gt;.
For testing of Single Page Applications use &lt;code&gt;clickAndWait&lt;/code&gt; rather than
&lt;code&gt;navigate&lt;/code&gt; commands for all steps after the first, as &lt;code&gt;navigate&lt;/code&gt; will do a
full load instead of the lightweight virtual page load.&lt;/li&gt;
&lt;li&gt;Make sure to combine the different steps of the flow in the analysis via
&lt;a href=&quot;https://github.com/WPO-Foundation/webpagetest-docs/blob/main/src/scripting.md#combinesteps&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;combineSteps&lt;/code&gt;&lt;/a&gt;
to produce a single overall result report for the complete flow.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Such a script could look like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;combineSteps&lt;br /&gt;navigate	https://www.store.google.com/landingpage&lt;br /&gt;navigate	https://www.store.google.com/productpage&lt;br /&gt;clickAndWait	innerText=Buy Now&lt;br /&gt;navigate	https://www.store.google.com/basket&lt;br /&gt;navigate	https://www.store.google.com/checkout&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;With a script like this in place you can easily measure and compare performance
over time. This can even be automated through the
&lt;a href=&quot;https://github.com/WPO-Foundation/webpagetest-docs/blob/5337749ae99d0529fc0ae690d402fd4f88766be9/dev/api.md&quot; rel=&quot;noopener&quot;&gt;WebPageTest API&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;puppeteer&quot;&gt;Puppeteer &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/what-should-you-measure-to-improve-performance/#puppeteer&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Another great option to script testing is via headless Chrome, which can be
controlled through the Node API
&lt;a href=&quot;https://github.com/GoogleChrome/puppeteer&quot; rel=&quot;noopener&quot;&gt;Puppeteer&lt;/a&gt;. The general idea is to
start the browser through Puppeteer, navigate to the landing page through the
&lt;a href=&quot;https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagegotourl-options&quot; rel=&quot;noopener&quot;&gt;goto&lt;/a&gt;
function,
&lt;a href=&quot;https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pageevaluatepagefunction-args&quot; rel=&quot;noopener&quot;&gt;inject JavaScript&lt;/a&gt;
to fill fields or
&lt;a href=&quot;https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pageclickselector-options&quot; rel=&quot;noopener&quot;&gt;click&lt;/a&gt;
buttons and proceed through the funnel through further
&lt;a href=&quot;https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagegotourl-options&quot; rel=&quot;noopener&quot;&gt;goto&lt;/a&gt;
calls as needed.&lt;/p&gt;
&lt;p&gt;As a metric the duration of the flow can be measured directly,
but you could also sum up the FCP, FMP or TTI values of the individual loads of
the flow.
&lt;a href=&quot;https://michaljanaszek.com/blog/test-website-performance-with-puppeteer&quot; rel=&quot;noopener&quot;&gt;Test website performance with Puppeteer&lt;/a&gt;
provides an overview of how to get performance metrics via Puppeteer. A very
simplified example Node script could look like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; puppeteer &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;puppeteer&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; browser &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; puppeteer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;launch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&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; page &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&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; start &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; performance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;goto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://www.store.google.com/landingpage&#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;await&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;goto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://www.store.google.com/productpage&#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;// click the buy button, which triggers overlay basket&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;#buy_btn&#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;// wait until basket overlay is shown&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;#close_btn&#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;await&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;goto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://www.store.google.com/basket&#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;await&lt;/span&gt; page&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;goto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://www.store.google.com/checkout&#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;  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;Flow took &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;performance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; start&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;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39; seconds&#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;await&lt;/span&gt; browser&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;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&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 script can easily be automated, and even be made part of the build process
or perf budgets, and be monitored regularly.&lt;/p&gt;
&lt;h2 id=&quot;re-engagement&quot;&gt;Re-Engagement &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/what-should-you-measure-to-improve-performance/#re-engagement&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Users will return to your site in different time intervals. Depending on time
passed, the browser may have less of the website&#39;s resources cached, needing
more network requests. This makes it difficult to estimate performance
differences across repeat visits in lab tests. It is still recommended to keep
an eye on this though, and a great lab test tool for repeat visits is
&lt;a href=&quot;https://www.webpagetest.org/&quot; rel=&quot;noopener&quot;&gt;WebPageTest&lt;/a&gt;, which has a dedicated option for a
direct repeat visit:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;The WebPageTest homepage form for auditing a site. The repeat view option is highlighted.&quot; decoding=&quot;async&quot; height=&quot;650&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/6eHzqWl6gaKjL39n8Dwh.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/6eHzqWl6gaKjL39n8Dwh.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/6eHzqWl6gaKjL39n8Dwh.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/6eHzqWl6gaKjL39n8Dwh.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/6eHzqWl6gaKjL39n8Dwh.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/6eHzqWl6gaKjL39n8Dwh.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/6eHzqWl6gaKjL39n8Dwh.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/6eHzqWl6gaKjL39n8Dwh.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/6eHzqWl6gaKjL39n8Dwh.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/6eHzqWl6gaKjL39n8Dwh.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/6eHzqWl6gaKjL39n8Dwh.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/6eHzqWl6gaKjL39n8Dwh.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/6eHzqWl6gaKjL39n8Dwh.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/6eHzqWl6gaKjL39n8Dwh.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/6eHzqWl6gaKjL39n8Dwh.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/6eHzqWl6gaKjL39n8Dwh.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/6eHzqWl6gaKjL39n8Dwh.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/6eHzqWl6gaKjL39n8Dwh.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Webpagetest offers options to test first load and repeat load as well
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To get a better feeling for repeat visit performance in the field use your
analytics package of choice to segment your performance metrics by user type.
Here is an example of such a report in Google Analytics:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A Google Analytics dashboard shows a number of fields being added to a custom report.&quot; decoding=&quot;async&quot; height=&quot;444&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/pSxM9xuJKho7RjOn4aDK.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/pSxM9xuJKho7RjOn4aDK.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/pSxM9xuJKho7RjOn4aDK.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/pSxM9xuJKho7RjOn4aDK.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/pSxM9xuJKho7RjOn4aDK.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/pSxM9xuJKho7RjOn4aDK.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/pSxM9xuJKho7RjOn4aDK.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/pSxM9xuJKho7RjOn4aDK.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/pSxM9xuJKho7RjOn4aDK.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/pSxM9xuJKho7RjOn4aDK.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/pSxM9xuJKho7RjOn4aDK.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/pSxM9xuJKho7RjOn4aDK.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/pSxM9xuJKho7RjOn4aDK.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/pSxM9xuJKho7RjOn4aDK.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/pSxM9xuJKho7RjOn4aDK.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/pSxM9xuJKho7RjOn4aDK.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/pSxM9xuJKho7RjOn4aDK.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/pSxM9xuJKho7RjOn4aDK.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    A Google Analytics custom report can be used to report speed metrics for new and returning users.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;A report like this will give you page load times for new and returning users as well.&lt;/p&gt;
&lt;h2 id=&quot;recap&quot;&gt;Recap &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/what-should-you-measure-to-improve-performance/#recap&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This guide showed you how to measure first load, flow and repeat load via field and lab tests. Make sure to optimize the different steps of the funnel accordingly to maximize discovery (first load), engagement (navigations and flow) and re-engagement (repeat load).&lt;/p&gt;
</content>
    <author>
      <name>Martin Schierle</name>
    </author>
  </entry>
  
  <entry>
    <title>How to report metrics and build a performance culture</title>
    <link href="https://web.dev/how-to-report-metrics/"/>
    <updated>2019-05-05T00:00:00Z</updated>
    <id>https://web.dev/how-to-report-metrics/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;introduction&quot;&gt;Introduction &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-report-metrics/#introduction&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Website performance is a key goal towards e-commerce conversions, so it must be
a core priority for business stakeholders and leadership, not just development
teams.&lt;/p&gt;
&lt;p&gt;For that reason, it&#39;s crucial to make performance metrics visible and tangible
for all stakeholders, and to build reporting and monitoring into your
workflow.&lt;/p&gt;
&lt;p&gt;This guide describes what to be aware of and what to avoid to achieve
this successfully.&lt;/p&gt;
&lt;h2 id=&quot;a-word-about-metrics&quot;&gt;A word about metrics &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-report-metrics/#a-word-about-metrics&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are countless metrics to evaluate website performance, and while it may
seem useful to collect all of them, too many metrics can be confusing and
misleading. There are several ways to deal with this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Gather multiple metrics and try to narrow and filter afterwards on
what might be relevant for the task at hand.&lt;/li&gt;
&lt;li&gt;Abstract metrics into an overall score, as for example
&lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/performance-scoring&quot; rel=&quot;noopener&quot;&gt;Lighthouse does&lt;/a&gt;.
This can be especially useful for non-technical staff and other
stakeholders, but is probably insufficient for deeper technical analysis.&lt;/li&gt;
&lt;li&gt;Try to find the one metric which is most relevant as a predictor for
your conversions and then optimize towards this.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In reality, a pragmatic combination makes the most sense here. Gather more rather
than less to begin with, report an abstract score to management, and optimize
towards the one metric which best predicts your conversions.&lt;/p&gt;
&lt;p&gt;Finding this metric can be done by using your analytics tool of choice
to map performance metrics to user engagement, conversions and transaction values.
For example, a custom report to do so looks like this in Google Analytics:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A Google Analytics dashboard shows a number of fields being added to a custom report.&quot; decoding=&quot;async&quot; height=&quot;430&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/YI31t9fE82uEhrrYKTVg.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/YI31t9fE82uEhrrYKTVg.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/YI31t9fE82uEhrrYKTVg.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/YI31t9fE82uEhrrYKTVg.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/YI31t9fE82uEhrrYKTVg.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/YI31t9fE82uEhrrYKTVg.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/YI31t9fE82uEhrrYKTVg.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/YI31t9fE82uEhrrYKTVg.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/YI31t9fE82uEhrrYKTVg.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/YI31t9fE82uEhrrYKTVg.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/YI31t9fE82uEhrrYKTVg.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/YI31t9fE82uEhrrYKTVg.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/YI31t9fE82uEhrrYKTVg.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/YI31t9fE82uEhrrYKTVg.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/YI31t9fE82uEhrrYKTVg.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/YI31t9fE82uEhrrYKTVg.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/YI31t9fE82uEhrrYKTVg.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/YI31t9fE82uEhrrYKTVg.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Fig 1: Custom report in Google Analytics to analyze the impact of speed on conversions and engagement.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;strong&gt;Metrics can be deceiving.&lt;/strong&gt; Unfortunately metrics can sometimes be misleading with respect to performance. Keep reading for specifics. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;increasing-bounce-rate&quot;&gt;Increasing bounce rate &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-report-metrics/#increasing-bounce-rate&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;problem&quot;&gt;Problem &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-report-metrics/#problem&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Often it is assumed that bounce rate will drop when page load speed
increases. While this normally does hold true, measurements sometimes show the
opposite. This is because analytics can only measure a bounce after the
analytics library is loaded. A faster page load means analytics code also loads
faster, so analytics may see more bounces even if there aren&#39;t more happening.&lt;/p&gt;
&lt;h3 id=&quot;solution&quot;&gt;Solution &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-report-metrics/#solution&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This can be eased by measuring
&lt;a href=&quot;https://web.dev/user-centric-performance-metrics/&quot;&gt;real page abandonment&lt;/a&gt; instead.&lt;/p&gt;
&lt;h2 id=&quot;decreasing-relative-conversions&quot;&gt;Decreasing relative conversions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-report-metrics/#decreasing-relative-conversions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;problem-2&quot;&gt;Problem &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-report-metrics/#problem-2&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Relative conversions may sometimes seem to drop for faster sites.
This is because faster pages reach a bigger audience who might be less engaged
or committed. While incremental traffic and conversions increase with faster
pages, relative conversions (the ratio of conversions to page views or visitors)
might still drop.&lt;/p&gt;
&lt;h3 id=&quot;solution-2&quot;&gt;Solution &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-report-metrics/#solution-2&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This effect can be eased by looking out for absolute conversions instead, and
even calculate Cost Per Sales (conversions divided with investment level) or ROI.&lt;/p&gt;
&lt;h2 id=&quot;dropping-engagement&quot;&gt;Dropping engagement &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-report-metrics/#dropping-engagement&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;problem-3&quot;&gt;Problem &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-report-metrics/#problem-3&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Page engagement may seem to drop for a faster page.&lt;/p&gt;
&lt;h3 id=&quot;solution-3&quot;&gt;Solution &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-report-metrics/#solution-3&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This can actually be a positive signal! If the page is faster
users can reach their goal more quickly and might just have shorter sessions and
less time on page.&lt;/p&gt;
&lt;h2 id=&quot;using-averages-or-medians&quot;&gt;Using averages or medians &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-report-metrics/#using-averages-or-medians&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;problem-4&quot;&gt;Problem &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-report-metrics/#problem-4&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In general, it can be deceiving to look at averages and medians of
performance metrics (see this nicely described in
&lt;em&gt;&lt;a href=&quot;https://bravenewgeek.com/everything-you-know-about-latency-is-wrong/&quot; rel=&quot;noopener&quot;&gt;Everything You Know About Latency Is Wrong&lt;/a&gt;)&lt;/em&gt;.
Similar to a chain being just as strong as the weakest link, the performance of
a funnel is only as good as its slowest load. A single slow load may be enough
to lose the user. Therefore averages and medians are more likely to hide the
real performance issues, than to reveal them.&lt;/p&gt;
&lt;h3 id=&quot;solution-4&quot;&gt;Solution &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-report-metrics/#solution-4&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It&#39;s best to analyze complete distributions, but as this is not
always easily possible we recommend using 90th percentiles—this is the value
for which 90% of loads were faster. Even then a user hitting ten pages will
still have one load slower than this, and might drop out there.&lt;/p&gt;
&lt;p&gt;While performance measurement is highly important, make sure to keep an open
mind and question unexpected results—and make sure to not report misleading
numbers to stakeholders and management. If unsure on what to pick and report,
we&#39;d advise as a minimum for 90th percentile
&lt;a href=&quot;https://web.dev/fcp/&quot;&gt;First Contentful Paint&lt;/a&gt;, which is also what we use
across our public tooling.&lt;/p&gt;
&lt;h2 id=&quot;third-party-content&quot;&gt;Third party content &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-report-metrics/#third-party-content&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Website performance is especially prone to being dragged down
by third party content
(see &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/performance/render-blocking-resources/&quot; rel=&quot;noopener&quot;&gt;Eliminate render-blocking resources&lt;/a&gt;).
This is a particular problem for e-commerce, often due to trackers and widgets.&lt;/p&gt;
&lt;p&gt;Some ways to handle third party content with respect to performance:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Always keep third party content out of the
&lt;a href=&quot;https://web.dev/critical-rendering-path/&quot;&gt;critical rendering path&lt;/a&gt;.
If the third party has server problems and times out, it will impact your
website heavily. You can test and simulate this via
&lt;a href=&quot;https://css-tricks.com/use-webpagetest-api/#single-point-of-failure&quot; rel=&quot;noopener&quot;&gt;WebPageTest Single-Point-of-Failure&lt;/a&gt;
tests.&lt;/li&gt;
&lt;li&gt;Continually measure and report the ratio of third party content, for
example via WebPageTest &lt;a href=&quot;https://www.webpagetest.org/domains.php&quot; rel=&quot;noopener&quot;&gt;domain
breakdown&lt;/a&gt;. Make sure to use
performance budgets for third party content.&lt;/li&gt;
&lt;li&gt;If you suspect that a particular third party component is having a
detrimental effect on performance, do a performance comparison with it
included and excluded.
&lt;a href=&quot;https://andydavies.me/blog/2018/02/19/using-webpagetest-to-measure-the-impact-of-3rd-party-tags/&quot; rel=&quot;noopener&quot;&gt;Find out how&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Try to stay on one stack from one vendor if possible. For example, if
you have a tag manager and analytics on one stack, you may only need a
single script, and may be able to
&lt;a href=&quot;https://web.dev/performance-http2/&quot;&gt;take advantage of HTTP2 synergies&lt;/a&gt;
as there is only one host involved.&lt;/li&gt;
&lt;li&gt;Make sure not to use the same functionality from two different vendors.
You shouldn&#39;t need two tag managers or two analytics platforms.&lt;/li&gt;
&lt;li&gt;Routinely audit and clean out redundant third party scripts, trackers
and widgets. This can easily be done via &lt;a href=&quot;https://chrome.google.com/webstore/detail/ghostery-%E2%80%93-privacy-ad-blo/mlomiejdfkolichcflejclcbmpeaniij?hl=en&quot; rel=&quot;noopener&quot;&gt;Ghostery Extension&lt;/a&gt; or tools like &lt;a href=&quot;https://chrome.google.com/webstore/detail/whatruns/cmkdbmfndkfgebldhnkbfhlneefdaaip?hl=en&quot; rel=&quot;noopener&quot;&gt;WhatRuns&lt;/a&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A Ghostery report showing all loaded trackers.&quot; decoding=&quot;async&quot; height=&quot;710&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/w2xb4mUJ7kDpt254fq0k.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/w2xb4mUJ7kDpt254fq0k.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/w2xb4mUJ7kDpt254fq0k.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/w2xb4mUJ7kDpt254fq0k.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/w2xb4mUJ7kDpt254fq0k.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/w2xb4mUJ7kDpt254fq0k.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/w2xb4mUJ7kDpt254fq0k.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/w2xb4mUJ7kDpt254fq0k.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/w2xb4mUJ7kDpt254fq0k.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/w2xb4mUJ7kDpt254fq0k.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/w2xb4mUJ7kDpt254fq0k.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/w2xb4mUJ7kDpt254fq0k.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/w2xb4mUJ7kDpt254fq0k.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/w2xb4mUJ7kDpt254fq0k.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/w2xb4mUJ7kDpt254fq0k.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/w2xb4mUJ7kDpt254fq0k.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/w2xb4mUJ7kDpt254fq0k.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/w2xb4mUJ7kDpt254fq0k.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    A Ghostery report showing all loaded trackers
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Learn more in
&lt;a href=&quot;https://web.dev/optimizing-content-efficiency-loading-third-party-javascript/&quot;&gt;Loading Third-Party JavaScript&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;performance-culture&quot;&gt;Performance Culture &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-report-metrics/#performance-culture&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Unfortunately performance is often seen as a one-off optimization task, and then
regresses over time as stakeholders raise new feature requests or insist on
adding new trackers and widgets.&lt;/p&gt;
&lt;p&gt;Performance must be a continuous goal to improve acquisition, discovery, and
conversion rates as well as safeguarding the reputation of your brand. This can
be achieved with &lt;a href=&quot;https://web.dev/performance-budgets-101&quot;&gt;performance
budgets&lt;/a&gt;
like &lt;a href=&quot;https://medium.com/@addyosmani/a-tinder-progressive-web-app-performance-case-study-78919d98ece0&quot; rel=&quot;noopener&quot;&gt;Tinder did&lt;/a&gt;,
and by establishing and fostering a performance culture, where all employees and
especially decision makers recognize speed as a core feature of the website.&lt;/p&gt;
&lt;h2 id=&quot;make-metrics-tangible&quot;&gt;Make metrics tangible &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-report-metrics/#make-metrics-tangible&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Reports on metrics are often abstract and easy to challenge or dismiss. It&#39;s
best to make your performance tangible and visible. There are several good ways
to do this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.theverge.com/2015/10/28/9625062/facebook-2g-tuesdays-slow-internet-developing-world&quot; rel=&quot;noopener&quot;&gt;Facebook&lt;/a&gt;
and Google do this by providing slow networks across the company for testing.&lt;/li&gt;
&lt;li&gt;Make average, low-spec devices with low
&lt;a href=&quot;https://web.dev/performance-poor-connectivity/&quot;&gt;bandwidth or high latencies&lt;/a&gt; available
to management and other stakeholders.&lt;/li&gt;
&lt;li&gt;Consider adding overlays showing performance metrics on your development
or staging servers. Connections to these servers from mobile can
potentially be throttled by default on corporate networks.&lt;/li&gt;
&lt;li&gt;Monitors can be placed strategically across the company showing videos
or timestrips of your website&#39;s loading behavior, preferably in comparison
to competitors. WebPageTest &lt;a href=&quot;https://www.webpagetest.org/video/&quot; rel=&quot;noopener&quot;&gt;can create
these&lt;/a&gt; very easily and automatically.&lt;/li&gt;
&lt;li&gt;Performance can also be fun—maybe a &lt;a href=&quot;https://g.co/perfgame&quot; rel=&quot;noopener&quot;&gt;game based on an individual Lighthouse report&lt;/a&gt; can reach audiences which can&#39;t be reached by pure reports and metrics?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;recap&quot;&gt;Recap &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-report-metrics/#recap&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This guide explains why the selection of metrics, and how they are reported and handled, is as important as the measurement and optimization itself—or even more. Make sure to prefer percentiles or distributions over averages, be cautious of using just bounce rate or relative conversion rate as impact measures, and make sure that metrics are easy to understand and tangible for stakeholders across the company. Establishing a performance culture is also an important step towards a well performing e-commerce site.&lt;/p&gt;
</content>
    <author>
      <name>Martin Schierle</name>
    </author>
  </entry>
</feed>
