<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://web.dev/</id>
  <title>Matt Gaunt on web.dev</title>
  <updated>2026-04-15T23:21:06Z</updated>
  <author>
    <name>Matt Gaunt</name>
  </author>
  <link href="https://web.dev/authors/mattgaunt/feed.xml" rel="self"/>
  <link href="https://web.dev/"/>
  <icon>https://web-dev.imgix.net/image/admin/yGv3dhfUhHvSpDA3bNYW.jpg?auto=format</icon>
  <logo>https://web.dev/images/shared/rss-banner.png</logo>
  <subtitle>Our latest news, updates, and stories by Matt Gaunt.</subtitle>
  
  
  <entry>
    <title>Push notifications overview</title>
    <link href="https://web.dev/push-notifications-overview/"/>
    <updated>2020-11-10T00:00:00Z</updated>
    <id>https://web.dev/push-notifications-overview/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;what&quot;&gt;What are push notifications? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-overview/#what&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Push messages enable you to bring information to the attention of your
users even when they&#39;re not using your website. They&#39;re called &lt;strong&gt;push&lt;/strong&gt;
messages because you can &amp;quot;push&amp;quot; information to your users even when they&#39;re
not active. Compare &lt;a href=&quot;https://en.wikipedia.org/wiki/Push_technology&quot; rel=&quot;noopener&quot;&gt;Push
technology&lt;/a&gt; with &lt;a href=&quot;https://en.wikipedia.org/wiki/Pull_technology&quot; rel=&quot;noopener&quot;&gt;Pull
technology&lt;/a&gt; to understand this
concept further.&lt;/p&gt;
&lt;p&gt;Notifications present small chunks of information to a user. Websites can use
notifications to tell users about important, time-sensitive events, or actions
the user needs to take. The look and feel of notifications varies between platforms:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Examples of notifications on macOS and Android.&quot; decoding=&quot;async&quot; height=&quot;361&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yC2ZZRuLFnnYGGmPUu9h.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yC2ZZRuLFnnYGGmPUu9h.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yC2ZZRuLFnnYGGmPUu9h.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yC2ZZRuLFnnYGGmPUu9h.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yC2ZZRuLFnnYGGmPUu9h.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yC2ZZRuLFnnYGGmPUu9h.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yC2ZZRuLFnnYGGmPUu9h.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yC2ZZRuLFnnYGGmPUu9h.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yC2ZZRuLFnnYGGmPUu9h.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yC2ZZRuLFnnYGGmPUu9h.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yC2ZZRuLFnnYGGmPUu9h.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yC2ZZRuLFnnYGGmPUu9h.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yC2ZZRuLFnnYGGmPUu9h.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yC2ZZRuLFnnYGGmPUu9h.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yC2ZZRuLFnnYGGmPUu9h.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yC2ZZRuLFnnYGGmPUu9h.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yC2ZZRuLFnnYGGmPUu9h.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yC2ZZRuLFnnYGGmPUu9h.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;Examples of notifications on macOS and Android.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Push messages and notifications are two separate but complementary technologies.
Push is the technology for sending messages from your server to users even when
they&#39;re not actively using your website. Notifications is the technology for
displaying the pushed information on the user&#39;s device. It&#39;s possible to use
notifications without push messaging. One day it may also be possible to use
push messages without user-facing notifications (&lt;strong&gt;silent push&lt;/strong&gt;) but browsers
currently don&#39;t allow that. In practice they&#39;re usually used together.
A non-technical user probably won&#39;t understand the difference between push
messages and notifications. In this collection when
we say &lt;strong&gt;push notifications&lt;/strong&gt; we mean the combination of pushing a message
followed by displaying it as a notification. When we say &lt;strong&gt;push messages&lt;/strong&gt;
we are referring to push technology on its own. And when we say &lt;strong&gt;notifications&lt;/strong&gt;
we&#39;re referring to notification technology on its own.&lt;/p&gt;
&lt;h2 id=&quot;why&quot;&gt;Why use push notifications? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-overview/#why&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;For users, push notifications are a way to receive &lt;strong&gt;timely&lt;/strong&gt;, &lt;strong&gt;relevant&lt;/strong&gt;,
and &lt;strong&gt;precise&lt;/strong&gt; information.&lt;/li&gt;
&lt;li&gt;For you (a website owner), push notifications are a way to increase user
engagement.&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;aside flow bg-state-warn-bg color-state-warn-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block color-state-warn-text&quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Warning sign&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M23 21L12 2 1 21h22zm-12-3v-2h2v2h-2zm0-4h2v-4h-2v4z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Warning&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; If you try to use push notifications for content that your users don&#39;t find timely, relevant, and precise, you&#39;ll probably end up annoying your users and reducing overall engagement. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;how&quot;&gt;How do push notifications work? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-overview/#how&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At a high-level, the key steps for implementing push notifications are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Adding client logic to ask the user for permission to send push notifications, and
then sending client identifier information to your server for storage in a database.&lt;/li&gt;
&lt;li&gt;Adding server logic to push messages to client devices.&lt;/li&gt;
&lt;li&gt;Adding client logic to receive messages that have been pushed to the device
and displaying them as notifications.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The rest of this page explains these steps in more detail.&lt;/p&gt;
&lt;h3 id=&quot;permission&quot;&gt;Get permission to send push notifications &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-overview/#permission&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;First, your website needs to get the user&#39;s permission to send push notifications.
This should be triggered by a user gesture, such as clicking a &lt;strong&gt;Yes&lt;/strong&gt; button
next to a &lt;code&gt;Do you want to receive push notifications?&lt;/code&gt; prompt. After that confirmation,
call &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Notification/requestPermission&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Notification.requestPermission()&lt;/code&gt;&lt;/a&gt;. The operating system or
browser on the user&#39;s device will probably present some kind of UI to formally confirm that the
user wants to opt in to push notifications. This UI varies across platforms.&lt;/p&gt;
&lt;h3 id=&quot;subscription&quot;&gt;Subscribe the client to push notifications &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-overview/#subscription&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;After you get permission, your website needs to initiate the process of
subscribing the user to push notifications. This is done through JavaScript,
using the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Push_API&quot; rel=&quot;noopener&quot;&gt;Push API&lt;/a&gt;. You&#39;ll need to provide a public authentication key
during the subscription process, which you&#39;ll learn more about later. After
you kick off the subscription process, the browser makes a network request
to a web service known as a push service, which you&#39;ll also learn more about later.&lt;/p&gt;
&lt;p&gt;Assuming that the subscription was successful, the browser returns a
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/PushSubscription&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;PushSubscription&lt;/code&gt;&lt;/a&gt;
object. You&#39;ll need to store this data long-term.
Usually this is done by sending the information to a server that you control,
and then having the server store it in a database.&lt;/p&gt;
&lt;img alt=&quot;Get permission to send push messages. Get PushSubscription. Send PushSubscription to your server.&quot; decoding=&quot;async&quot; height=&quot;213&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/F7gyVwzozw0eYkbTBChu.svg&quot; width=&quot;800&quot; /&gt;
&lt;h3 id=&quot;send&quot;&gt;Send a push message &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-overview/#send&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Your server doesn&#39;t actually send the push message directly to a client. A
&lt;strong&gt;push service&lt;/strong&gt; does that. A push service is a web service controlled by your
user&#39;s browser vendor. When you want to send a push notification to a client you need
to make a web service request to a push service. The web service request that
you send to the push service is known as a &lt;strong&gt;web push protocol request&lt;/strong&gt;. The
web push protocol request should include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What data to include in the message.&lt;/li&gt;
&lt;li&gt;What client to send the message to.&lt;/li&gt;
&lt;li&gt;Instructions on how the push service should deliver the message. For example, you
can specify that the push service should stop attempting to send the message
after 10 minutes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Normally you make the web push protocol request through a server that you control.
Of course, your server doesn&#39;t have to construct the raw web service request
itself. There are libraries that can handle that for you, such as the
&lt;a href=&quot;https://github.com/web-push-libs/&quot; rel=&quot;noopener&quot;&gt;web-push-libs&lt;/a&gt;. But the underlying mechanism is
a web service request over HTTP.&lt;/p&gt;
&lt;img alt=&quot;Your server sends a web push protocol request to the push service and the push service sends to the message to the user&amp;#x27;s device.&quot; decoding=&quot;async&quot; height=&quot;220&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/BD7hUXHhprQfUgWWGsMk.svg&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;The push service receives your request, authenticates it, and routes the push
message to the appropriate client. If the client&#39;s browser is offline, the push
service queues the push message until the browser comes online.&lt;/p&gt;
&lt;p&gt;Each browser uses whatever push service it wants. You as a website developer
have no control over that. This isn&#39;t a problem because the web push protocol
request is &lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-webpush-protocol&quot; rel=&quot;noopener&quot;&gt;standardized&lt;/a&gt;.
In other words, you don&#39;t have to care which push service the browser vendor is
using. You just need to make sure that your web push protocol request follows the spec.
Among other things, the spec states that the request must include certain headers
and the data must be sent as a stream of bytes.&lt;/p&gt;
&lt;p&gt;You do, however, need to make sure that you&#39;re sending the web push protocol
request to the correct push service. The &lt;code&gt;PushSubscription&lt;/code&gt; data that the
browser returned to you during the subscription process provides this
information. A &lt;code&gt;PushSubscription&lt;/code&gt; object looks like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;endpoint&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://fcm.googleapis.com/fcm/send/c1KrmpTuRm…&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;expirationTime&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token null keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;keys&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;p256dh&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;BGyyVt9FFV…&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;auth&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;R9sidzkcdf…&quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The domain of the &lt;code&gt;endpoint&lt;/code&gt; is essentially the push service. The path of the
&lt;code&gt;endpoint&lt;/code&gt; is client identifier information that helps the push service determine
exactly which client to push the message to.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;keys&lt;/code&gt; are used for encryption, which is explained next.&lt;/p&gt;
&lt;h4 id=&quot;encrypt&quot;&gt;Encrypt the push message &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-overview/#encrypt&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The data that you send to a push service must be encrypted. This prevents
the push service from being able to view the data you&#39;re sending to the client.
Remember that the browser vendor decides what push service to use, and that
push service could theoretically be unsafe or insecure. Your server must use
the &lt;code&gt;keys&lt;/code&gt; provided in the &lt;code&gt;PushSubscription&lt;/code&gt; to encrypt its web push protocol
requests.&lt;/p&gt;
&lt;h4 id=&quot;sign&quot;&gt;Sign your web push protocol requests &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-overview/#sign&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The push service provides a way to prevent anyone else from sending messages to your
users. Technically you don&#39;t have to do this but the easiest implementation on
Chrome requires it. It&#39;s optional on Firefox. Other browsers may require it
in the future.&lt;/p&gt;
&lt;p&gt;This workflow involves a private key and public key that are unique to your
application. The authentication process roughly works like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You generate the private and public key as a one-off task. The combination
of the private and public key is known as the
&lt;strong&gt;application server keys&lt;/strong&gt;. You might also see them called the &lt;strong&gt;VAPID
keys&lt;/strong&gt;. &lt;a href=&quot;https://tools.ietf.org/html/draft-thomson-webpush-vapid-02&quot; rel=&quot;noopener&quot;&gt;VAPID&lt;/a&gt; is
the spec that defines this authentication process.&lt;/li&gt;
&lt;li&gt;When you subscribe a client to push notifications from your JavaScript code,
you provide your public key. When the push service generates an &lt;code&gt;endpoint&lt;/code&gt;
for the device, it associates the provided public key with the &lt;code&gt;endpoint&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;When you send a web push protocol request, you sign some JSON information
with your private key.&lt;/li&gt;
&lt;li&gt;When the push service receives your web push protocol request, it uses the stored
public key to authenticate the signed information. If the signature is valid
then the push service knows that the request came from a server with the
matching private key.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;customize&quot;&gt;Customize the delivery of the push message &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-overview/#customize&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The web push protocol request spec also defines parameters that let you
customize how the push service attempts to send the push message to the client.
For example, you can customize:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Time-To-Live (TTL) of a message, which defines how long the push service should
attempt to deliver a message.&lt;/li&gt;
&lt;li&gt;The urgency of the message, which is useful in case the push service is preserving
the client&#39;s battery life by only delivering high-priority messages.&lt;/li&gt;
&lt;li&gt;The topic of a message, which replaces any pending messages of the same topic
with the latest message.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;notification&quot;&gt;Receive and display the pushed messages as notifications &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-overview/#notification&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Once you&#39;ve sent the web push protocol request to the push service, the push service keeps
your request queued until one of the following events happens:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The client comes online and the push service delivers the push message.&lt;/li&gt;
&lt;li&gt;The message expires.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When a client browser receives a pushed message, it decrypts the push message
data and dispatches a &lt;code&gt;push&lt;/code&gt; event to your &lt;a href=&quot;https://web.dev/service-workers-cache-storage/#service-workers&quot;&gt;service
worker&lt;/a&gt;. A service worker is
basically JavaScript code that can run in the background, even when your website
isn&#39;t open or the browser is closed. In your service worker&#39;s &lt;code&gt;push&lt;/code&gt; event
handler you call &lt;code&gt;ServiceWorkerRegistration.showNotification()&lt;/code&gt; to display the information
as a notification.&lt;/p&gt;
&lt;img alt=&quot;Message arrives on device. Browser wakes up service worker. Push event is dispatched.&quot; decoding=&quot;async&quot; height=&quot;238&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/2pLZ4T0vVrG3nqitaAeH.svg&quot; width=&quot;800&quot; /&gt;
&lt;h2 id=&quot;where-to-go-next&quot;&gt;Where to go next &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-overview/#where-to-go-next&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Web Push Notification Overview&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-how-push-works/&quot;&gt;How Push Works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-subscribing-a-user/&quot;&gt;Subscribing a User&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-permissions-ux/&quot;&gt;Permission UX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sending-messages-with-web-push-libraries/&quot;&gt;Sending Messages with Web Push Libraries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-web-push-protocol/&quot;&gt;Web Push Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-handling-messages/&quot;&gt;Handling Push Events&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-display-a-notification/&quot;&gt;Displaying a Notification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-notification-behaviour/&quot;&gt;Notification Behavior&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-common-notification-patterns/&quot;&gt;Common Notification Patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-faq/&quot;&gt;Push Notifications FAQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/&quot;&gt;Common Issues and Reporting Bugs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;code-labs&quot;&gt;Code labs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-overview/#code-labs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-client-codelab/&quot;&gt;Build a push notification client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-server-codelab/&quot;&gt;Build a push notification server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Kayce Basques</name>
    </author><author>
      <name>Matt Gaunt</name>
    </author>
  </entry>
  
  <entry>
    <title>Common Issues and Reporting Bugs</title>
    <link href="https://web.dev/push-notifications-common-issues-and-reporting-bugs/"/>
    <updated>2017-03-30T00:00:00Z</updated>
    <id>https://web.dev/push-notifications-common-issues-and-reporting-bugs/</id>
    <content type="html" mode="escaped">&lt;p&gt;When you hit an issue with web push, it can be difficult to debug the issue or
find help. This doc outlines some of the common issues and what you should
do if you&#39;ve found a bug in Chrome or Firefox.&lt;/p&gt;
&lt;p&gt;Before we dive into debugging push, you may be hitting issues with debugging
service workers themselves, the file not updating, failing to register or
generally just unusual behavior. There is an
&lt;a href=&quot;https://web.dev/web/fundamentals/getting-started/codelabs/debugging-service-workers/&quot;&gt;awesome document on debugging service workers&lt;/a&gt;
that I strongly recommend checking out if you are new to
service worker development.&lt;/p&gt;
&lt;p&gt;There are two distinct stages to check off when developing and testing web push,
each with its own set of common issues / problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sending a Message:&lt;/strong&gt; Make sure that sending messages is successful.
You should be getting a 201 HTTP code. If you aren&#39;t :
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Check for Authorization Errors:&lt;/strong&gt; If you receive an authorization
error message see the
&lt;a href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/#authorization-issues&quot;&gt;Authorization Issues section&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Other API Errors:&lt;/strong&gt; If you receive a non-201 status code response,
see the &lt;a href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/#http-status-codes&quot;&gt;HTTP Status Codes section&lt;/a&gt; for
guidance on the cause of the issue.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Receiving a Message&lt;/strong&gt;: If you&#39;re able to send a message successfully,
but the message is not received on the browser:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Check for Encryption Issues:&lt;/strong&gt; See the &lt;a href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/#payload-encryption-issue&quot;&gt;Payload Encryption
Issue Section&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check for Connection Issues:&lt;/strong&gt; If the problem is on Chrome, it
may be a connection. See &lt;a href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/#connection-issue&quot;&gt;Connection Issues section&lt;/a&gt;
for more info.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you aren&#39;t able to send and receive a push message and the relevant sections
in this doc aren&#39;t helping debug the problem then you may have found a
bug in the push mechanism itself. In this case, refer to the
&lt;a href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/#raising-bug-reports&quot;&gt;Raising Bug Reports&lt;/a&gt;
section to file a good bug report with all the necessary information to expedite
the bug fixing process.&lt;/p&gt;
&lt;p&gt;One thing I&#39;d like to call out before we start is that &lt;strong&gt;Firefox and the
Mozilla AutoPush Service have great error messages.&lt;/strong&gt; If you get stuck and
are not sure what the problem is, then test in Firefox and see if you
get a more helpful error message.&lt;/p&gt;
&lt;h2 id=&quot;authorization-issues&quot;&gt;Authorization issues &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/#authorization-issues&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Authorization issues are one of the most common issues developers hit when
starting out with web push. This is normally a problem with the configuration of a
site&#39;s &lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-webpush-vapid-02&quot; rel=&quot;noopener&quot;&gt;Application Server Keys (a.k.a. VAPID keys)
&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The easiest way to support push in both Firefox and Chrome is to supply an
&lt;code&gt;applicationServerKey&lt;/code&gt; in the &lt;code&gt;subscribe()&lt;/code&gt; call. The down side is that
any discrepancy between your front end and server&#39;s keys will result in an
authorization error.&lt;/p&gt;
&lt;h3 id=&quot;on-chrome-and-fcm&quot;&gt;On Chrome and FCM &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/#on-chrome-and-fcm&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For Chrome, which uses FCM as a push service, you&#39;ll receive an
&lt;code&gt;UnauthorizedRegistration&lt;/code&gt; response from FCM for a range of different
errors, all involving the application server keys.&lt;/p&gt;
&lt;p&gt;You&#39;ll receive an &lt;code&gt;UnauthorizedRegistration&lt;/code&gt; error in any of the following
situations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you fail to define an &lt;code&gt;Authorization&lt;/code&gt; header in the request to FCM.&lt;/li&gt;
&lt;li&gt;Your application key used to subscribe the user doesn&#39;t match the key used
to sign the Authorization header.&lt;/li&gt;
&lt;li&gt;The expiration is invalid in your JWT, i.e. the expiration exceeds 24 hours or
the JWT has expired.&lt;/li&gt;
&lt;li&gt;The JWT is malformed or has invalid values.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The full error response looks like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;UnauthorizedRegistration&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;bgcolor&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#FFFFFF&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#000000&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;UnauthorizedRegistration&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Error 400&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If you receive this error message in Chrome, consider testing in Firefox to see
if it&#39;ll provide more insight to the problem.&lt;/p&gt;
&lt;h3 id=&quot;firefox-and-mozilla-autopush&quot;&gt;Firefox and Mozilla AutoPush &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/#firefox-and-mozilla-autopush&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Firefox and Mozilla AutoPush provide a friendly set of error messages for
&lt;code&gt;Authorization&lt;/code&gt; issues.&lt;/p&gt;
&lt;p&gt;You&#39;ll also receive an &lt;code&gt;Unauthorized&lt;/code&gt; error response from
Mozilla AutoPush if the &lt;code&gt;Authorization&lt;/code&gt; header is not included in your push
request.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;errno&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;109&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Request did not validate missing authorization header&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;code&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;401&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;more_info&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://autopush.readthedocs.io/en/latest/http.html#error-codes&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;error&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Unauthorized&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If the expiration in your JWT has expired, you&#39;ll also receive an
&lt;code&gt;Unauthorized&lt;/code&gt; error with a message that explains that the token has
expired.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;code&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;401&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;errno&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;109&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;error&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Unauthorized&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;more_info&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://autopush.readthedocs.io/en/latest/http.html#error-codes&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Request did not validate Invalid bearer token: Auth expired&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If the application server keys are different between when the user was
subscribed and when the Authorization header was signed, a &lt;code&gt;Not Found&lt;/code&gt;
error will be returned:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;errno&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;102&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Request did not validate invalid token&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;code&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;more_info&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://autopush.readthedocs.io/en/latest/http.html#error-codes&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;error&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Not Found&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Lastly, if you have an invalid value in your JWT (for example if the &amp;quot;alg&amp;quot; value
is an unexpected value), you&#39;ll receive the following error from Mozilla
AutoPush:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;code&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;401&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;errno&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;109&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;error&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Unauthorized&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;more_info&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://autopush.readthedocs.io/en/latest/http.html#error-codes&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Request did not validate Invalid Authorization Header&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;http-status-codes&quot;&gt;HTTP status codes &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/#http-status-codes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are a range of issues that can result in a non-201 response code from a
push service. Below is a list of HTTP status codes and what they mean in relation
to web push.&lt;/p&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;th&gt;Status Code&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;429&lt;/td&gt;
&lt;td&gt;Too many requests. Your application server has reached a rate limit with a
push service. The response from the service should include a &#39;Retry-After&#39; header to
indicate how long before another request can be made.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;400&lt;/td&gt;
&lt;td&gt;Invalid request. One of your headers is invalid or
poorly formatted.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;404&lt;/td&gt;
&lt;td&gt;Not Found. In this case you should delete the PushSubscription from your
back end and wait for an opportunity to resubscribe the user.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;410&lt;/td&gt;
&lt;td&gt;Gone. The subscription is no longer valid and should be removed from your
back end. This can be reproduced by calling `unsubscribe()` on a
`PushSubscription`.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;413&lt;/td&gt;
&lt;td&gt;Payload size too large. The minimum size payload a push service must
support is 4096 bytes (or 4kb). Anything larger can result in this error.&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;If the HTTP status code is not in this list and the error message is not
helpful, check the &lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-webpush-protocol&quot; rel=&quot;noopener&quot;&gt;Web Push Protocol
spec&lt;/a&gt; to see if the
status code is referenced along with a scenario of when that status code can
be used.&lt;/p&gt;
&lt;h2 id=&quot;payload-encryption-issue&quot;&gt;Payload encryption issue &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/#payload-encryption-issue&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you can successfully trigger a push message (i.e. send a message to a web
push service and receive a 201 response code) but the push event never fires in
your service worker, this normally indicates that the browser failed to
decrypt the message it received.&lt;/p&gt;
&lt;p&gt;If this is the case, you should see an error message in Firefox&#39;s DevTools
console like so:&lt;/p&gt;
&lt;img alt=&quot;Firefox DevTools with decryption message.&quot; decoding=&quot;async&quot; height=&quot;269&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/g8VpxsP7PMWPSdtSvAkY.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/g8VpxsP7PMWPSdtSvAkY.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/g8VpxsP7PMWPSdtSvAkY.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/g8VpxsP7PMWPSdtSvAkY.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/g8VpxsP7PMWPSdtSvAkY.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/g8VpxsP7PMWPSdtSvAkY.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/g8VpxsP7PMWPSdtSvAkY.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/g8VpxsP7PMWPSdtSvAkY.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/g8VpxsP7PMWPSdtSvAkY.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/g8VpxsP7PMWPSdtSvAkY.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/g8VpxsP7PMWPSdtSvAkY.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/g8VpxsP7PMWPSdtSvAkY.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/g8VpxsP7PMWPSdtSvAkY.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/g8VpxsP7PMWPSdtSvAkY.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/g8VpxsP7PMWPSdtSvAkY.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/g8VpxsP7PMWPSdtSvAkY.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/g8VpxsP7PMWPSdtSvAkY.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/g8VpxsP7PMWPSdtSvAkY.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;To check if this is the issue in Chrome, do the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go to about://gcm-internals and click the &amp;quot;Start Recording&amp;quot; button.&lt;/li&gt;
&lt;/ol&gt;
&lt;img alt=&quot;Chrome GCM internals record.&quot; decoding=&quot;async&quot; height=&quot;226&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VH4juz336de2yiInKjUO.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VH4juz336de2yiInKjUO.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VH4juz336de2yiInKjUO.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VH4juz336de2yiInKjUO.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VH4juz336de2yiInKjUO.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VH4juz336de2yiInKjUO.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VH4juz336de2yiInKjUO.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VH4juz336de2yiInKjUO.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VH4juz336de2yiInKjUO.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VH4juz336de2yiInKjUO.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VH4juz336de2yiInKjUO.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VH4juz336de2yiInKjUO.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VH4juz336de2yiInKjUO.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VH4juz336de2yiInKjUO.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VH4juz336de2yiInKjUO.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VH4juz336de2yiInKjUO.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VH4juz336de2yiInKjUO.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VH4juz336de2yiInKjUO.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;ol&gt;
&lt;li&gt;Trigger a push message, and look under the &amp;quot;Message Decryption Failure Log&amp;quot;.&lt;/li&gt;
&lt;/ol&gt;
&lt;img alt=&quot;GCM internals decryption log.&quot; decoding=&quot;async&quot; height=&quot;155&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DszP9aaiMTmX5LdTXKPx.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DszP9aaiMTmX5LdTXKPx.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DszP9aaiMTmX5LdTXKPx.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DszP9aaiMTmX5LdTXKPx.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DszP9aaiMTmX5LdTXKPx.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DszP9aaiMTmX5LdTXKPx.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DszP9aaiMTmX5LdTXKPx.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DszP9aaiMTmX5LdTXKPx.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DszP9aaiMTmX5LdTXKPx.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DszP9aaiMTmX5LdTXKPx.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DszP9aaiMTmX5LdTXKPx.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DszP9aaiMTmX5LdTXKPx.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DszP9aaiMTmX5LdTXKPx.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DszP9aaiMTmX5LdTXKPx.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DszP9aaiMTmX5LdTXKPx.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DszP9aaiMTmX5LdTXKPx.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DszP9aaiMTmX5LdTXKPx.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DszP9aaiMTmX5LdTXKPx.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;If there is an issue with the decryption of the payload, you&#39;ll see an error
similar to the one displayed above. (Notice the &lt;code&gt;AES-GCM decryption failed&lt;/code&gt;
message in the details column.)&lt;/p&gt;
&lt;p&gt;There are a few tools which may help debug encryption if this is your issue:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tests.peter.sh/push-encryption-verifier/&quot; rel=&quot;noopener&quot;&gt;Push Encryption Verifier tool by Peter Beverloo&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mozilla-services.github.io/WebPushDataTestPage/&quot; rel=&quot;noopener&quot;&gt;Web Push: Data Encryption Test Page by Mozilla&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;connection-issue&quot;&gt;Connection issue &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/#connection-issue&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you aren&#39;t receiving a push event in your service worker and you aren&#39;t
seeing any decryption errors, then the browser may be failing to connect to
a push service.&lt;/p&gt;
&lt;p&gt;In Chrome, you can check whether the browser is receiving messages by examining
the &#39;Receive Message Log&#39; (sic) in &lt;code&gt;about://gcm-internals&lt;/code&gt;.&lt;/p&gt;
&lt;img alt=&quot;GCM internals receive message log.&quot; decoding=&quot;async&quot; height=&quot;117&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/vRKzAO05KD5vUpQ6nL3z.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/vRKzAO05KD5vUpQ6nL3z.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/vRKzAO05KD5vUpQ6nL3z.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/vRKzAO05KD5vUpQ6nL3z.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/vRKzAO05KD5vUpQ6nL3z.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/vRKzAO05KD5vUpQ6nL3z.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/vRKzAO05KD5vUpQ6nL3z.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/vRKzAO05KD5vUpQ6nL3z.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/vRKzAO05KD5vUpQ6nL3z.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/vRKzAO05KD5vUpQ6nL3z.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/vRKzAO05KD5vUpQ6nL3z.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/vRKzAO05KD5vUpQ6nL3z.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/vRKzAO05KD5vUpQ6nL3z.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/vRKzAO05KD5vUpQ6nL3z.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/vRKzAO05KD5vUpQ6nL3z.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/vRKzAO05KD5vUpQ6nL3z.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/vRKzAO05KD5vUpQ6nL3z.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/vRKzAO05KD5vUpQ6nL3z.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;If you aren&#39;t seeing the message arrive in a timely fashion, then make sure that
the connection status of your browser is &lt;code&gt;CONNECTED&lt;/code&gt;:&lt;/p&gt;
&lt;img alt=&quot;GCM internals connection state.&quot; decoding=&quot;async&quot; height=&quot;246&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 584px) 584px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/YdYj19Kn0zl6Jlfbm9hg.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/YdYj19Kn0zl6Jlfbm9hg.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/YdYj19Kn0zl6Jlfbm9hg.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/YdYj19Kn0zl6Jlfbm9hg.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/YdYj19Kn0zl6Jlfbm9hg.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/YdYj19Kn0zl6Jlfbm9hg.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/YdYj19Kn0zl6Jlfbm9hg.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/YdYj19Kn0zl6Jlfbm9hg.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/YdYj19Kn0zl6Jlfbm9hg.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/YdYj19Kn0zl6Jlfbm9hg.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/YdYj19Kn0zl6Jlfbm9hg.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/YdYj19Kn0zl6Jlfbm9hg.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/YdYj19Kn0zl6Jlfbm9hg.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/YdYj19Kn0zl6Jlfbm9hg.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/YdYj19Kn0zl6Jlfbm9hg.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/YdYj19Kn0zl6Jlfbm9hg.png?auto=format&amp;w=1168 1168w&quot; width=&quot;584&quot; /&gt;
&lt;p&gt;If it&#39;s &lt;strong&gt;not&lt;/strong&gt; &#39;CONNECTED&#39;, you may need to delete your current profile and
&lt;a href=&quot;https://support.google.com/chrome/answer/2364824&quot; rel=&quot;noopener&quot;&gt;create a new one&lt;/a&gt;. If that
still doesn&#39;t solve the issue, please raise a bug report as suggested below.&lt;/p&gt;
&lt;h2 id=&quot;raising-bug-reports&quot;&gt;Raising bug reports &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/#raising-bug-reports&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If none of the above helps with your issue and there is no sign of what the
problem could be, please raise an issue against the browser you are having an
issue with:&lt;/p&gt;
&lt;p&gt;For Chrome, you&#39;d raise the issue here:
&lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/list&quot; rel=&quot;noopener&quot;&gt;https://bugs.chromium.org/p/chromium/issues/list&lt;/a&gt;
For Firefox, you should raise the issue on:
&lt;a href=&quot;https://bugzilla.mozilla.org/&quot; rel=&quot;noopener&quot;&gt;https://bugzilla.mozilla.org/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To provide a good bug report, you should provide the following details:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Browsers you&#39;ve tested in (e.g. Chrome version 50, Chrome version 51, Firefox
version 50, Firefox version 51).&lt;/li&gt;
&lt;li&gt;An example &lt;code&gt;PushSubscription&lt;/code&gt; that demonstrates the problem.&lt;/li&gt;
&lt;li&gt;Include any example requests (i.e. content of network requests to a push
service, including headers).&lt;/li&gt;
&lt;li&gt;Include any example responses from network requests as well.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you can provide a reproducible example, either source code or a hosted web
site, it often speeds up diagnosing and solving the problem.&lt;/p&gt;
&lt;h2 id=&quot;where-to-go-next&quot;&gt;Where to go next &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/#where-to-go-next&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-overview/&quot;&gt;Web Push Notification Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-how-push-works/&quot;&gt;How Push Works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-subscribing-a-user/&quot;&gt;Subscribing a User&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-permissions-ux/&quot;&gt;Permission UX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sending-messages-with-web-push-libraries/&quot;&gt;Sending Messages with Web Push Libraries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-web-push-protocol/&quot;&gt;Web Push Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-handling-messages/&quot;&gt;Handling Push Events&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-display-a-notification/&quot;&gt;Displaying a Notification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-notification-behaviour/&quot;&gt;Notification Behavior&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-common-notification-patterns/&quot;&gt;Common Notification Patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-faq/&quot;&gt;Push Notifications FAQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Common Issues and Reporting Bugs&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;code-labs&quot;&gt;Code labs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/#code-labs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-client-codelab/&quot;&gt;Build a push notification client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-server-codelab/&quot;&gt;Build a push notification server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Matt Gaunt</name>
    </author>
  </entry>
  
  <entry>
    <title>Displaying a Notification</title>
    <link href="https://web.dev/push-notifications-display-a-notification/"/>
    <updated>2016-06-30T00:00:00Z</updated>
    <id>https://web.dev/push-notifications-display-a-notification/</id>
    <content type="html" mode="escaped">&lt;p&gt;The notification options is split into two sections, one that deals with the visual aspects (this
section) and one that explains the behavioral aspects of notifications (the next section).&lt;/p&gt;
&lt;p&gt;You can play around with various notification options in various browsers on various platforms
using &lt;a href=&quot;https://twitter.com/beverloo&quot; rel=&quot;noopener&quot;&gt;Peter Beverloo&lt;/a&gt;&#39;s
&lt;a href=&quot;https://tests.peter.sh/notification-generator/&quot; rel=&quot;noopener&quot;&gt;Notification Generator&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;visual-options&quot;&gt;Visual Options &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-display-a-notification/#visual-options&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The API for showing a notification is simple:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ServiceWorkerRegistration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;.showNotification(&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;options&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Both arguments, &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;options&lt;/code&gt; are optional.&lt;/p&gt;
&lt;p&gt;The title is a string and options can be any of the following:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;//&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Visual Options&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;body&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;String&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;icon&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;URL String&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;image&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;URL String&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;badge&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;URL String&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;dir&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;String of &#39;auto&#39; | &#39;ltr&#39; | &#39;rtl&#39;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;timestamp&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;Long&gt;&quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;//&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Both visual &amp;amp; behavioral options&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;actions&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;Array of Strings&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;data&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;Anything&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;//&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Behavioral Options&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;tag&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;String&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;requireInteraction&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;boolean&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;renotify&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;Boolean&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;vibrate&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;Array of Integers&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;sound&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;URL String&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;silent&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;Boolean&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Let&#39;s look at the visual options:&lt;/p&gt;
&lt;img alt=&quot;Dissection of the UI of a Notification.&quot; decoding=&quot;async&quot; height=&quot;284&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ZVIQcUqIlNerdPiqOM9C.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ZVIQcUqIlNerdPiqOM9C.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ZVIQcUqIlNerdPiqOM9C.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ZVIQcUqIlNerdPiqOM9C.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ZVIQcUqIlNerdPiqOM9C.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ZVIQcUqIlNerdPiqOM9C.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ZVIQcUqIlNerdPiqOM9C.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ZVIQcUqIlNerdPiqOM9C.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ZVIQcUqIlNerdPiqOM9C.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ZVIQcUqIlNerdPiqOM9C.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ZVIQcUqIlNerdPiqOM9C.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ZVIQcUqIlNerdPiqOM9C.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ZVIQcUqIlNerdPiqOM9C.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ZVIQcUqIlNerdPiqOM9C.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ZVIQcUqIlNerdPiqOM9C.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ZVIQcUqIlNerdPiqOM9C.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ZVIQcUqIlNerdPiqOM9C.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ZVIQcUqIlNerdPiqOM9C.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h3 id=&quot;title-and-body-options&quot;&gt;Title and body options &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-display-a-notification/#title-and-body-options&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This is what a notification looks like without the title and options in Chrome on Windows:&lt;/p&gt;
&lt;img alt=&quot;Notification without the title and options in Chrome on Windows.&quot; decoding=&quot;async&quot; height=&quot;312&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 780px) 780px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/oq8F8nrC9z8aY3CtC8pr.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/oq8F8nrC9z8aY3CtC8pr.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/oq8F8nrC9z8aY3CtC8pr.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/oq8F8nrC9z8aY3CtC8pr.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/oq8F8nrC9z8aY3CtC8pr.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/oq8F8nrC9z8aY3CtC8pr.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/oq8F8nrC9z8aY3CtC8pr.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/oq8F8nrC9z8aY3CtC8pr.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/oq8F8nrC9z8aY3CtC8pr.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/oq8F8nrC9z8aY3CtC8pr.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/oq8F8nrC9z8aY3CtC8pr.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/oq8F8nrC9z8aY3CtC8pr.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/oq8F8nrC9z8aY3CtC8pr.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/oq8F8nrC9z8aY3CtC8pr.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/oq8F8nrC9z8aY3CtC8pr.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/oq8F8nrC9z8aY3CtC8pr.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/oq8F8nrC9z8aY3CtC8pr.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/oq8F8nrC9z8aY3CtC8pr.png?auto=format&amp;w=1560 1560w&quot; width=&quot;780&quot; /&gt;
&lt;p&gt;As you can see, the browser name is used as the title and the &amp;quot;New notification&amp;quot; placeholder is
used as the notification body.&lt;/p&gt;
&lt;p&gt;If a progressive web application is installed on the device, the web app name will be used instead
of the browser name:&lt;/p&gt;
&lt;img alt=&quot;Notification with the web app name instead of the browser name.&quot; decoding=&quot;async&quot; height=&quot;312&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 780px) 780px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/5TmavxZQDURHIHQnwScW.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/5TmavxZQDURHIHQnwScW.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/5TmavxZQDURHIHQnwScW.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/5TmavxZQDURHIHQnwScW.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/5TmavxZQDURHIHQnwScW.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/5TmavxZQDURHIHQnwScW.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/5TmavxZQDURHIHQnwScW.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/5TmavxZQDURHIHQnwScW.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/5TmavxZQDURHIHQnwScW.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/5TmavxZQDURHIHQnwScW.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/5TmavxZQDURHIHQnwScW.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/5TmavxZQDURHIHQnwScW.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/5TmavxZQDURHIHQnwScW.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/5TmavxZQDURHIHQnwScW.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/5TmavxZQDURHIHQnwScW.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/5TmavxZQDURHIHQnwScW.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/5TmavxZQDURHIHQnwScW.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/5TmavxZQDURHIHQnwScW.png?auto=format&amp;w=1560 1560w&quot; width=&quot;780&quot; /&gt;
&lt;p&gt;If we ran the following code:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Simple Title&#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; options &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;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Simple piece of body text.\nSecond line of body text :)&#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;br /&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;we&#39;d get this notification in Chrome on Linux:&lt;/p&gt;
&lt;img alt=&quot;Notification with title and body text in Chrome on Linux.&quot; decoding=&quot;async&quot; height=&quot;117&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 380px) 380px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BZnd4sFC3XMq8yPUkYVz.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BZnd4sFC3XMq8yPUkYVz.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BZnd4sFC3XMq8yPUkYVz.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BZnd4sFC3XMq8yPUkYVz.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BZnd4sFC3XMq8yPUkYVz.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BZnd4sFC3XMq8yPUkYVz.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BZnd4sFC3XMq8yPUkYVz.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BZnd4sFC3XMq8yPUkYVz.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BZnd4sFC3XMq8yPUkYVz.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BZnd4sFC3XMq8yPUkYVz.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BZnd4sFC3XMq8yPUkYVz.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BZnd4sFC3XMq8yPUkYVz.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/BZnd4sFC3XMq8yPUkYVz.png?auto=format&amp;w=760 760w&quot; width=&quot;380&quot; /&gt;
&lt;p&gt;In Firefox on Linux it would look like this:&lt;/p&gt;
&lt;img alt=&quot;Notification with title and body text in Firefox on Linux.&quot; decoding=&quot;async&quot; height=&quot;74&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 521px) 521px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/53pAkj17dCQhnODMk2jq.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/53pAkj17dCQhnODMk2jq.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/53pAkj17dCQhnODMk2jq.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/53pAkj17dCQhnODMk2jq.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/53pAkj17dCQhnODMk2jq.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/53pAkj17dCQhnODMk2jq.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/53pAkj17dCQhnODMk2jq.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/53pAkj17dCQhnODMk2jq.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/53pAkj17dCQhnODMk2jq.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/53pAkj17dCQhnODMk2jq.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/53pAkj17dCQhnODMk2jq.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/53pAkj17dCQhnODMk2jq.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/53pAkj17dCQhnODMk2jq.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/53pAkj17dCQhnODMk2jq.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/53pAkj17dCQhnODMk2jq.png?auto=format&amp;w=1042 1042w&quot; width=&quot;521&quot; /&gt;
&lt;p&gt;This is what the notification with a lot of text in the title and body looks like in Chrome on
Linux:&lt;/p&gt;
&lt;img alt=&quot;Notification with long title and body text in Chrome on Linux.&quot; decoding=&quot;async&quot; height=&quot;156&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 380px) 380px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/MjZfnuD9h6MJLjv9enVi.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/MjZfnuD9h6MJLjv9enVi.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/MjZfnuD9h6MJLjv9enVi.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/MjZfnuD9h6MJLjv9enVi.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/MjZfnuD9h6MJLjv9enVi.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/MjZfnuD9h6MJLjv9enVi.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/MjZfnuD9h6MJLjv9enVi.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/MjZfnuD9h6MJLjv9enVi.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/MjZfnuD9h6MJLjv9enVi.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/MjZfnuD9h6MJLjv9enVi.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/MjZfnuD9h6MJLjv9enVi.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/MjZfnuD9h6MJLjv9enVi.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/MjZfnuD9h6MJLjv9enVi.png?auto=format&amp;w=760 760w&quot; width=&quot;380&quot; /&gt;
&lt;p&gt;Firefox on Linux collapses the body text until you hover the notification, causing
the notification to expand:&lt;/p&gt;
&lt;img alt=&quot;Notification with long title and body text in Firefox on Linux.&quot; decoding=&quot;async&quot; height=&quot;74&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 521px) 521px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/hyJWXo3xO7AlrlE3I61F.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/hyJWXo3xO7AlrlE3I61F.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/hyJWXo3xO7AlrlE3I61F.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/hyJWXo3xO7AlrlE3I61F.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/hyJWXo3xO7AlrlE3I61F.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/hyJWXo3xO7AlrlE3I61F.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/hyJWXo3xO7AlrlE3I61F.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/hyJWXo3xO7AlrlE3I61F.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/hyJWXo3xO7AlrlE3I61F.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/hyJWXo3xO7AlrlE3I61F.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/hyJWXo3xO7AlrlE3I61F.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/hyJWXo3xO7AlrlE3I61F.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/hyJWXo3xO7AlrlE3I61F.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/hyJWXo3xO7AlrlE3I61F.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/hyJWXo3xO7AlrlE3I61F.png?auto=format&amp;w=1042 1042w&quot; width=&quot;521&quot; /&gt;
&lt;img alt=&quot;Notification with long title and body text in Firefox on Linux while hovering over the notification with the mouse cursor.&quot; decoding=&quot;async&quot; height=&quot;159&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 521px) 521px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Huyo4xr85SwcmPN9zSly.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Huyo4xr85SwcmPN9zSly.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Huyo4xr85SwcmPN9zSly.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Huyo4xr85SwcmPN9zSly.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Huyo4xr85SwcmPN9zSly.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Huyo4xr85SwcmPN9zSly.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Huyo4xr85SwcmPN9zSly.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Huyo4xr85SwcmPN9zSly.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Huyo4xr85SwcmPN9zSly.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Huyo4xr85SwcmPN9zSly.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Huyo4xr85SwcmPN9zSly.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Huyo4xr85SwcmPN9zSly.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Huyo4xr85SwcmPN9zSly.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Huyo4xr85SwcmPN9zSly.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Huyo4xr85SwcmPN9zSly.png?auto=format&amp;w=1042 1042w&quot; width=&quot;521&quot; /&gt;
&lt;p&gt;The same notifications in Firefox on Windows look like this:&lt;/p&gt;
&lt;img alt=&quot;Notification with title and body text in Firefox on Windows.&quot; decoding=&quot;async&quot; height=&quot;232&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 562px) 562px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/l5Pi7IvjKtGnn6YK2NdS.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/l5Pi7IvjKtGnn6YK2NdS.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/l5Pi7IvjKtGnn6YK2NdS.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/l5Pi7IvjKtGnn6YK2NdS.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/l5Pi7IvjKtGnn6YK2NdS.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/l5Pi7IvjKtGnn6YK2NdS.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/l5Pi7IvjKtGnn6YK2NdS.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/l5Pi7IvjKtGnn6YK2NdS.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/l5Pi7IvjKtGnn6YK2NdS.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/l5Pi7IvjKtGnn6YK2NdS.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/l5Pi7IvjKtGnn6YK2NdS.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/l5Pi7IvjKtGnn6YK2NdS.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/l5Pi7IvjKtGnn6YK2NdS.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/l5Pi7IvjKtGnn6YK2NdS.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/l5Pi7IvjKtGnn6YK2NdS.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/l5Pi7IvjKtGnn6YK2NdS.png?auto=format&amp;w=1124 1124w&quot; width=&quot;562&quot; /&gt;
&lt;img alt=&quot;Notification with long title and body text in Firefox on Windows.&quot; decoding=&quot;async&quot; height=&quot;281&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 562px) 562px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OQRchMO68ph6UV2Lovy0.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OQRchMO68ph6UV2Lovy0.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OQRchMO68ph6UV2Lovy0.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OQRchMO68ph6UV2Lovy0.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OQRchMO68ph6UV2Lovy0.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OQRchMO68ph6UV2Lovy0.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OQRchMO68ph6UV2Lovy0.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OQRchMO68ph6UV2Lovy0.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OQRchMO68ph6UV2Lovy0.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OQRchMO68ph6UV2Lovy0.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OQRchMO68ph6UV2Lovy0.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OQRchMO68ph6UV2Lovy0.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OQRchMO68ph6UV2Lovy0.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OQRchMO68ph6UV2Lovy0.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OQRchMO68ph6UV2Lovy0.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/OQRchMO68ph6UV2Lovy0.png?auto=format&amp;w=1124 1124w&quot; width=&quot;562&quot; /&gt;
&lt;p&gt;As you can see, the same notification may look different in different browsers. It may also look
different in the same browser on different platforms.&lt;/p&gt;
&lt;p&gt;Chrome and Firefox use the system notifications and notification center on platforms where these
are available.&lt;/p&gt;
&lt;p&gt;For example, system notifications on macOS don&#39;t support images and actions (buttons and inline
replies).&lt;/p&gt;
&lt;p&gt;Chrome also has a custom notifications for all desktop platforms. You can enable it by setting the
&lt;code&gt;chrome://flags/#enable-system-notifications&lt;/code&gt; flag to the &lt;code&gt;Disabled&lt;/code&gt; state.&lt;/p&gt;
&lt;h3 id=&quot;icon&quot;&gt;Icon &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-display-a-notification/#icon&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;icon&lt;/code&gt; option is essentially a small image you can show next to the title and body text.&lt;/p&gt;
&lt;p&gt;In your code you need to provide a URL to the image you&#39;d like to load:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Icon Notification&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/icon-512x512.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You get this notification in Chrome on Linux:&lt;/p&gt;
&lt;img alt=&quot;Notification with icon in Chrome on Linux.&quot; decoding=&quot;async&quot; height=&quot;100&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 380px) 380px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/RBQH1QGLIRT9HxJTDjkj.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/RBQH1QGLIRT9HxJTDjkj.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/RBQH1QGLIRT9HxJTDjkj.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/RBQH1QGLIRT9HxJTDjkj.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/RBQH1QGLIRT9HxJTDjkj.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/RBQH1QGLIRT9HxJTDjkj.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/RBQH1QGLIRT9HxJTDjkj.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/RBQH1QGLIRT9HxJTDjkj.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/RBQH1QGLIRT9HxJTDjkj.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/RBQH1QGLIRT9HxJTDjkj.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/RBQH1QGLIRT9HxJTDjkj.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/RBQH1QGLIRT9HxJTDjkj.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/RBQH1QGLIRT9HxJTDjkj.png?auto=format&amp;w=760 760w&quot; width=&quot;380&quot; /&gt;
&lt;p&gt;and in Firefox on Linux:&lt;/p&gt;
&lt;img alt=&quot;Notification with icon in Firefox on Linux.&quot; decoding=&quot;async&quot; height=&quot;75&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 522px) 522px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/EoS5ydulQRjq1nxTJxrA.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/EoS5ydulQRjq1nxTJxrA.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/EoS5ydulQRjq1nxTJxrA.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/EoS5ydulQRjq1nxTJxrA.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/EoS5ydulQRjq1nxTJxrA.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/EoS5ydulQRjq1nxTJxrA.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/EoS5ydulQRjq1nxTJxrA.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/EoS5ydulQRjq1nxTJxrA.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/EoS5ydulQRjq1nxTJxrA.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/EoS5ydulQRjq1nxTJxrA.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/EoS5ydulQRjq1nxTJxrA.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/EoS5ydulQRjq1nxTJxrA.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/EoS5ydulQRjq1nxTJxrA.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/EoS5ydulQRjq1nxTJxrA.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/EoS5ydulQRjq1nxTJxrA.png?auto=format&amp;w=1044 1044w&quot; width=&quot;522&quot; /&gt;
&lt;p&gt;Sadly there aren&#39;t any solid guidelines for what size image to use for an icon.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://stackoverflow.com/questions/7220738/honeycomb-notifications-how-to-set-largeicon-to-the-right-size&quot; rel=&quot;noopener&quot;&gt;Android seems to want a 64dp image&lt;/a&gt;
(which is 64px multiples by the device pixel ratio).&lt;/p&gt;
&lt;p&gt;Assuming the highest pixel ratio for a device is 3, an icon size of 192px or more is a
safe bet.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Some browsers may require the image be served over HTTPS. Be aware of this if you intend to use a third-party image. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;badge&quot;&gt;Badge &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-display-a-notification/#badge&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;badge&lt;/code&gt; is a small monochrome icon that is used to portray a little more information to the
user about where the notification is from:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Badge Notification&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;badge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/badge-128x128.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;At the time of writing the badge is only used in Chrome on Android.&lt;/p&gt;
&lt;img alt=&quot;Notification with badge in Chrome on Android.&quot; decoding=&quot;async&quot; height=&quot;248&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LaAYwycbWMn8SCro7fyo.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LaAYwycbWMn8SCro7fyo.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LaAYwycbWMn8SCro7fyo.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LaAYwycbWMn8SCro7fyo.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LaAYwycbWMn8SCro7fyo.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LaAYwycbWMn8SCro7fyo.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LaAYwycbWMn8SCro7fyo.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LaAYwycbWMn8SCro7fyo.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LaAYwycbWMn8SCro7fyo.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LaAYwycbWMn8SCro7fyo.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LaAYwycbWMn8SCro7fyo.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LaAYwycbWMn8SCro7fyo.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LaAYwycbWMn8SCro7fyo.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LaAYwycbWMn8SCro7fyo.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LaAYwycbWMn8SCro7fyo.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LaAYwycbWMn8SCro7fyo.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LaAYwycbWMn8SCro7fyo.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LaAYwycbWMn8SCro7fyo.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;On other browsers (or Chrome without the badge), you&#39;ll see an icon of the browser.&lt;/p&gt;
&lt;img alt=&quot;Notification with badge in Firefox on Android.&quot; decoding=&quot;async&quot; height=&quot;212&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Xy0ewGrawZtPRQWM99iR.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Xy0ewGrawZtPRQWM99iR.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Xy0ewGrawZtPRQWM99iR.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Xy0ewGrawZtPRQWM99iR.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Xy0ewGrawZtPRQWM99iR.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Xy0ewGrawZtPRQWM99iR.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Xy0ewGrawZtPRQWM99iR.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Xy0ewGrawZtPRQWM99iR.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Xy0ewGrawZtPRQWM99iR.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Xy0ewGrawZtPRQWM99iR.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Xy0ewGrawZtPRQWM99iR.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Xy0ewGrawZtPRQWM99iR.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Xy0ewGrawZtPRQWM99iR.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Xy0ewGrawZtPRQWM99iR.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Xy0ewGrawZtPRQWM99iR.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Xy0ewGrawZtPRQWM99iR.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Xy0ewGrawZtPRQWM99iR.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Xy0ewGrawZtPRQWM99iR.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;As with the &lt;code&gt;icon&lt;/code&gt; option, there are no real guidelines on what size to use.&lt;/p&gt;
&lt;p&gt;Digging through &lt;a href=&quot;https://developer.android.com/guide/practices/ui_guidelines/icon_design_status_bar.html&quot; rel=&quot;noopener&quot;&gt;Android guidelines&lt;/a&gt;
the recommended size is 24px multiplied by the device pixel ratio.&lt;/p&gt;
&lt;p&gt;Meaning an image of 72px or more should be good (assuming a max device pixel ratio of 3).&lt;/p&gt;
&lt;h3 id=&quot;image&quot;&gt;Image &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-display-a-notification/#image&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;image&lt;/code&gt; option can be used to display a larger image to the user. This is particularly
useful to display a preview image to the user.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Image Notification&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/unsplash-farzad-nazifi-1600x1100.jpg&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In Chrome on Linux the notification will look like this:&lt;/p&gt;
&lt;img alt=&quot;Notification with image in Chrome on Linux.&quot; decoding=&quot;async&quot; height=&quot;340&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 380px) 380px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xF6oPjieHKCqtvrsWfM7.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xF6oPjieHKCqtvrsWfM7.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xF6oPjieHKCqtvrsWfM7.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xF6oPjieHKCqtvrsWfM7.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xF6oPjieHKCqtvrsWfM7.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xF6oPjieHKCqtvrsWfM7.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xF6oPjieHKCqtvrsWfM7.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xF6oPjieHKCqtvrsWfM7.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xF6oPjieHKCqtvrsWfM7.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xF6oPjieHKCqtvrsWfM7.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xF6oPjieHKCqtvrsWfM7.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xF6oPjieHKCqtvrsWfM7.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/xF6oPjieHKCqtvrsWfM7.png?auto=format&amp;w=760 760w&quot; width=&quot;380&quot; /&gt;
&lt;p&gt;In Chrome on Android the cropping and ratio are different:&lt;/p&gt;
&lt;img alt=&quot;Notification with image in Chrome on Android.&quot; decoding=&quot;async&quot; height=&quot;554&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VF32ZZXDZj2xO7dJdR3W.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VF32ZZXDZj2xO7dJdR3W.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VF32ZZXDZj2xO7dJdR3W.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VF32ZZXDZj2xO7dJdR3W.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VF32ZZXDZj2xO7dJdR3W.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VF32ZZXDZj2xO7dJdR3W.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VF32ZZXDZj2xO7dJdR3W.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VF32ZZXDZj2xO7dJdR3W.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VF32ZZXDZj2xO7dJdR3W.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VF32ZZXDZj2xO7dJdR3W.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VF32ZZXDZj2xO7dJdR3W.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VF32ZZXDZj2xO7dJdR3W.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VF32ZZXDZj2xO7dJdR3W.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VF32ZZXDZj2xO7dJdR3W.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VF32ZZXDZj2xO7dJdR3W.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VF32ZZXDZj2xO7dJdR3W.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VF32ZZXDZj2xO7dJdR3W.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VF32ZZXDZj2xO7dJdR3W.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Given the differences in ratio between desktop and mobile, it&#39;s extremely hard to suggest
guidelines.&lt;/p&gt;
&lt;p&gt;Since Chrome on desktop doesn&#39;t fill the available space and has a ratio of 4:3, perhaps the best
approach is to serve an image with this ratio and allow Android to crop the image. That being said,
the &lt;code&gt;image&lt;/code&gt; option may still change.&lt;/p&gt;
&lt;p&gt;On Android, the only &lt;a href=&quot;https://code.google.com/p/android/issues/detail?id=36744&quot; rel=&quot;noopener&quot;&gt;guideline&lt;/a&gt; is a
width of 450dp.&lt;/p&gt;
&lt;p&gt;Using this guideline, an image of width 1350px or more would be a good bet.&lt;/p&gt;
&lt;h3 id=&quot;actions-buttons&quot;&gt;Actions (Buttons) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-display-a-notification/#actions-buttons&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You can define &lt;code&gt;actions&lt;/code&gt; to display buttons with a notification:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Actions Notification&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;actions&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;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&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;&#39;coffee-action&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;button&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Coffee&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/action-1-128x128.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&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;&#39;doughnut-action&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;button&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Doughnut&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/action-2-128x128.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&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;&#39;gramophone-action&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;button&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Gramophone&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/action-3-128x128.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&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;&#39;atom-action&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;button&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Atom&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/action-4-128x128.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;For each action you can define a &lt;code&gt;title&lt;/code&gt;, an &lt;code&gt;action&lt;/code&gt; (which is essentially an ID), an &lt;code&gt;icon&lt;/code&gt;, and
a &lt;code&gt;type&lt;/code&gt;. The title and icon is what you can see in the notification. The ID is used when detecting
that the action button had been clicked (more about this in the next section). The type
can be omitted because &lt;code&gt;&#39;button&#39;&lt;/code&gt; is the default value.&lt;/p&gt;
&lt;p&gt;At the time of writing only Chrome and Opera for Android support actions.&lt;/p&gt;
&lt;p&gt;In the example above there are four actions defined to illustrate that you can define more actions than
will be displayed. If you want to know the number of actions that will be displayed by the browser,
you can check &lt;code&gt;window.Notification?.maxActions&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; maxVisibleActions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Notification&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;maxActions&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;maxVisibleActions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Up to &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;maxVisibleActions&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; notification actions can be displayed.&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Notification actions are not supported.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;On desktop, the action button icons display their colors (see the pink doughnut):&lt;/p&gt;
&lt;img alt=&quot;Notification with action buttons on Chrome on Linux.&quot; decoding=&quot;async&quot; height=&quot;178&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 380px) 380px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/REyUtJ7icGvOoepXgpLQ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/REyUtJ7icGvOoepXgpLQ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/REyUtJ7icGvOoepXgpLQ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/REyUtJ7icGvOoepXgpLQ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/REyUtJ7icGvOoepXgpLQ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/REyUtJ7icGvOoepXgpLQ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/REyUtJ7icGvOoepXgpLQ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/REyUtJ7icGvOoepXgpLQ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/REyUtJ7icGvOoepXgpLQ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/REyUtJ7icGvOoepXgpLQ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/REyUtJ7icGvOoepXgpLQ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/REyUtJ7icGvOoepXgpLQ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/REyUtJ7icGvOoepXgpLQ.png?auto=format&amp;w=760 760w&quot; width=&quot;380&quot; /&gt;
&lt;p&gt;On Android 6 and earlier, the icons are colored to match the system color scheme:&lt;/p&gt;
&lt;img alt=&quot;Notification with action buttons on Chrome for Android.&quot; decoding=&quot;async&quot; height=&quot;296&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GI8sj9krxVtxWVeHGuvs.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GI8sj9krxVtxWVeHGuvs.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GI8sj9krxVtxWVeHGuvs.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GI8sj9krxVtxWVeHGuvs.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GI8sj9krxVtxWVeHGuvs.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GI8sj9krxVtxWVeHGuvs.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GI8sj9krxVtxWVeHGuvs.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GI8sj9krxVtxWVeHGuvs.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GI8sj9krxVtxWVeHGuvs.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GI8sj9krxVtxWVeHGuvs.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GI8sj9krxVtxWVeHGuvs.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GI8sj9krxVtxWVeHGuvs.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GI8sj9krxVtxWVeHGuvs.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GI8sj9krxVtxWVeHGuvs.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GI8sj9krxVtxWVeHGuvs.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GI8sj9krxVtxWVeHGuvs.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GI8sj9krxVtxWVeHGuvs.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/GI8sj9krxVtxWVeHGuvs.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;On Android 7 and later, the action icons aren&#39;t shown at all.&lt;/p&gt;
&lt;p&gt;Chrome will hopefully change it&#39;s behavior on desktop to match Android (i.e. apply the
appropriate color scheme to make the icons match the system look and feel). In the meantime, you
can match Chrome&#39;s text color by making your icons have a color of &lt;code&gt;#333333&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It&#39;s also worth calling out that icons look crisp on Android but &lt;strong&gt;not&lt;/strong&gt; on desktop.&lt;/p&gt;
&lt;p&gt;The best size I could get to work on desktop Chrome was 24px x 24px. This sadly looks out of place
on Android.&lt;/p&gt;
&lt;p&gt;The best practice we can draw from these differences:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Stick to a consistent color scheme for your icons so at least all your icons are consistently
displayed to the user.&lt;/li&gt;
&lt;li&gt;Make sure they work in monochrome as some platforms may display them that way.&lt;/li&gt;
&lt;li&gt;Test the size and see what works for you. 128px × 128px works well on Android for me but was poor
quality on desktop.&lt;/li&gt;
&lt;li&gt;Expect your action icons not to be displayed at all.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Notification spec is exploring a way to define multiple sizes of icons, but it looks like it&#39;ll
be some time before anything is agreed upon.&lt;/p&gt;
&lt;h3 id=&quot;actions-inline-replies&quot;&gt;Actions (Inline Replies) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-display-a-notification/#actions-inline-replies&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You can add an inline reply to the notification by defining an action with the &lt;code&gt;&#39;text&#39;&lt;/code&gt; type:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Alexey Rodionov&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;How are you doing? )&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/avatar-512x512.jpg&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/icon-512x512.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;badge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/badge-128x128.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token 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;&#39;reply&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;text&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Reply&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/action-5-128x128.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This is what it will look like on Android:&lt;/p&gt;
&lt;img alt=&quot;Notification on Android with a reply action button.&quot; decoding=&quot;async&quot; height=&quot;304&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/Ph3WKnKo7I2qIZdHSmzO.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/Ph3WKnKo7I2qIZdHSmzO.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/Ph3WKnKo7I2qIZdHSmzO.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/Ph3WKnKo7I2qIZdHSmzO.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/Ph3WKnKo7I2qIZdHSmzO.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/Ph3WKnKo7I2qIZdHSmzO.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/Ph3WKnKo7I2qIZdHSmzO.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/Ph3WKnKo7I2qIZdHSmzO.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/Ph3WKnKo7I2qIZdHSmzO.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/Ph3WKnKo7I2qIZdHSmzO.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/Ph3WKnKo7I2qIZdHSmzO.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/Ph3WKnKo7I2qIZdHSmzO.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/Ph3WKnKo7I2qIZdHSmzO.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/Ph3WKnKo7I2qIZdHSmzO.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/Ph3WKnKo7I2qIZdHSmzO.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/Ph3WKnKo7I2qIZdHSmzO.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/Ph3WKnKo7I2qIZdHSmzO.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/Ph3WKnKo7I2qIZdHSmzO.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Clicking on the action button opens a text input field:&lt;/p&gt;
&lt;img alt=&quot;Notification on Android with an opened text input field.&quot; decoding=&quot;async&quot; height=&quot;330&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/3eVno9EWU28kN2DP2xIT.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/3eVno9EWU28kN2DP2xIT.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/3eVno9EWU28kN2DP2xIT.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/3eVno9EWU28kN2DP2xIT.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/3eVno9EWU28kN2DP2xIT.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/3eVno9EWU28kN2DP2xIT.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/3eVno9EWU28kN2DP2xIT.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/3eVno9EWU28kN2DP2xIT.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/3eVno9EWU28kN2DP2xIT.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/3eVno9EWU28kN2DP2xIT.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/3eVno9EWU28kN2DP2xIT.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/3eVno9EWU28kN2DP2xIT.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/3eVno9EWU28kN2DP2xIT.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/3eVno9EWU28kN2DP2xIT.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/3eVno9EWU28kN2DP2xIT.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/3eVno9EWU28kN2DP2xIT.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/3eVno9EWU28kN2DP2xIT.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/3eVno9EWU28kN2DP2xIT.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;You can customize the placeholder for the text input field:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Alexey Rodionov&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;How are you doing? )&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/avatar-512x512.jpg&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;badge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/badge-128x128.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;reply&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;text&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Reply&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/action-5-128x128.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;placeholder&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Type text here&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;img alt=&quot;Notification on Android with customized placeholder for text input field.&quot; decoding=&quot;async&quot; height=&quot;330&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/dD8gIYQMO29voqF3qyHY.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/dD8gIYQMO29voqF3qyHY.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/dD8gIYQMO29voqF3qyHY.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/dD8gIYQMO29voqF3qyHY.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/dD8gIYQMO29voqF3qyHY.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/dD8gIYQMO29voqF3qyHY.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/dD8gIYQMO29voqF3qyHY.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/dD8gIYQMO29voqF3qyHY.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/dD8gIYQMO29voqF3qyHY.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/dD8gIYQMO29voqF3qyHY.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/dD8gIYQMO29voqF3qyHY.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/dD8gIYQMO29voqF3qyHY.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/dD8gIYQMO29voqF3qyHY.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/dD8gIYQMO29voqF3qyHY.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/dD8gIYQMO29voqF3qyHY.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/dD8gIYQMO29voqF3qyHY.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/dD8gIYQMO29voqF3qyHY.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/dD8gIYQMO29voqF3qyHY.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;In Chrome on Windows, the text input field is always visible without having to click the action
button:&lt;/p&gt;
&lt;img alt=&quot;Notification on Windows with a text input field and a reply action button.&quot; decoding=&quot;async&quot; height=&quot;504&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 780px) 780px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/qZhA529rWfVHzt7RVacJ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/qZhA529rWfVHzt7RVacJ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/qZhA529rWfVHzt7RVacJ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/qZhA529rWfVHzt7RVacJ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/qZhA529rWfVHzt7RVacJ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/qZhA529rWfVHzt7RVacJ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/qZhA529rWfVHzt7RVacJ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/qZhA529rWfVHzt7RVacJ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/qZhA529rWfVHzt7RVacJ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/qZhA529rWfVHzt7RVacJ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/qZhA529rWfVHzt7RVacJ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/qZhA529rWfVHzt7RVacJ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/qZhA529rWfVHzt7RVacJ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/qZhA529rWfVHzt7RVacJ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/qZhA529rWfVHzt7RVacJ.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/qZhA529rWfVHzt7RVacJ.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/qZhA529rWfVHzt7RVacJ.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/qZhA529rWfVHzt7RVacJ.png?auto=format&amp;w=1560 1560w&quot; width=&quot;780&quot; /&gt;
&lt;p&gt;You can add more than one inline reply or combine buttons and inline replies:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Poll&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Do you like this photo?&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/cat-image.jpg&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/icon-512x512.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;badge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/badge-128x128.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token 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;&#39;yes&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;button&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;👍 Yes&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&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;&#39;no&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;text&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;👎 No (explain why)&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;placeholder&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Type your explanation here&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;img alt=&quot;Notification on Windows with a text input field and two action buttons.&quot; decoding=&quot;async&quot; height=&quot;891&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/fKYyLvbAKghcnbmwEOfE.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/fKYyLvbAKghcnbmwEOfE.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/fKYyLvbAKghcnbmwEOfE.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/fKYyLvbAKghcnbmwEOfE.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/fKYyLvbAKghcnbmwEOfE.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/fKYyLvbAKghcnbmwEOfE.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/fKYyLvbAKghcnbmwEOfE.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/fKYyLvbAKghcnbmwEOfE.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/fKYyLvbAKghcnbmwEOfE.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/fKYyLvbAKghcnbmwEOfE.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/fKYyLvbAKghcnbmwEOfE.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/fKYyLvbAKghcnbmwEOfE.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/fKYyLvbAKghcnbmwEOfE.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/fKYyLvbAKghcnbmwEOfE.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/fKYyLvbAKghcnbmwEOfE.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/fKYyLvbAKghcnbmwEOfE.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/fKYyLvbAKghcnbmwEOfE.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/fKYyLvbAKghcnbmwEOfE.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h3 id=&quot;direction&quot;&gt;Direction &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-display-a-notification/#direction&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;dir&lt;/code&gt; parameter allows you to define which direction the text should be displayed,
right-to-left or left-to-right.&lt;/p&gt;
&lt;p&gt;In testing, it seemed that the direction was largely determined by the text rather than this
parameter. According to the spec, this is intended to suggest to the browser how to layout options
like actions, but I saw no difference.&lt;/p&gt;
&lt;p&gt;Probably best to define if you can, otherwise the browser should do the right thing according to
the text supplied.&lt;/p&gt;
&lt;p&gt;The parameter should be set to either &lt;code&gt;auto&lt;/code&gt;, &lt;code&gt;ltr&lt;/code&gt; or &lt;code&gt;rtl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A right-to-left language used on Chrome on Linux looks like this:&lt;/p&gt;
&lt;img alt=&quot;Notification with right-to-left language on Chrome on Linux.&quot; decoding=&quot;async&quot; height=&quot;234&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 380px) 380px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/epWm0UDjQzcRMz37uaaa.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/epWm0UDjQzcRMz37uaaa.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/epWm0UDjQzcRMz37uaaa.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/epWm0UDjQzcRMz37uaaa.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/epWm0UDjQzcRMz37uaaa.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/epWm0UDjQzcRMz37uaaa.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/epWm0UDjQzcRMz37uaaa.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/epWm0UDjQzcRMz37uaaa.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/epWm0UDjQzcRMz37uaaa.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/epWm0UDjQzcRMz37uaaa.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/epWm0UDjQzcRMz37uaaa.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/epWm0UDjQzcRMz37uaaa.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/epWm0UDjQzcRMz37uaaa.png?auto=format&amp;w=760 760w&quot; width=&quot;380&quot; /&gt;
&lt;p&gt;In Firefox (while hovering over it) you&#39;ll get this:&lt;/p&gt;
&lt;img alt=&quot;Notification with right-to-left language on Firefox on Linux.&quot; decoding=&quot;async&quot; height=&quot;102&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 521px) 521px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ncKq2yMj7ipFkGGOWKBr.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ncKq2yMj7ipFkGGOWKBr.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ncKq2yMj7ipFkGGOWKBr.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ncKq2yMj7ipFkGGOWKBr.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ncKq2yMj7ipFkGGOWKBr.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ncKq2yMj7ipFkGGOWKBr.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ncKq2yMj7ipFkGGOWKBr.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ncKq2yMj7ipFkGGOWKBr.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ncKq2yMj7ipFkGGOWKBr.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ncKq2yMj7ipFkGGOWKBr.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ncKq2yMj7ipFkGGOWKBr.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ncKq2yMj7ipFkGGOWKBr.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ncKq2yMj7ipFkGGOWKBr.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ncKq2yMj7ipFkGGOWKBr.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ncKq2yMj7ipFkGGOWKBr.png?auto=format&amp;w=1042 1042w&quot; width=&quot;521&quot; /&gt;
&lt;h3 id=&quot;vibrate&quot;&gt;Vibrate &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-display-a-notification/#vibrate&quot;&gt;#&lt;/a&gt;&lt;/h3&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; Support for &lt;a href=&quot;https://crbug.com/971422&quot;&gt;&lt;code&gt;vibrate&lt;/code&gt; has been deprecated&lt;/a&gt; on Android 8 and later. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The vibrate option allows you to define a vibration pattern that&#39;ll run when a notification is
displayed, assuming the user&#39;s current settings allow for vibrations (i.e. the device isn&#39;t in
silent mode).&lt;/p&gt;
&lt;p&gt;The format of the vibrate option should be an array of numbers that describe the number of
milliseconds the device should vibrate, followed by the number of milliseconds the device should
&lt;em&gt;not&lt;/em&gt; vibrate.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Vibrate Notification&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// Star Wars shamelessly taken from the awesome Peter Beverloo&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token comment&quot;&gt;// https://tests.peter.sh/notification-generator/&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;vibrate&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;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;110&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;110&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;450&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;110&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;110&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;170&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;450&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;110&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;110&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;170&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This only affects devices that support vibration.&lt;/p&gt;
&lt;h3 id=&quot;sound&quot;&gt;Sound &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-display-a-notification/#sound&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The sound parameter allows you to define a sound to play when the notification is received.&lt;/p&gt;
&lt;p&gt;At the time of writing, no browser has support for this option.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Sound Notification&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;sound&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/demos/notification-examples/audio/notification-sound.mp3&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;timestamp&quot;&gt;Timestamp &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-display-a-notification/#timestamp&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Timestamp allows you to tell the platform the time when an event occurred that resulted in the push
notification being sent.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;timestamp&lt;/code&gt; should be the number of milliseconds since 00:00:00 UTC, which is 1 January 1970
(which is the UNIX epoch).&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Timestamp Notification&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Timestamp is set to &quot;01 Jan 2000 00:00:00&quot;.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;timestamp&lt;/span&gt;&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;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;01 Jan 2000 00:00:00&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;ux-best-practices&quot;&gt;UX Best Practices &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-display-a-notification/#ux-best-practices&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The biggest UX failure I&#39;ve seen with notifications is a lack of specificity in the information
displayed by a notification.&lt;/p&gt;
&lt;p&gt;You should consider why you sent the push message in the first place and make sure all of the
notification options are used to help users understand why they are reading that notification.&lt;/p&gt;
&lt;p&gt;To be honest, it&#39;s easy to see examples and think &amp;quot;I&#39;ll never make that mistake&amp;quot;. But it&#39;s easier
to fall into that trap than you might think.&lt;/p&gt;
&lt;p&gt;Some common pitfalls to avoid:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Don&#39;t put your website in the title or the body. Browsers include your domain in the
notification so &lt;strong&gt;don&#39;t duplicate it&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Use all the information that you have available. If you send a push message because someone
sent a message to a user, rather than using a title of &#39;New Message&#39; and body of &#39;Click here to
read it.&#39; use a title of &#39;John just sent a new message&#39; and set the body of the notification to
part of the message.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;browsers-and-feature-detection&quot;&gt;Browsers and feature detection &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-display-a-notification/#browsers-and-feature-detection&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At the time of writing, there is a pretty big disparity between Chrome and Firefox in terms of
feature support for notifications.&lt;/p&gt;
&lt;p&gt;Luckily, you can detect support for notification features by looking at the &lt;code&gt;window.Notification&lt;/code&gt;
prototype.&lt;/p&gt;
&lt;p&gt;Let&#39;s say we wanted to know if a notification supports action buttons, we&#39;d do the following:&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;&#39;actions&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Notification&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;prototype&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Action buttons are supported.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Action buttons are NOT supported.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;With this, we could change the notification we display to our users.&lt;/p&gt;
&lt;p&gt;With the other options, just do the same as above, replacing &lt;code&gt;&#39;actions&#39;&lt;/code&gt; with the desired
parameter name.&lt;/p&gt;
&lt;h2 id=&quot;where-to-go-next&quot;&gt;Where to go next &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-display-a-notification/#where-to-go-next&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-overview/&quot;&gt;Web Push Notification Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-how-push-works/&quot;&gt;How Push Works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-subscribing-a-user/&quot;&gt;Subscribing a User&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-permissions-ux/&quot;&gt;Permission UX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sending-messages-with-web-push-libraries/&quot;&gt;Sending Messages with Web Push Libraries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-web-push-protocol/&quot;&gt;Web Push Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-handling-messages/&quot;&gt;Handling Push Events&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Displaying a Notification&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-notification-behaviour/&quot;&gt;Notification Behavior&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-common-notification-patterns/&quot;&gt;Common Notification Patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-faq/&quot;&gt;Push Notifications FAQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/&quot;&gt;Common Issues and Reporting Bugs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;code-labs&quot;&gt;Code labs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-display-a-notification/#code-labs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-client-codelab/&quot;&gt;Build a push notification client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-server-codelab/&quot;&gt;Build a push notification server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Matt Gaunt</name>
    </author><author>
      <name>Alexey Rodionov</name>
    </author>
  </entry>
  
  <entry>
    <title>Notification behavior</title>
    <link href="https://web.dev/push-notifications-notification-behaviour/"/>
    <updated>2016-06-30T00:00:00Z</updated>
    <id>https://web.dev/push-notifications-notification-behaviour/</id>
    <content type="html" mode="escaped">&lt;p&gt;So far, we&#39;ve looked at the options that alter the visual appearance of a notification. There are
also options that alter the behavior of notifications.&lt;/p&gt;
&lt;p&gt;By default, calling &lt;code&gt;showNotification()&lt;/code&gt; with just visual options will have the following
behaviors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Clicking on the notification does nothing.&lt;/li&gt;
&lt;li&gt;Each new notification is shown one after the other. The browser will not collapse the
notifications in any way.&lt;/li&gt;
&lt;li&gt;The platform may play a sound or vibrate the user&#39;s device (depending on the platform).&lt;/li&gt;
&lt;li&gt;On some platforms, the notification will disappear after a short period of time while others will
show the notification unless the user interacts with it. (For example, compare your notifications
on Android and Desktop.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this section, we are going to look at how we can alter these default behaviors using options
alone. These are relatively easy to implement and take advantage of.&lt;/p&gt;
&lt;h2 id=&quot;notification-click-event&quot;&gt;Notification Click Event &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-notification-behaviour/#notification-click-event&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When a user clicks on a notification, the default behavior is for nothing to happen. It doesn&#39;t
even close or remove the notification.&lt;/p&gt;
&lt;p&gt;The common practice for a notification click is for it to close and perform some other logic (i.e.
open a window or make some API call to the application).&lt;/p&gt;
&lt;p&gt;To achieve this, you need to add a &lt;code&gt;&#39;notificationclick&#39;&lt;/code&gt; event listener to our service worker. This
will be called whenever a notification is clicked.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;notificationclick&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; clickedNotification &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;notification&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  clickedNotification&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;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Do something as the result of the notification click&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; promiseChain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;doSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitUntil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;promiseChain&lt;span class=&quot;token punctuation&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;As you can see in this example, the notification that was clicked can be accessed as
&lt;code&gt;event.notification&lt;/code&gt;. From this, you can access the notification&#39;s properties and methods. In this
case, you call its &lt;code&gt;close()&lt;/code&gt; method and perform additional work.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-warn-bg color-state-warn-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block color-state-warn-text&quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Warning sign&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M23 21L12 2 1 21h22zm-12-3v-2h2v2h-2zm0-4h2v-4h-2v4z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Warning&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; You need to make use of &lt;code&gt;event.waitUntil()&lt;/code&gt; to keep the service worker running while your code is busy. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;actions&quot;&gt;Actions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-notification-behaviour/#actions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Actions allow you to create another level of interaction with your users over just clicking the
notification.&lt;/p&gt;
&lt;h3 id=&quot;buttons&quot;&gt;Buttons &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-notification-behaviour/#buttons&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In the previous section, you saw how to define action buttons when calling &lt;code&gt;showNotification()&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Actions Notification&#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; options &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;actions&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;coffee-action&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Coffee&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;button&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/action-1-128x128.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;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;&#39;doughnut-action&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;button&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Doughnut&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/action-2-128x128.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;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;&#39;gramophone-action&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;button&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Gramophone&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/action-3-128x128.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;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;&#39;atom-action&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;button&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Atom&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/action-4-128x128.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If the user clicks an action button, check the &lt;code&gt;event.action&lt;/code&gt; value in the &lt;code&gt;noticationclick&lt;/code&gt;
event to tell which action button was clicked.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;event.action&lt;/code&gt; will contain the &lt;code&gt;action&lt;/code&gt; value set in the options. In the example above the
&lt;code&gt;event.action&lt;/code&gt; values would be one of the following: &lt;code&gt;&#39;coffee-action&#39;&lt;/code&gt;, &lt;code&gt;&#39;doughnut-action&#39;&lt;/code&gt;,
&lt;code&gt;&#39;gramophone-action&#39;&lt;/code&gt; or &lt;code&gt;&#39;atom-action&#39;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With this we would detect notification clicks or action clicks like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;notificationclick&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;action&lt;span class=&quot;token punctuation&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;// Was a normal notification click&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;Notification Click.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;coffee-action&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;User ❤️️&#39;s coffee.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;doughnut-action&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;User ❤️️&#39;s doughnuts.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;gramophone-action&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;User ❤️️&#39;s music.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;atom-action&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;User ❤️️&#39;s science.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Unknown action clicked: &#39;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;action&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;inline-replies&quot;&gt;Inline Replies &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-notification-behaviour/#inline-replies&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Also, in the previous section, you saw how to add an inline reply to the notification:&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; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Poll&#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; options &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;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Do you like this photo?&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/cat-image.jpg&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/icon-512x512.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;badge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/images/demos/badge-128x128.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;yes&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;button&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;👍 Yes&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;no&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;text&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;👎 No (explain why)&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;placeholder&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Type your explanation here&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;event.reply&lt;/code&gt; will contain the value typed by the user in the input field:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;notificationclick&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; reply &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reply&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Do something with the user&#39;s reply&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; promiseChain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;doSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reply&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitUntil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;promiseChain&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;tag&quot;&gt;Tag &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-notification-behaviour/#tag&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;tag&lt;/code&gt; option is essentially a string ID that &amp;quot;groups&amp;quot; notifications together, providing an easy
way to determine how multiple notifications are displayed to the user. This is easiest to explain
with an example.&lt;/p&gt;
&lt;p&gt;Let&#39;s display a notification and give it a tag, of &lt;code&gt;&#39;message-group-1&#39;&lt;/code&gt;. We&#39;d display the
notification with this code:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Notification 1 of 3&#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; options &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;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;With &#39;tag&#39; of &#39;message-group-1&#39;&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;tag&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;message-group-1&#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;br /&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Which will show our first notification.&lt;/p&gt;
&lt;img alt=&quot;First notification with tag of message group 1.&quot; decoding=&quot;async&quot; height=&quot;222&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 380px) 380px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/UKvAEJq7hWFQsxQjlnKv.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/UKvAEJq7hWFQsxQjlnKv.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/UKvAEJq7hWFQsxQjlnKv.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/UKvAEJq7hWFQsxQjlnKv.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/UKvAEJq7hWFQsxQjlnKv.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/UKvAEJq7hWFQsxQjlnKv.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/UKvAEJq7hWFQsxQjlnKv.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/UKvAEJq7hWFQsxQjlnKv.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/UKvAEJq7hWFQsxQjlnKv.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/UKvAEJq7hWFQsxQjlnKv.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/UKvAEJq7hWFQsxQjlnKv.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/UKvAEJq7hWFQsxQjlnKv.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/UKvAEJq7hWFQsxQjlnKv.png?auto=format&amp;w=760 760w&quot; width=&quot;380&quot; /&gt;
&lt;p&gt;Let&#39;s display a second notification with a new tag of &lt;code&gt;&#39;message-group-2&#39;&lt;/code&gt;, like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Notification 2 of 3&#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; options &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;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;With &#39;tag&#39; of &#39;message-group-2&#39;&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;tag&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;message-group-2&#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;br /&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This will display a second notification to the user.&lt;/p&gt;
&lt;img alt=&quot;Two notifications where the second tag of message group 2.&quot; decoding=&quot;async&quot; height=&quot;222&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 380px) 380px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/L1reebxFF4pJ1sIYRvcE.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/L1reebxFF4pJ1sIYRvcE.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/L1reebxFF4pJ1sIYRvcE.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/L1reebxFF4pJ1sIYRvcE.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/L1reebxFF4pJ1sIYRvcE.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/L1reebxFF4pJ1sIYRvcE.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/L1reebxFF4pJ1sIYRvcE.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/L1reebxFF4pJ1sIYRvcE.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/L1reebxFF4pJ1sIYRvcE.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/L1reebxFF4pJ1sIYRvcE.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/L1reebxFF4pJ1sIYRvcE.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/L1reebxFF4pJ1sIYRvcE.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/L1reebxFF4pJ1sIYRvcE.png?auto=format&amp;w=760 760w&quot; width=&quot;380&quot; /&gt;
&lt;p&gt;Now let&#39;s show a third notification but re-use the first tag of &lt;code&gt;&#39;message-group-1&#39;&lt;/code&gt;. Doing this
will close the first notification and replace it with our new notification.&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; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Notification 3 of 3&#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; options &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;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;With &#39;tag&#39; of &#39;message-group-1&#39;&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;tag&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;message-group-1&#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;br /&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Now we have two notifications even though &lt;code&gt;showNotification()&lt;/code&gt; was called three times.&lt;/p&gt;
&lt;img alt=&quot;Two notifications where the first notification is replaced by a third notification.&quot; decoding=&quot;async&quot; height=&quot;222&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 380px) 380px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/RLHBEuD6REvyuZhHgWJw.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/RLHBEuD6REvyuZhHgWJw.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/RLHBEuD6REvyuZhHgWJw.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/RLHBEuD6REvyuZhHgWJw.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/RLHBEuD6REvyuZhHgWJw.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/RLHBEuD6REvyuZhHgWJw.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/RLHBEuD6REvyuZhHgWJw.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/RLHBEuD6REvyuZhHgWJw.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/RLHBEuD6REvyuZhHgWJw.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/RLHBEuD6REvyuZhHgWJw.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/RLHBEuD6REvyuZhHgWJw.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/RLHBEuD6REvyuZhHgWJw.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/8WbTDNrhLsU0El80frMBGE4eMCD3/RLHBEuD6REvyuZhHgWJw.png?auto=format&amp;w=760 760w&quot; width=&quot;380&quot; /&gt;
&lt;p&gt;The &lt;code&gt;tag&lt;/code&gt; option is simply a way of grouping messages so that any old notifications that are
currently displayed will be closed if they have the same tag as a new notification.&lt;/p&gt;
&lt;p&gt;A subtlety to using &lt;code&gt;tag&lt;/code&gt; is that when it replaces a notification, it will do so &lt;em&gt;without&lt;/em&gt; a sound
or vibration.&lt;/p&gt;
&lt;p&gt;This is where the &lt;code&gt;renotify&lt;/code&gt; option comes in.&lt;/p&gt;
&lt;h2 id=&quot;renotify&quot;&gt;Renotify &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-notification-behaviour/#renotify&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This largely applies to mobile devices at the time of writing. Setting this option makes new
notifications vibrate and play a system sound.&lt;/p&gt;
&lt;p&gt;There are scenarios where you might want a replacing notification to notify the user rather than
silently update. Chat applications are a good example. In this case, you should set &lt;code&gt;tag&lt;/code&gt; and
&lt;code&gt;renotify&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Notification 2 of 2&#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; options &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;tag&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;renotify&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;renotify&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-warn-bg color-state-warn-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block color-state-warn-text&quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Warning sign&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M23 21L12 2 1 21h22zm-12-3v-2h2v2h-2zm0-4h2v-4h-2v4z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Warning&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; If you set &lt;code&gt;renotify&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; on a notification without a &lt;code&gt;tag&lt;/code&gt;, you&#39;ll get the following error: &lt;code&gt;TypeError: Failed to execute &#39;showNotification&#39; on &#39;ServiceWorkerRegistration&#39;: Notifications which set the renotify flag must specify a non-empty tag&lt;/code&gt; &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;silent&quot;&gt;Silent &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-notification-behaviour/#silent&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This option allows you to show a new notification but prevents the default behavior of vibration,
sound and turning on the device&#39;s display.&lt;/p&gt;
&lt;p&gt;This is ideal if your notifications don&#39;t require immediate attention from the user.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Silent Notification&#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; options &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;silent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Note: If you define both &lt;code&gt;silent&lt;/code&gt; and &lt;code&gt;renotify&lt;/code&gt;, &lt;code&gt;silent&lt;/code&gt; will take precedence.&lt;/p&gt;
&lt;h2 id=&quot;requires-interaction&quot;&gt;Requires interaction &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-notification-behaviour/#requires-interaction&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Chrome on desktop will show notifications for a set time period before hiding them. Chrome on
Android doesn&#39;t have this behavior. Notifications are displayed until the user interacts with them.&lt;/p&gt;
&lt;p&gt;To force a notification to stay visible until the user interacts with it, add the &lt;code&gt;requireInteraction&lt;/code&gt;
option. This will show the notification until the user dismisses or clicks your notification.&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; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Require Interaction Notification&#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; options &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;requireInteraction&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Use this option with consideration. Showing a notification and forcing the user to stop what
they are doing to dismiss your notification can be frustrating.&lt;/p&gt;
&lt;p&gt;In the next section, we are going to look at some of the common patterns used on the web for
managing notifications and performing actions such as opening pages when a notification is clicked.&lt;/p&gt;
&lt;h2 id=&quot;where-to-go-next&quot;&gt;Where to go next &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-notification-behaviour/#where-to-go-next&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-overview/&quot;&gt;Web Push Notification Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-how-push-works/&quot;&gt;How Push Works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-subscribing-a-user/&quot;&gt;Subscribing a User&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-permissions-ux/&quot;&gt;Permission UX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sending-messages-with-web-push-libraries/&quot;&gt;Sending Messages with Web Push Libraries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-web-push-protocol/&quot;&gt;Web Push Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-handling-messages/&quot;&gt;Handling Push Events&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-display-a-notification/&quot;&gt;Displaying a Notification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Notification Behavior&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-common-notification-patterns/&quot;&gt;Common Notification Patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-faq/&quot;&gt;Push Notifications FAQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/&quot;&gt;Common Issues and Reporting Bugs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;code-labs&quot;&gt;Code labs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-notification-behaviour/#code-labs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-client-codelab/&quot;&gt;Build a push notification client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-server-codelab/&quot;&gt;Build a push notification server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Matt Gaunt</name>
    </author><author>
      <name>Alexey Rodionov</name>
    </author>
  </entry>
  
  <entry>
    <title>The Web Push Protocol</title>
    <link href="https://web.dev/push-notifications-web-push-protocol/"/>
    <updated>2016-06-30T00:00:00Z</updated>
    <id>https://web.dev/push-notifications-web-push-protocol/</id>
    <content type="html" mode="escaped">&lt;p&gt;We&#39;ve seen how a library can be used to trigger push messages, but what
exactly are these libraries doing?&lt;/p&gt;
&lt;p&gt;Well, they&#39;re making network requests while ensuring such requests are
the right format. The spec that defines this network request is the
&lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-webpush-protocol&quot; rel=&quot;noopener&quot;&gt;Web Push Protocol&lt;/a&gt;.&lt;/p&gt;
&lt;img alt=&quot;Diagram of sending a push message from your server to a push service&quot; decoding=&quot;async&quot; height=&quot;220&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/jjHOGQvZttcOEij3c6UR.svg&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;This section outlines how the server can identify itself with application
server keys and how the encrypted payload and associated data is sent.&lt;/p&gt;
&lt;p&gt;This isn&#39;t a pretty side of web push and I&#39;m no expert at encryption, but let&#39;s look through
each piece since it&#39;s handy to know what these libraries are doing under the hood.&lt;/p&gt;
&lt;h2 id=&quot;application-server-keys&quot;&gt;Application server keys &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#application-server-keys&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When we subscribe a user, we pass in an &lt;code&gt;applicationServerKey&lt;/code&gt;. This key is
passed to the push service and used to check that the application that subscribed
the user is also the application that is triggering push messages.&lt;/p&gt;
&lt;p&gt;When we trigger a push message, there are a set of headers that we send that
allow the push service to authenticate the application. (This is defined
by the &lt;a href=&quot;https://tools.ietf.org/html/draft-thomson-webpush-vapid&quot; rel=&quot;noopener&quot;&gt;VAPID spec&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;What does all this actually mean and what exactly happens? Well these are the steps taken for
application server authentication:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The application server signs some JSON information with it&#39;s &lt;strong&gt;private application key&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;This signed information is sent to the push service as a header in a POST request.&lt;/li&gt;
&lt;li&gt;The push service uses the stored public key it received from
&lt;code&gt;pushManager.subscribe()&lt;/code&gt; to check the received information is signed by
the private key relating to the public key. &lt;em&gt;Remember&lt;/em&gt;: The public key is
the &lt;code&gt;applicationServerKey&lt;/code&gt; passed into the subscribe call.&lt;/li&gt;
&lt;li&gt;If the signed information is valid the push service sends the push
message to the user.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;An example of this flow of information is below. (Note the legend in the bottom left to indicate
public and private keys.)&lt;/p&gt;
&lt;img alt=&quot;Illustration of how the private application server key is used when sending a message&quot; decoding=&quot;async&quot; height=&quot;378&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/iRDfZt1TmornyUzjT0qW.svg&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;The &amp;quot;signed information&amp;quot; added to a header in the request is a JSON Web Token.&lt;/p&gt;
&lt;h3 id=&quot;json-web-token&quot;&gt;JSON web token &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#json-web-token&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A &lt;a href=&quot;https://jwt.io/&quot; rel=&quot;noopener&quot;&gt;JSON web token&lt;/a&gt; (or JWT for short) is a way of
sending a message to a third party such that the receiver can validate
who sent it.&lt;/p&gt;
&lt;p&gt;When a third party receives a message, they need to get the senders
public key and use it to validate the signature of the JWT. If the
signature is valid then the JWT must have been signed with the matching
private key so must be from the expected sender.&lt;/p&gt;
&lt;p&gt;There are a host of libraries on &lt;a href=&quot;https://jwt.io/&quot; rel=&quot;noopener&quot;&gt;https://jwt.io/&lt;/a&gt; that
can perform the signing for you and I&#39;d recommend you do that where you
can. For completeness, let&#39;s look at how to manually create a signed JWT.&lt;/p&gt;
&lt;h3 id=&quot;web-push-and-signed-jwts&quot;&gt;Web push and signed JWTs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#web-push-and-signed-jwts&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A signed JWT is just a string, though it can be thought of as three strings joined
by dots.&lt;/p&gt;
&lt;img alt=&quot;A illustration of the strings in a JSON Web Token&quot; decoding=&quot;async&quot; height=&quot;246&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/QMpK203iFn9FIWrnFxa3.svg&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;The first and second strings (The JWT info and JWT data) are pieces of
JSON that have been base64 encoded, meaning it&#39;s publicly readable.&lt;/p&gt;
&lt;p&gt;The first string is information about the JWT itself, indicating which algorithm
was used to create the signature.&lt;/p&gt;
&lt;p&gt;The JWT info for web push must contain the following information:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;typ&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;JWT&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;alg&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ES256&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The second string is the JWT Data. This provides information about the sender of the JWT, who
it&#39;s intended for and how long it&#39;s valid.&lt;/p&gt;
&lt;p&gt;For web push, the data would have this format:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;aud&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://some-push-service.org&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;exp&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1469618703&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;sub&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;mailto:example@web-push-book.org&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The &lt;code&gt;aud&lt;/code&gt; value is the &amp;quot;audience&amp;quot;, i.e. who the JWT is for. For web push the
audience is the push service, so we set it to the &lt;strong&gt;origin of the push
service&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;exp&lt;/code&gt; value is the expiration of the JWT, this prevent snoopers from being
able to re-use a JWT if they intercept it. The expiration is a timestamp in
seconds and must be no longer 24 hours.&lt;/p&gt;
&lt;p&gt;In Node.js the expiration is set using:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;floor&lt;/span&gt;&lt;span class=&quot;token punctuation&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 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 number&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;It&#39;s 12 hours rather than 24 hours to avoid
any issues with clock differences between the sending application and the push service.&lt;/p&gt;
&lt;p&gt;Finally, the &lt;code&gt;sub&lt;/code&gt; value needs to be either a URL or a &lt;code&gt;mailto&lt;/code&gt; email address.
This is so that if a push service needed to reach out to sender, it can find
contact information from the JWT. (This is why the web-push library needed an
email address).&lt;/p&gt;
&lt;p&gt;Just like the JWT Info, the JWT Data is encoded as a URL safe base64
string.&lt;/p&gt;
&lt;p&gt;The third string, the signature, is the result of taking the first two strings
(the JWT Info and JWT Data), joining them with a dot character, which we&#39;ll
call the &amp;quot;unsigned token&amp;quot;, and signing it.&lt;/p&gt;
&lt;p&gt;The signing process requires encrypting the &amp;quot;unsigned token&amp;quot; using ES256. According to the &lt;a href=&quot;https://tools.ietf.org/html/rfc7519&quot; rel=&quot;noopener&quot;&gt;JWT
spec&lt;/a&gt;, ES256 is short for &amp;quot;ECDSA using the P-256 curve and
the SHA-256 hash algorithm&amp;quot;. Using web crypto you can create the signature like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Utility function for UTF-8 encoding a string to an ArrayBuffer.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; utf8Encoder &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;TextEncoder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;utf-8&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// The unsigned token is the concatenation of the URL-safe base64 encoded&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// header and body.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; unsignedToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Sign the |unsignedToken| using ES256 (SHA-256 over ECDSA).&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; key &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;kty&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;EC&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;crv&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;P-256&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;uint8ArrayToBase64Url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;    applicationServerKeys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;publicKey&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subarray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;33&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&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;y&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;uint8ArrayToBase64Url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;    applicationServerKeys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;publicKey&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subarray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;33&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;65&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&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;d&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;uint8ArrayToBase64Url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;applicationServerKeys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;privateKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Sign the |unsignedToken| with the server&#39;s private key to generate&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// the signature.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; crypto&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;subtle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;importKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;jwk&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key&lt;span class=&quot;token punctuation&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;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;ECDSA&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;namedCurve&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;P-256&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;sign&#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 function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;key&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;return&lt;/span&gt; crypto&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;subtle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sign&lt;/span&gt;&lt;span class=&quot;token punctuation&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;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;ECDSA&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;hash&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;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;SHA-256&#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; key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; utf8Encoder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;encode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;unsignedToken&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;signature&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Signature: &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; signature&lt;span class=&quot;token punctuation&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;A push service can validate a JWT using the public application server key
to decrypt the signature and make sure the decrypted string is the same
as the &amp;quot;unsigned token&amp;quot; (i.e. the first two strings in the JWT).&lt;/p&gt;
&lt;p&gt;The signed JWT (i.e. all three strings joined by dots), is sent to the web
push service as the &lt;code&gt;Authorization&lt;/code&gt; header with &lt;code&gt;WebPush &lt;/code&gt; prepended, like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Authorization: &#39;WebPush [JWT Info].[JWT Data].[Signature]&#39;;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The Web Push Protocol also states the public application server key must be
sent in the &lt;code&gt;Crypto-Key&lt;/code&gt; header as a URL safe base64 encoded string with
&lt;code&gt;p256ecdsa=&lt;/code&gt; prepended to it.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Crypto-Key: p256ecdsa=[URL Safe Base64 Public Application Server Key]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;the-payload-encryption&quot;&gt;The Payload Encryption &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#the-payload-encryption&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Next let&#39;s look at how we can send a payload with a push message so that when our web app
receives a push message, it can access the data it receives.&lt;/p&gt;
&lt;p&gt;A common question that arises from any who&#39;ve used other push services is why does the web push
payload need to be encrypted? With native apps, push messages can send data as plain text.&lt;/p&gt;
&lt;p&gt;Part of the beauty of web push is that because all push services use the
same API (the web push protocol), developers don&#39;t have to care who the
push service is. We can make a request in the right format and expect a
push message to be sent. The downside of this is that developers could
conceivably send messages to a push service that isn&#39;t trustworthy. By
encrypting the payload, a push service can&#39;t read the data that&#39;s sent.
Only the browser can decrypt the information. This protects the user&#39;s
data.&lt;/p&gt;
&lt;p&gt;The encryption of the payload is defined in the &lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-webpush-encryption&quot; rel=&quot;noopener&quot;&gt;Message Encryption
spec&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Before we look at the specific steps to encrypt a push messages payload,
we should cover some techniques that&#39;ll be used during the encryption
process. (Massive hat tip to Mat Scales for his excellent article on push
encryption.)&lt;/p&gt;
&lt;h3 id=&quot;ecdh-and-hkdf&quot;&gt;ECDH and HKDF &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#ecdh-and-hkdf&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Both ECDH and HKDF are used throughout the encryption process and offer benefits for the
purpose of encrypting information.&lt;/p&gt;
&lt;h4 id=&quot;ecdh-elliptic-curve-diffie-hellman-key-exchange&quot;&gt;ECDH: Elliptic Curve Diffie-Hellman key exchange &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#ecdh-elliptic-curve-diffie-hellman-key-exchange&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Imagine you have two people who want to share information, Alice and Bob.
Both Alice and Bob have their own public and private keys. Alice and Bob
share their public keys with each other.&lt;/p&gt;
&lt;p&gt;The useful property of keys generated with ECDH is that Alice can use her
private key and Bob&#39;s public key to create secret value &#39;X&#39;. Bob can do
the same, taking his private key and Alice&#39;s public key to
independently create the same value &#39;X&#39;. This makes &#39;X&#39; a shared secret
and Alice and Bob only had to share their public key. Now Bob and Alice
can use &#39;X&#39; to encrypt and decrypt messages between them.&lt;/p&gt;
&lt;p&gt;ECDH, to the best of my knowledge, defines the properties of curves which allow this &amp;quot;feature&amp;quot;
of making a shared secret &#39;X&#39;.&lt;/p&gt;
&lt;p&gt;This is a high level explanation of ECDH, if you want to learn more &lt;a href=&quot;https://www.youtube.com/watch?v=F3zzNa42-tQ&quot; rel=&quot;noopener&quot;&gt;I recommend checking out this video&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In terms of code; most languages / platforms come with libraries to make it
easy to generate these keys.&lt;/p&gt;
&lt;p&gt;In node we&#39;d do the following:&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; keyCurve &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; crypto&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createECDH&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;prime256v1&#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;keyCurve&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generateKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; publicKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; keyCurve&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPublicKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&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; privateKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; keyCurve&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPrivateKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h4 id=&quot;hkdf-hmac-based-key-derivation-function&quot;&gt;HKDF: HMAC based key derivation function &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#hkdf-hmac-based-key-derivation-function&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Wikipedia has a succinct description of &lt;a href=&quot;https://tools.ietf.org/html/rfc5869&quot; rel=&quot;noopener&quot;&gt;HKDF&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;HKDF is an HMAC based key derivation function that transforms any weak key
material into cryptographically strong key material. It can be used, for
example, to convert Diffie Hellman exchanged shared secrets into key material
suitable for use in encryption, integrity checking or authentication.&lt;/p&gt;
&lt;p&gt;Essentially, HKDF will take input that is not particular secure and make it more secure.&lt;/p&gt;
&lt;p&gt;The spec defining this encryption requires use of SHA-256 as our hash algorithm
and the resulting keys for HKDF in web push should be no longer than 256 bits
(32 bytes).&lt;/p&gt;
&lt;p&gt;In node this could be implemented like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Simplified HKDF, returning keys up to 32 bytes long&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hkdf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;salt&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ikm&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; info&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; length&lt;/span&gt;&lt;span class=&quot;token punctuation&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;// Extract&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; keyHmac &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; crypto&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createHmac&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;sha256&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; salt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  keyHmac&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ikm&lt;span class=&quot;token punctuation&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; key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; keyHmac&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;digest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Expand&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; infoHmac &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; crypto&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createHmac&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;sha256&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  infoHmac&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// A one byte long buffer containing only 0x01&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ONE_BUFFER&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Buffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  infoHmac&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ONE_BUFFER&lt;/span&gt;&lt;span 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; infoHmac&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;digest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span 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;Hat tip to &lt;a href=&quot;https://web.dev/web/updates/2016/03/web-push-encryption&quot;&gt;Mat Scale&#39;s article for this example code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This loosely covers &lt;a href=&quot;https://en.wikipedia.org/wiki/Elliptic_curve_Diffie%E2%80%93Hellman&quot; rel=&quot;noopener&quot;&gt;ECDH&lt;/a&gt;
and &lt;a href=&quot;https://tools.ietf.org/html/rfc5869&quot; rel=&quot;noopener&quot;&gt;HKDF&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;ECDH a secure way to share public keys and generate a shared secret. HKDF is a way to take
insecure material and make it secure.&lt;/p&gt;
&lt;p&gt;This will be used during the encryption of our payload. Next let&#39;s look at what we take as
input and how that&#39;s encrypted.&lt;/p&gt;
&lt;h4 id=&quot;inputs&quot;&gt;Inputs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#inputs&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When we want to send a push message to a user with a payload, there are three inputs we need:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The payload itself.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;auth&lt;/code&gt; secret from the &lt;code&gt;PushSubscription&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;p256dh&lt;/code&gt; key from the &lt;code&gt;PushSubscription&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We&#39;ve seen the &lt;code&gt;auth&lt;/code&gt; and &lt;code&gt;p256dh&lt;/code&gt; values being retrieved from a &lt;code&gt;PushSubscription&lt;/code&gt; but for a
quick reminder, given a subscription we&#39;d need these values:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;subscription&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toJSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;auth&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;subscription&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toJSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;p256dh&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;subscription&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;auth&#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;subscription&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;p256dh&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The &lt;code&gt;auth&lt;/code&gt; value should be treated as a secret and not shared outside of your application.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;p256dh&lt;/code&gt; key is a public key, this is sometimes referred to as the client public key. Here
we&#39;ll refer to &lt;code&gt;p256dh&lt;/code&gt; as the subscription public key. The subscription public key is generated
by the browser. The browser will keep the private key secret and use it for decrypting the
payload.&lt;/p&gt;
&lt;p&gt;These three values, &lt;code&gt;auth&lt;/code&gt;, &lt;code&gt;p256dh&lt;/code&gt; and &lt;code&gt;payload&lt;/code&gt; are needed as inputs and the result of the
encryption process will be the encrypted payload, a salt value and a public key used just for
encrypting the data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Salt&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The salt needs to be 16 bytes of random data. In NodeJS, we&#39;d do the following to create a salt:&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; salt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; crypto&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;randomBytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Public / Private Keys&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The public and private keys should be generated using a P-256 elliptic curve,
which we&#39;d do in Node like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; localKeysCurve &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; crypto&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createECDH&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;prime256v1&#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;localKeysCurve&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generateKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; localPublicKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; localKeysCurve&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPublicKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&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; localPrivateKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; localKeysCurve&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPrivateKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&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;We&#39;ll refer to these keys as &amp;quot;local keys&amp;quot;. They are used &lt;em&gt;just&lt;/em&gt; for encryption and have
&lt;em&gt;nothing&lt;/em&gt; to do with application server keys.&lt;/p&gt;
&lt;p&gt;With the payload, auth secret and subscription public key as inputs and with a newly generated
salt and set of local keys, we are ready to actually do some encryption.&lt;/p&gt;
&lt;h4 id=&quot;shared-secret&quot;&gt;Shared secret &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#shared-secret&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The first step is to create a shared secret using the subscription public key and our new
private key (remember the ECDH explanation with Alice and Bob? Just like that).&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; sharedSecret &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; localKeysCurve&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;computeSecret&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;  subscription&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;p256dh&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token string&quot;&gt;&#39;base64&#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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This is used in the next step to calculate the Pseudo Random Key (PRK).&lt;/p&gt;
&lt;h4 id=&quot;pseudo-random-key&quot;&gt;Pseudo random key &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#pseudo-random-key&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The Pseudo Random Key (PRK) is the combination of the push subscription&#39;s auth
secret, and the shared secret we just created.&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; authEncBuff &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;Buffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Content-Encoding: auth\0&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;utf8&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; prk &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hkdf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subscription&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;auth&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sharedSecret&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; authEncBuff&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You might be wondering what the &lt;code&gt;Content-Encoding: auth\0&lt;/code&gt; string is for.
In short, it doesn&#39;t have a clear purpose, although browsers could
decrypt an incoming message and look for the expected content-encoding.
The &lt;code&gt;\0&lt;/code&gt; adds a byte with a value of 0 to end of the Buffer. This is
expected by browsers decrypting the message who will expect so many bytes
for the content encoding, followed a byte with value 0, followed by the
encrypted data.&lt;/p&gt;
&lt;p&gt;Our Pseudo Random Key is simply running the auth, shared secret and a piece of encoding info
through HKDF (i.e. making it cryptographically stronger).&lt;/p&gt;
&lt;h4 id=&quot;context&quot;&gt;Context &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#context&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &amp;quot;context&amp;quot; is a set of bytes that is used to calculate two values later on in the encryption
browser. It&#39;s essentially an array of bytes containing the subscription public key and the
local public key.&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; keyLabel &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;Buffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;P-256\0&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;utf8&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Convert subscription public key into a buffer.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; subscriptionPubKey &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;Buffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subscription&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;p256dh&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;base64&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; subscriptionPubKeyLength &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;Uint8Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;subscriptionPubKeyLength&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;subscriptionPubKeyLength&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; subscriptionPubKey&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;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; localPublicKeyLength &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;Uint8Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;subscriptionPubKeyLength&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;subscriptionPubKeyLength&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; localPublicKey&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;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; contextBuffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Buffer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;  keyLabel&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  subscriptionPubKeyLength&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;buffer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  subscriptionPubKey&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  localPublicKeyLength&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;buffer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  localPublicKey&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The final context buffer is a label, the number of bytes in the subscription public key,
followed by the key itself, then the number of bytes local public key, followed by the key
itself.&lt;/p&gt;
&lt;p&gt;With this context value we can use it in the creation of a nonce and a content encryption key
(CEK).&lt;/p&gt;
&lt;h4 id=&quot;content-encryption-key-and-nonce&quot;&gt;Content encryption key and nonce &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#content-encryption-key-and-nonce&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A &lt;a href=&quot;https://en.wikipedia.org/wiki/Cryptographic_nonce&quot; rel=&quot;noopener&quot;&gt;nonce&lt;/a&gt; is a value that prevents replay
attacks as it should only be used once.&lt;/p&gt;
&lt;p&gt;The content encryption key (CEK) is the key that will ultimately be used to encrypt our payload.&lt;/p&gt;
&lt;p&gt;First we need to create the bytes of data for the nonce and CEK, which is simply a content
encoding string followed by the context buffer we just calculated:&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; nonceEncBuffer &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;Buffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Content-Encoding: nonce\0&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;utf8&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; nonceInfo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Buffer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;nonceEncBuffer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; contextBuffer&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cekEncBuffer &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;Buffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Content-Encoding: aesgcm\0&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cekInfo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Buffer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;cekEncBuffer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; contextBuffer&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&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 information is run through HKDF combining the salt and PRK with the nonceInfo and cekInfo:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// The nonce should be 12 bytes long&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; nonce &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hkdf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;salt&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; prk&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; nonceInfo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// The CEK should be 16 bytes long&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; contentEncryptionKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hkdf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;salt&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; prk&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cekInfo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&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 gives us our nonce and content encryption key.&lt;/p&gt;
&lt;h4 id=&quot;perform-the-encryption&quot;&gt;Perform the encryption &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#perform-the-encryption&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Now that we have our content encryption key, we can encrypt the payload.&lt;/p&gt;
&lt;p&gt;We create an AES128 cipher using the content encryption key
as the key and the nonce is an initialization vector.&lt;/p&gt;
&lt;p&gt;In Node this is done like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cipher &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; crypto&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createCipheriv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token string&quot;&gt;&#39;id-aes128-GCM&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  contentEncryptionKey&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  nonce&lt;span 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;Before we encrypt our payload, we need to define how much padding we wish
to add to the front of the payload. The reason we&#39;d want to add padding
is that it prevents the risk of eavesdroppers being able to determine
&amp;quot;types&amp;quot; of messages based on the payload size.&lt;/p&gt;
&lt;p&gt;You must add two bytes of padding to indicate the length of any additional padding.&lt;/p&gt;
&lt;p&gt;For example, if you added no padding, you&#39;d have two bytes with value 0, i.e. no padding exists, after these two bytes you&#39;ll be reading the payload. If you added 5 bytes of padding, the first two bytes will have a value of 5, so the consumer will then read an additional five bytes and then start reading the payload.&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; padding &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;Buffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; paddingLength&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// The buffer must be only zeros, except the length&lt;/span&gt;&lt;br /&gt;padding&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;padding&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeUInt16BE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;paddingLength&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;We then run our padding and payload through this cipher.&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; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cipher&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Buffer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;padding&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; payload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;cipher&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;final&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Append the auth tag to the result -&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// https://nodejs.org/api/crypto.html#crypto_cipher_getauthtag&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; encryptedPayload &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Buffer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cipher&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAuthTag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&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;We now have our encrypted payload. Yay!&lt;/p&gt;
&lt;p&gt;All that remains is to determine how this payload is sent to the push service.&lt;/p&gt;
&lt;h4 id=&quot;encrypted-payload-headers-and-body&quot;&gt;Encrypted payload headers &amp;amp; body &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#encrypted-payload-headers-and-body&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;To send this encrypted payload to the push service we need to define a few
different headers in our POST request.&lt;/p&gt;
&lt;h4 id=&quot;encryption-header&quot;&gt;Encryption header &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#encryption-header&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &#39;Encryption&#39; header must contain the &lt;em&gt;salt&lt;/em&gt; used for encrypting the payload.&lt;/p&gt;
&lt;p&gt;The 16 byte salt should be base64 URL safe encoded and added to the Encryption header, like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Encryption: salt=[URL Safe Base64 Encoded Salt]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h4 id=&quot;crypto-key-header&quot;&gt;Crypto-Key header &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#crypto-key-header&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;We saw that the &lt;code&gt;Crypto-Key&lt;/code&gt; header is used under the &#39;Application Server Keys&#39;
section to contain the public application server key.&lt;/p&gt;
&lt;p&gt;This header is also used to share the local public key used to encrypt
the payload.&lt;/p&gt;
&lt;p&gt;The resulting header looks like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Crypto-Key: dh=[URL Safe Base64 Encoded Local Public Key String]; p256ecdsa=[URL Safe Base64 Encoded Public Application Server Key]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h4 id=&quot;content-type,-length-and-encoding-headers&quot;&gt;Content type, length &amp;amp; encoding headers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#content-type,-length-and-encoding-headers&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;Content-Length&lt;/code&gt; header is the number of bytes in the encrypted
payload. &#39;Content-Type&#39; and &#39;Content-Encoding&#39; headers are fixed values.
This is shown below.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Content-Length: [Number of Bytes in Encrypted Payload]&lt;br /&gt;Content-Type: &#39;application/octet-stream&#39;&lt;br /&gt;Content-Encoding: &#39;aesgcm&#39;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;With these headers set, we need to send the encrypted payload as the body
of our request. Notice that the &lt;code&gt;Content-Type&lt;/code&gt; is set to
&lt;code&gt;application/octet-stream&lt;/code&gt;. This is because the encrypted payload must be
sent as a stream of bytes.&lt;/p&gt;
&lt;p&gt;In NodeJS we would do this like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; pushRequest &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;httpsOptions&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;pushResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;pushRequest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;encryptedPayload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;pushRequest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h4 id=&quot;more-headers&quot;&gt;More headers? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#more-headers&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;We&#39;ve covered the headers used for JWT / Application Server Keys (i.e. how to identify the
application with the push service) and we&#39;ve covered the headers used to send an encrypted
payload.&lt;/p&gt;
&lt;p&gt;There are additional headers that push services use to alter the behavior of
sent messages. Some of these headers are required, while others are optional.&lt;/p&gt;
&lt;h4 id=&quot;ttl-header&quot;&gt;TTL header &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#ttl-header&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Required&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;TTL&lt;/code&gt; (or time to live) is an integer specifying the number of seconds
you want your push message to live on the push service before it&#39;s
delivered. When the &lt;code&gt;TTL&lt;/code&gt; expires, the message will be removed from the
push service queue and it won&#39;t be delivered.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;TTL: [Time to live in seconds]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If you set a &lt;code&gt;TTL&lt;/code&gt; of zero, the push service will attempt to deliver the
message immediately, &lt;strong&gt;but&lt;/strong&gt; if the device can&#39;t be reached, your message
will be immediately dropped from the push service queue.&lt;/p&gt;
&lt;p&gt;Technically a push service can reduce the &lt;code&gt;TTL&lt;/code&gt; of a push message if it
wants. You can tell if this has happened by examining the &lt;code&gt;TTL&lt;/code&gt; header in
the response from a push service.&lt;/p&gt;
&lt;h5 id=&quot;topic&quot;&gt;Topic &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#topic&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;Optional&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Topics are strings that can be used to replace a pending messages with a
new message if they have matching topic names.&lt;/p&gt;
&lt;p&gt;This is useful in scenarios where multiple messages are sent while a
device is offline, and you really only want a user to see the latest
message when the device is turned on.&lt;/p&gt;
&lt;h5 id=&quot;urgency&quot;&gt;Urgency &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#urgency&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;Optional&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Urgency indicates to the push service how important a message is to the user. This
can be used by the push service to help conserve the battery life of a user&#39;s device by only
waking up for important messages when battery is low.&lt;/p&gt;
&lt;p&gt;The header value is defined as shown below. The default
value is &lt;code&gt;normal&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Urgency: [very-low | low | normal | high]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h4 id=&quot;everything-together&quot;&gt;Everything together &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#everything-together&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;If you have further questions about how this all works you can always see how libraries trigger
push messages on &lt;a href=&quot;https://github.com/web-push-libs&quot; rel=&quot;noopener&quot;&gt;the web-push-libs org&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once you have an encrypted payload, and the headers above, you just need to make a POST request
to the &lt;code&gt;endpoint&lt;/code&gt; in a &lt;code&gt;PushSubscription&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So what do we do with the response to this POST request?&lt;/p&gt;
&lt;h4 id=&quot;response-from-push-service&quot;&gt;Response from push service &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#response-from-push-service&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Once you&#39;ve made a request to a push service, you need to check the status code
of the response as that&#39;ll tell you whether the request was successful
or not.&lt;/p&gt;
&lt;table&gt;
  &lt;tr&gt;
    &lt;th&gt;Status Code&lt;/th&gt;
    &lt;th&gt;Description&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;201&lt;/td&gt;
    &lt;td&gt;Created. The request to send a push message was received and accepted.
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;429&lt;/td&gt;
    &lt;td&gt;Too many requests. Meaning your application server has reached a rate
    limit with a push service. The push service should include a &#39;Retry-After&#39;
    header to indicate how long before another request can be made.&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;400&lt;/td&gt;
    &lt;td&gt;Invalid request. This generally means one of your headers is invalid
    or improperly formatted.&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;404&lt;/td&gt;
    &lt;td&gt;Not Found. This is an indication that the subscription is expired
    and can&#39;t be used. In this case you should delete the `PushSubscription`
    and wait for the client to resubscribe the user.&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;410&lt;/td&gt;
    &lt;td&gt;Gone. The subscription is no longer valid and should be removed
    from application server. This can be reproduced by calling
    `unsubscribe()` on a `PushSubscription`.&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;413&lt;/td&gt;
    &lt;td&gt;Payload size too large. The minimum size payload a push service must
    support is &lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-webpush-protocol-10#section-7.2&quot;&gt;4096 bytes&lt;/a&gt;
(or 4kb).&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;h2 id=&quot;where-to-go-next&quot;&gt;Where to go next &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#where-to-go-next&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-overview/&quot;&gt;Web Push Notification Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-how-push-works/&quot;&gt;How Push Works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-subscribing-a-user/&quot;&gt;Subscribing a User&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-permissions-ux/&quot;&gt;Permission UX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sending-messages-with-web-push-libraries/&quot;&gt;Sending Messages with Web Push Libraries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Web Push Protocol&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-handling-messages/&quot;&gt;Handling Push Events&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-display-a-notification/&quot;&gt;Displaying a Notification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-notification-behaviour/&quot;&gt;Notification Behavior&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-common-notification-patterns/&quot;&gt;Common Notification Patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-faq/&quot;&gt;Push Notifications FAQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/&quot;&gt;Common Issues and Reporting Bugs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;code-labs&quot;&gt;Code labs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-web-push-protocol/#code-labs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-client-codelab/&quot;&gt;Build a push notification client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-server-codelab/&quot;&gt;Build a push notification server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Matt Gaunt</name>
    </author>
  </entry>
  
  <entry>
    <title>Common notification patterns</title>
    <link href="https://web.dev/push-notifications-common-notification-patterns/"/>
    <updated>2016-06-30T00:00:00Z</updated>
    <id>https://web.dev/push-notifications-common-notification-patterns/</id>
    <content type="html" mode="escaped">&lt;p&gt;We&#39;re going to look at some common implementation patterns for web push.&lt;/p&gt;
&lt;p&gt;This will involve using a few different APIs that are available in the service worker.&lt;/p&gt;
&lt;h2 id=&quot;notification-close-event&quot;&gt;Notification close event &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-notification-patterns/#notification-close-event&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the last section we saw how we can listen for &lt;code&gt;notificationclick&lt;/code&gt; events.&lt;/p&gt;
&lt;p&gt;There is also a &lt;code&gt;notificationclose&lt;/code&gt; event that is called if the user dismisses one of your
notifications (i.e. rather than clicking the notification, the user clicks the cross or swipes the
notification away).&lt;/p&gt;
&lt;p&gt;This event is normally used for analytics to track user engagement with notifications.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;notificationclose&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dismissedNotification &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;notification&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; promiseChain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;notificationCloseAnalytics&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitUntil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;promiseChain&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;adding-data-to-a-notification&quot;&gt;Adding data to a notification &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-notification-patterns/#adding-data-to-a-notification&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When a push message is received it&#39;s common to have data that is only
useful if the user has clicked the notification. For example, the URL
that should be opened when a notification is clicked.&lt;/p&gt;
&lt;p&gt;The easiest way to take data from a push event and attach it to a
notification is to add a &lt;code&gt;data&lt;/code&gt; parameter to the options object passed
into &lt;code&gt;showNotification()&lt;/code&gt;, like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token string&quot;&gt;&#39;This notification has data attached to it that is printed &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token string&quot;&gt;&quot;to the console when it&#39;s clicked.&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;tag&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;data-notification&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;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;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&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;message&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Hello, World!&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Notification with Data&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Inside a click handler, the data can be accessed with &lt;code&gt;event.notification.data&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; notificationData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;notification&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&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;&#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;The notification data has the following parameters:&#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;Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;notificationData&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;key&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;notificationData&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span 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;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;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;open-a-window&quot;&gt;Open a window &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-notification-patterns/#open-a-window&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One of the most common responses to a notification is to open a
window / tab to a specific URL. We can do this with the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Clients/openWindow&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;clients.openWindow()&lt;/code&gt;&lt;/a&gt;
API.&lt;/p&gt;
&lt;p&gt;In our &lt;code&gt;notificationclick&lt;/code&gt; event, we&#39;d run some code like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; examplePage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/demos/notification-examples/example-page.html&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; promiseChain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; clients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;openWindow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;examplePage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitUntil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;promiseChain&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In the next section, we&#39;ll look at how to check if the page we want to direct the user to is
already open or not. This way, we can focus the open tab rather than opening new
tabs.&lt;/p&gt;
&lt;h2 id=&quot;focus-an-existing-window&quot;&gt;Focus an existing window &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-notification-patterns/#focus-an-existing-window&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When it&#39;s possible, we should focus a window rather than open a new window every time the user
clicks a notification.&lt;/p&gt;
&lt;p&gt;Before we look at how to achieve this, it&#39;s worth highlighting that this
is &lt;strong&gt;only possible for pages on your origin&lt;/strong&gt;. This is because we can
only see what pages are open that belong to our site. This prevents
developers from being able to see all the sites their users are viewing.&lt;/p&gt;
&lt;p&gt;Taking the previous example, we&#39;ll alter the code to see if
&lt;code&gt;/demos/notification-examples/example-page.html&lt;/code&gt; is already open.&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; urlToOpen &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;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;examplePage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;location&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;origin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;href&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; promiseChain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; clients&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matchAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;window&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;includeUncontrolled&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;windowClients&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; matchingClient &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; windowClients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; windowClient &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; windowClients&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;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;windowClient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; urlToOpen&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        matchingClient &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; windowClient&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;matchingClient&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; matchingClient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;focus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; clients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;openWindow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;urlToOpen&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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;promiseChain&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;Let&#39;s step through the code.&lt;/p&gt;
&lt;p&gt;First we parse our example page using the URL API. This is a neat trick I picked up from &lt;a href=&quot;https://twitter.com/jeffposnick&quot; rel=&quot;noopener&quot;&gt;Jeff
Posnick&lt;/a&gt;. Calling &lt;code&gt;new URL()&lt;/code&gt; with the &lt;code&gt;location&lt;/code&gt; object will
return an absolute URL if the string passed in is relative (i.e. &lt;code&gt;/&lt;/code&gt; will become
&lt;code&gt;https://example.com/&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;We make the URL absolute so we can match it against window URL&#39;s later on.&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; urlToOpen &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;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;examplePage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;location&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;origin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;href&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Then we get a list of the &lt;code&gt;WindowClient&lt;/code&gt; objects, which is the list of the
currently open tabs and windows. (Remember these are tabs for your origin only.)&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; promiseChain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; clients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matchAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;window&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;includeUncontrolled&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The options passed into &lt;code&gt;matchAll&lt;/code&gt; inform the browser that we only want
to search for &amp;quot;window&amp;quot; type clients (i.e. just look for tabs and windows
and exclude web workers). &lt;code&gt;includeUncontrolled&lt;/code&gt; allows us to search for
all tabs from your origin that are not controlled by the current service
worker, i.e. the service worker running this code. Generally, you&#39;ll
always want &lt;code&gt;includeUncontrolled&lt;/code&gt; to be true when calling &lt;code&gt;matchAll()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We capture the returned promise as &lt;code&gt;promiseChain&lt;/code&gt; so that we can pass it into
&lt;code&gt;event.waitUntil()&lt;/code&gt; later on, keeping our service worker alive.&lt;/p&gt;
&lt;p&gt;When the &lt;code&gt;matchAll()&lt;/code&gt; promise resolves, we iterate through the returned window clients and
compare their URLs to the URL we want to open. If we find a match, we focus that
client, which will bring that window to the users attention. Focusing is done with the
&lt;code&gt;matchingClient.focus()&lt;/code&gt; call.&lt;/p&gt;
&lt;p&gt;If we can&#39;t find a matching client, we open a new window, same as in the previous section.&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 punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;windowClients&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; matchingClient &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; windowClients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; windowClient &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; windowClients&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;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;windowClient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; urlToOpen&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      matchingClient &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; windowClient&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;matchingClient&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; matchingClient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;focus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; clients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;openWindow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;urlToOpen&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; We are returning the promise for &lt;code&gt;matchingClient.focus()&lt;/code&gt; and &lt;code&gt;clients.openWindow()&lt;/code&gt; so that the promises are accounted for in our promise chain. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;merging-notifications&quot;&gt;Merging notifications &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-notification-patterns/#merging-notifications&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We saw that adding a tag to a notification opts in to a behavior where any
existing notification with the same tag is replaced.&lt;/p&gt;
&lt;p&gt;You can however get more sophisticated with the collapsing of notifications using the
Notifications API. Consider a chat app, where the developer might want a new notification to
show a message similar to &amp;quot;You have two messages from Matt&amp;quot; rather than just showing the latest
message.&lt;/p&gt;
&lt;p&gt;You can do this, or manipulate current notifications in other ways, using the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/ServiceWorkerRegistration/getNotifications&quot; rel=&quot;noopener&quot;&gt;registration.getNotifications()&lt;/a&gt;
API which gives you access to all the currently visible notifications for your web app.&lt;/p&gt;
&lt;p&gt;Let&#39;s look at how we could use this API to implement the chat example.&lt;/p&gt;
&lt;p&gt;In our chat app, let&#39;s assume each notification has some data which includes a username.&lt;/p&gt;
&lt;p&gt;The first thing we&#39;ll want to do is find any open notifications for a user with a specific
username. We&#39;ll get &lt;code&gt;registration.getNotifications()&lt;/code&gt; and loop over them and check the
&lt;code&gt;notification.data&lt;/code&gt; for a specific username:&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; promiseChain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getNotifications&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;notifications&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; currentNotification&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; notifications&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;notifications&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; notifications&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userName &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; userName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      currentNotification &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; notifications&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; currentNotification&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The next step is to replace this notification with a new notification.&lt;/p&gt;
&lt;p&gt;In this fake message app, we&#39;ll track the number of new messages by adding a count to our new
notification&#39;s data and increment it with each new notification.&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 punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;currentNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; notificationTitle&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; options &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;icon&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; userIcon&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentNotification&lt;span class=&quot;token punctuation&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;// We have an open notification, let&#39;s do something with it.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; messageCount &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; currentNotification&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;newMessageCount &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;You have &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;messageCount&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; new messages from &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;userName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data &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;userName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; userName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;newMessageCount&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; messageCount&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;    notificationTitle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;New Messages from &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;userName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Remember to close the old notification.&lt;/span&gt;&lt;br /&gt;    currentNotification&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 keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;userMessage&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data &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;userName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; userName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;newMessageCount&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;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;    notificationTitle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;New Message from &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;userName&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;    notificationTitle&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    options&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;If there is a notification currently displayed, we increment the message count and set the
notification title and body message accordingly. If there
are no notifications, we create a new notification with a &lt;code&gt;newMessageCount&lt;/code&gt; of 1.&lt;/p&gt;
&lt;p&gt;The result is that the first message would look like this:&lt;/p&gt;
&lt;img alt=&quot;First notification without merging.&quot; decoding=&quot;async&quot; height=&quot;116&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 380px) 380px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/plgFelmhahN06Unqr2dr.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/plgFelmhahN06Unqr2dr.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/plgFelmhahN06Unqr2dr.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/plgFelmhahN06Unqr2dr.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/plgFelmhahN06Unqr2dr.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/plgFelmhahN06Unqr2dr.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/plgFelmhahN06Unqr2dr.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/plgFelmhahN06Unqr2dr.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/plgFelmhahN06Unqr2dr.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/plgFelmhahN06Unqr2dr.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/plgFelmhahN06Unqr2dr.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/plgFelmhahN06Unqr2dr.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/plgFelmhahN06Unqr2dr.png?auto=format&amp;w=760 760w&quot; width=&quot;380&quot; /&gt;
&lt;p&gt;A second notification would collapse the notifications into this:&lt;/p&gt;
&lt;img alt=&quot;Second notification with merging.&quot; decoding=&quot;async&quot; height=&quot;121&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 380px) 380px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bKoZiyLJI8DVYOPQBIGv.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bKoZiyLJI8DVYOPQBIGv.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bKoZiyLJI8DVYOPQBIGv.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bKoZiyLJI8DVYOPQBIGv.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bKoZiyLJI8DVYOPQBIGv.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bKoZiyLJI8DVYOPQBIGv.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bKoZiyLJI8DVYOPQBIGv.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bKoZiyLJI8DVYOPQBIGv.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bKoZiyLJI8DVYOPQBIGv.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bKoZiyLJI8DVYOPQBIGv.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bKoZiyLJI8DVYOPQBIGv.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bKoZiyLJI8DVYOPQBIGv.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/bKoZiyLJI8DVYOPQBIGv.png?auto=format&amp;w=760 760w&quot; width=&quot;380&quot; /&gt;
&lt;p&gt;The nice thing with this approach is that if your user witnesses the
notifications appearing one over the other, it&#39;ll look and feel more cohesive
than just replacing the notification with the latest message.&lt;/p&gt;
&lt;h2 id=&quot;the-exception-to-the-rule&quot;&gt;The exception to the rule &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-notification-patterns/#the-exception-to-the-rule&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&#39;ve been stating that you &lt;strong&gt;must&lt;/strong&gt; show a notification when you receive a push and this is
true &lt;em&gt;most&lt;/em&gt; of the time. The one scenario where you don&#39;t have to show a notification is when
the user has your site open and focused.&lt;/p&gt;
&lt;p&gt;Inside your push event, you can check whether you need to show a notification or not by
examining the window clients and looking for a focused window.&lt;/p&gt;
&lt;p&gt;The code to getting all the windows and looking for a focused window looks like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isClientFocused&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; clients&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matchAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;window&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;includeUncontrolled&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;windowClients&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; clientIsFocused &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; windowClients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; windowClient &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; windowClients&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;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;windowClient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;focused&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;          clientIsFocused &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; clientIsFocused&lt;span 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;We use &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Clients/matchAll&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;clients.matchAll()&lt;/code&gt;&lt;/a&gt;
to get all of our window clients and then we loop over them checking the &lt;code&gt;focused&lt;/code&gt; parameter.&lt;/p&gt;
&lt;p&gt;Inside our push event, we&#39;d use this function to decide if we need to show a notification:&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; promiseChain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isClientFocused&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;clientIsFocused&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;clientIsFocused&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Don&#39;t need to show a notification.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Client isn&#39;t focused, we need to show a notification.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Had to show a notification.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitUntil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;promiseChain&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;message-a-page-from-a-push-event&quot;&gt;Message a page from a push event &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-notification-patterns/#message-a-page-from-a-push-event&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We&#39;ve seen that you can skip showing a notification if the user is currently on your site. But
what if you still want to let the user know that an event has occurred, but a notification is
too heavy handed?&lt;/p&gt;
&lt;p&gt;One approach is to send a message from the service worker to the page, this way the web page
can show a notification or an update to the user, informing them of the event. This is useful for
situations when a subtle notification in the page is better and friendlier for the user.&lt;/p&gt;
&lt;p&gt;Let&#39;s say we&#39;ve received a push, checked that our web app is currently focused,
then we can &amp;quot;post a message&amp;quot; to each open page, like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; promiseChain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isClientFocused&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;clientIsFocused&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;clientIsFocused&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    windowClients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;windowClient&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;      windowClient&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;message&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Received a push message.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;No focused windows&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Had to show a notification instead of messaging each page.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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;promiseChain&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In each of the pages, we listen for messages by adding a message event
listener:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;serviceWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  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;Received a message from service worker: &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In this message listener, you could do anything you want, show a custom UI on
your page or completely ignore the message.&lt;/p&gt;
&lt;p&gt;It&#39;s also worth noting that if you don&#39;t define a message listener in your web page, the
messages from the service worker will not do anything.&lt;/p&gt;
&lt;h2 id=&quot;cache-a-page-and-open-a-window&quot;&gt;Cache a page and open a window &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-notification-patterns/#cache-a-page-and-open-a-window&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One scenario that is out of the scope of this guide but worth discussing is that you can
improve the overall UX of your web app by caching web pages you expect users to visit after
clicking on your notification.&lt;/p&gt;
&lt;p&gt;This requires having your service worker set-up to handle &lt;code&gt;fetch&lt;/code&gt; events,
but if you implement a &lt;code&gt;fetch&lt;/code&gt; event listener, make sure you take
advantage of it in your &lt;code&gt;push&lt;/code&gt; event by caching the page and assets
you&#39;ll need before showing your notification.&lt;/p&gt;
&lt;h2 id=&quot;browser-compatibility&quot;&gt;Browser compatibility &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-notification-patterns/#browser-compatibility&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;the-notificationclose-event&quot;&gt;The &lt;code&gt;notificationclose&lt;/code&gt; event &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-notification-patterns/#the-notificationclose-event&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;wdi-browser-compat&quot;&gt;
  &lt;span class=&quot;wdi-browser-compat__label&quot;&gt;Browser support&lt;/span&gt;
  &lt;ul class=&quot;wdi-browser-compat__items&quot;&gt;
    &lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;chrome&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Chrome 50, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      50
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox 44, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      44
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Edge 17, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      17
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Safari 16, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      16
    &lt;/span&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/ServiceWorkerGlobalScope/notificationclose_event#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;h3 id=&quot;clientsopenwindow&quot;&gt;&lt;code&gt;Clients.openWindow()&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-notification-patterns/#clientsopenwindow&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;wdi-browser-compat&quot;&gt;
  &lt;span class=&quot;wdi-browser-compat__label&quot;&gt;Browser support&lt;/span&gt;
  &lt;ul class=&quot;wdi-browser-compat__items&quot;&gt;
    &lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;chrome&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Chrome 40, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      40
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox 44, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      44
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Edge 17, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      17
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Safari 11.1, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      11.1
    &lt;/span&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/Clients/openWindow#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;h3 id=&quot;serviceworkerregistrationgetnotifications&quot;&gt;&lt;code&gt;ServiceWorkerRegistration.getNotifications()&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-notification-patterns/#serviceworkerregistrationgetnotifications&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;wdi-browser-compat&quot;&gt;
  &lt;span class=&quot;wdi-browser-compat__label&quot;&gt;Browser support&lt;/span&gt;
  &lt;ul class=&quot;wdi-browser-compat__items&quot;&gt;
    &lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;chrome&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Chrome 40, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      40
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox 44, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      44
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Edge 17, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      17
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Safari 16, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      16
    &lt;/span&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/ServiceWorkerRegistration/getNotifications#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;h3 id=&quot;clientsmatchall&quot;&gt;&lt;code&gt;clients.matchAll()&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-notification-patterns/#clientsmatchall&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;wdi-browser-compat&quot;&gt;
  &lt;span class=&quot;wdi-browser-compat__label&quot;&gt;Browser support&lt;/span&gt;
  &lt;ul class=&quot;wdi-browser-compat__items&quot;&gt;
    &lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;chrome&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Chrome 42, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      42
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox 54, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      54
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Edge 17, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      17
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Safari 11.1, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      11.1
    &lt;/span&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/Clients/matchAll#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;For more information, check out this &lt;a href=&quot;https://web.dev/web/fundamentals/getting-started/primers/service-workers&quot;&gt;introduction to service workers
post&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;where-to-go-next&quot;&gt;Where to go next &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-notification-patterns/#where-to-go-next&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-overview/&quot;&gt;Web Push Notification Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-how-push-works/&quot;&gt;How Push Works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-subscribing-a-user/&quot;&gt;Subscribing a User&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-permissions-ux/&quot;&gt;Permission UX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sending-messages-with-web-push-libraries/&quot;&gt;Sending Messages with Web Push Libraries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-web-push-protocol/&quot;&gt;Web Push Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-handling-messages/&quot;&gt;Handling Push Events&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-display-a-notification/&quot;&gt;Displaying a Notification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-notification-behaviour/&quot;&gt;Notification Behavior&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Common Notification Patterns&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-faq/&quot;&gt;Push Notifications FAQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/&quot;&gt;Common Issues and Reporting Bugs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;code-labs&quot;&gt;Code labs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-common-notification-patterns/#code-labs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-client-codelab/&quot;&gt;Build a push notification client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-server-codelab/&quot;&gt;Build a push notification server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Matt Gaunt</name>
    </author>
  </entry>
  
  <entry>
    <title>Push events</title>
    <link href="https://web.dev/push-notifications-handling-messages/"/>
    <updated>2016-06-30T00:00:00Z</updated>
    <id>https://web.dev/push-notifications-handling-messages/</id>
    <content type="html" mode="escaped">&lt;p&gt;By this point, we covered subscribing a user and sending a push message. The next step is to
receive this push message on the user&#39;s device and display a notification (as well as do any other
work we might want to do).&lt;/p&gt;
&lt;h2 id=&quot;the-push-event&quot;&gt;The Push Event &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-handling-messages/#the-push-event&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When a message is received, it&#39;ll result in a push event being dispatched in your service worker.&lt;/p&gt;
&lt;p&gt;The code for setting up a push event listener should be pretty similar to any other event
listener you&#39;d write in JavaScript:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;push&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    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;This push event has data: &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    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;This push event has no data.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The weirdest bit of this code to most developers who are new to service workers is the &lt;code&gt;self&lt;/code&gt;
variable. &lt;code&gt;self&lt;/code&gt; is commonly used in Web Workers, which a service worker is. &lt;code&gt;self&lt;/code&gt; refers to
the global scope, kind of like &lt;code&gt;window&lt;/code&gt; in a web page. But for web workers and service workers,
&lt;code&gt;self&lt;/code&gt; refers to the worker itself.&lt;/p&gt;
&lt;p&gt;In the example above, &lt;code&gt;self.addEventListener()&lt;/code&gt; can be thought of as adding an event listener to
the service worker itself.&lt;/p&gt;
&lt;p&gt;Inside the push event example, we check if there is any data and print something to the console.&lt;/p&gt;
&lt;p&gt;There are other ways you can parse data from a push 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 comment&quot;&gt;// Returns string&lt;/span&gt;&lt;br /&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Parses data as JSON string and returns an Object&lt;/span&gt;&lt;br /&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Returns blob of data&lt;/span&gt;&lt;br /&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;blob&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Returns an arrayBuffer&lt;/span&gt;&lt;br /&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;arrayBuffer&lt;/span&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;Most people use &lt;code&gt;json()&lt;/code&gt; or &lt;code&gt;text()&lt;/code&gt; depending on what they are expecting from their application.&lt;/p&gt;
&lt;p&gt;This example demonstrates how to add a push event listener and how to access data, but it&#39;s
missing two very important pieces of functionality. It&#39;s not showing a notification and it&#39;s
not making use of &lt;code&gt;event.waitUntil()&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;wait-until&quot;&gt;Wait Until &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-handling-messages/#wait-until&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One of the things to understand about service workers is that you have little control over when
the service worker code is going to run. The browser decides when to wake it up and when to
terminate it. The only way you can tell the browser, &amp;quot;Hey, I&#39;m super busy doing important
stuff&amp;quot;, is to pass a promise into the &lt;code&gt;event.waitUntil()&lt;/code&gt; method. With this, the browser will
keep the service worker running until the promise you passed in has settled.&lt;/p&gt;
&lt;p&gt;With push events, there is an additional requirement that you must display a notification before
the promise you passed in has settled.&lt;/p&gt;
&lt;p&gt;Here&#39;s a basic example of showing a notification:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;push&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; promiseChain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Hello, World.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;waitUntil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;promiseChain&lt;span class=&quot;token punctuation&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;Calling &lt;code&gt;self.registration.showNotification()&lt;/code&gt; is the method that displays a notification to
the user and it returns a promise that will resolve once the notification has been displayed.&lt;/p&gt;
&lt;p&gt;For the sake of keeping this example as clear as possible, I&#39;ve assigned this promise to a
variable called &lt;code&gt;promiseChain&lt;/code&gt;. This is then passed into &lt;code&gt;event.waitUntil()&lt;/code&gt;. I know this is
very verbose, but I&#39;ve seen a number of issues that have culminated as a result of
misunderstanding what should be passed into &lt;code&gt;waitUntil()&lt;/code&gt; or as a result of broken promise
chains.&lt;/p&gt;
&lt;p&gt;A more complicated example with a network request for data and tracking the push event with
analytics 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;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;push&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; analyticsPromise &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;pushReceivedTracking&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&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; pushInfoPromise &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/api/get-more-data&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userName &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39; says...&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message&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; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; message&lt;br /&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; promiseChain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Promise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;    analyticsPromise&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    pushInfoPromise&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;    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;promiseChain&lt;span class=&quot;token punctuation&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;Here we are calling a function that returns a promise &lt;code&gt;pushReceivedTracking()&lt;/code&gt;,
which, for the sake of the example, we can pretend will make a network request
to our analytics provider. We are also making a network request, getting the
response and showing a notification using the responses data for the title and
message of the notification.&lt;/p&gt;
&lt;p&gt;We can ensure the service worker is kept alive while both of these tasks are done by combining
these promises with &lt;code&gt;Promise.all()&lt;/code&gt;. The resulting promise is passed into &lt;code&gt;event.waitUntil()&lt;/code&gt;
meaning the browser will wait until both promises have finished before checking that a notification
has been displayed and terminating the service worker.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; If you ever find your promise chains confusing or a little messy, I find that breaking things into functions helps to reduce complexity. I&#39;d also recommend &lt;a href=&quot;https://philipwalton.com/articles/untangling-deeply-nested-promise-chains/&quot;&gt;this blog post by Philip Walton on untangling promise chains&lt;/a&gt;. The main point to take away is that you should experiment with how promises can be written and chained to find a style that works for you. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The reason we should be concerned about &lt;code&gt;waitUntil()&lt;/code&gt; and how to use it is that one of the most
common issues developers face is that when the promise chain is incorrect / broken, Chrome will
show this &amp;quot;default&amp;quot; notification:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;An Image of the default notification in Chrome&quot; decoding=&quot;async&quot; height=&quot;291&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eeTfi2PuON5UCch3aMAN.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eeTfi2PuON5UCch3aMAN.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eeTfi2PuON5UCch3aMAN.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eeTfi2PuON5UCch3aMAN.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eeTfi2PuON5UCch3aMAN.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eeTfi2PuON5UCch3aMAN.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eeTfi2PuON5UCch3aMAN.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eeTfi2PuON5UCch3aMAN.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eeTfi2PuON5UCch3aMAN.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eeTfi2PuON5UCch3aMAN.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eeTfi2PuON5UCch3aMAN.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eeTfi2PuON5UCch3aMAN.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eeTfi2PuON5UCch3aMAN.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eeTfi2PuON5UCch3aMAN.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eeTfi2PuON5UCch3aMAN.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eeTfi2PuON5UCch3aMAN.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eeTfi2PuON5UCch3aMAN.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/eeTfi2PuON5UCch3aMAN.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Chrome will only show the &amp;quot;This site has been updated in the background.&amp;quot; notification when a
push message is received and the push event in the service worker &lt;strong&gt;does not&lt;/strong&gt; show a
notification after the promise passed to &lt;code&gt;event.waitUntil()&lt;/code&gt; has finished.&lt;/p&gt;
&lt;p&gt;The main reason developers get caught by this is that their code will
often call &lt;code&gt;self.registration.showNotification()&lt;/code&gt; but they &lt;strong&gt;aren&#39;t&lt;/strong&gt; doing
anything with the promise it returns. This intermittently results in the default notification
being displayed. For example, we could remove the return for
&lt;code&gt;self.registration.showNotification()&lt;/code&gt; in the example above and we run the risk of seeing this
notification.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;push&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; analyticsPromise &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;pushReceivedTracking&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&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; pushInfoPromise &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/api/get-more-data&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userName &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39; says...&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; message&lt;br /&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; promiseChain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Promise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;    analyticsPromise&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    pushInfoPromise&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;    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;promiseChain&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You can see how it&#39;s an easy thing to miss.&lt;/p&gt;
&lt;p&gt;Just remember - if you see that notification, check your promise chains and &lt;code&gt;event.waitUntil()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In the next section, we&#39;re going to look at what we can do to style notifications and
what content we can display.&lt;/p&gt;
&lt;h2 id=&quot;where-to-go-next&quot;&gt;Where to go next &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-handling-messages/#where-to-go-next&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-overview/&quot;&gt;Web Push Notification Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-how-push-works/&quot;&gt;How Push Works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-subscribing-a-user/&quot;&gt;Subscribing a User&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-permissions-ux/&quot;&gt;Permission UX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sending-messages-with-web-push-libraries/&quot;&gt;Sending Messages with Web Push Libraries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-web-push-protocol/&quot;&gt;Web Push Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Handling Push Events&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-display-a-notification/&quot;&gt;Displaying a Notification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-notification-behaviour/&quot;&gt;Notification Behavior&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-common-notification-patterns/&quot;&gt;Common Notification Patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-faq/&quot;&gt;Push Notifications FAQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/&quot;&gt;Common Issues and Reporting Bugs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;code-labs&quot;&gt;Code labs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-handling-messages/#code-labs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-client-codelab/&quot;&gt;Build a push notification client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-server-codelab/&quot;&gt;Build a push notification server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Matt Gaunt</name>
    </author>
  </entry>
  
  <entry>
    <title>Permission UX</title>
    <link href="https://web.dev/push-notifications-permissions-ux/"/>
    <updated>2016-06-30T00:00:00Z</updated>
    <id>https://web.dev/push-notifications-permissions-ux/</id>
    <content type="html" mode="escaped">&lt;p&gt;The natural step after getting a &lt;code&gt;PushSubscription&lt;/code&gt; and saving it our server is
to trigger a push message, but there is one thing I flagrantly glossed over. The
user experience when asking for permission from the user to send them push
messages.&lt;/p&gt;
&lt;p&gt;Sadly, very few sites give much consideration to how they ask their user for
permission, so let&#39;s take a brief aside to look at both good and bad UX.&lt;/p&gt;
&lt;h2 id=&quot;common-patterns&quot;&gt;Common patterns &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-permissions-ux/#common-patterns&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There have been a few common patterns emerging that should guide and help you when
deciding what is best for your users and use case.&lt;/p&gt;
&lt;h3 id=&quot;value-proposition&quot;&gt;Value proposition &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-permissions-ux/#value-proposition&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Ask users to subscribe to push at a time when the benefit is obvious.&lt;/p&gt;
&lt;p&gt;For example, a user has just bought an item on an online store and finished the
checkout flow. The site can then offer updates on the delivery status.&lt;/p&gt;
&lt;p&gt;There are a range of situations where this approach works:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A particular item is out of stock, would you like to be notified when it&#39;s next available?&lt;/li&gt;
&lt;li&gt;This breaking news story will be regularly updated, would you like to be notified as the
story develops?&lt;/li&gt;
&lt;li&gt;You&#39;re the highest bidder, would you like to be notified if you are outbid?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are all points where the user has invested in your service and there
is a clear value proposition for them to enable push notifications.&lt;/p&gt;
&lt;img alt=&quot;Owen Campbell-Moore&amp;#x27;s example of good UX for push.&quot; decoding=&quot;async&quot; height=&quot;1352&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LC9ub0f2q77XwKpBM8Hu.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LC9ub0f2q77XwKpBM8Hu.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LC9ub0f2q77XwKpBM8Hu.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LC9ub0f2q77XwKpBM8Hu.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LC9ub0f2q77XwKpBM8Hu.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LC9ub0f2q77XwKpBM8Hu.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LC9ub0f2q77XwKpBM8Hu.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LC9ub0f2q77XwKpBM8Hu.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LC9ub0f2q77XwKpBM8Hu.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LC9ub0f2q77XwKpBM8Hu.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LC9ub0f2q77XwKpBM8Hu.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LC9ub0f2q77XwKpBM8Hu.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LC9ub0f2q77XwKpBM8Hu.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LC9ub0f2q77XwKpBM8Hu.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LC9ub0f2q77XwKpBM8Hu.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LC9ub0f2q77XwKpBM8Hu.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LC9ub0f2q77XwKpBM8Hu.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/LC9ub0f2q77XwKpBM8Hu.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;created a mock of a hypothetical airline
website to demonstrate this approach.&lt;/p&gt;
&lt;p&gt;After the user has booked a flight it asks if the user would like notifications of flight
delays.&lt;/p&gt;
&lt;p&gt;Note that this is a custom UI from the website.&lt;/p&gt;
&lt;img alt=&quot;Owen Campbell-Moore&amp;#x27;s example of good UX for the permission prompt.&quot; decoding=&quot;async&quot; height=&quot;1352&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Kt3PPi4trxkE6K85DtgR.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Kt3PPi4trxkE6K85DtgR.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Kt3PPi4trxkE6K85DtgR.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Kt3PPi4trxkE6K85DtgR.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Kt3PPi4trxkE6K85DtgR.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Kt3PPi4trxkE6K85DtgR.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Kt3PPi4trxkE6K85DtgR.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Kt3PPi4trxkE6K85DtgR.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Kt3PPi4trxkE6K85DtgR.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Kt3PPi4trxkE6K85DtgR.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Kt3PPi4trxkE6K85DtgR.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Kt3PPi4trxkE6K85DtgR.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Kt3PPi4trxkE6K85DtgR.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Kt3PPi4trxkE6K85DtgR.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Kt3PPi4trxkE6K85DtgR.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Kt3PPi4trxkE6K85DtgR.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Kt3PPi4trxkE6K85DtgR.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/Kt3PPi4trxkE6K85DtgR.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Another nice touch to Owen&#39;s demo is that if the user clicks to enable
notifications, the site adds a semi-transparent overlay on the entire page when
it shows the permission prompt. This draws the users attention to the
permission prompt.&lt;/p&gt;
&lt;p&gt;The alternative to this example, the &lt;strong&gt;bad UX&lt;/strong&gt; for asking permission, is to request
permission as soon as a user lands on the airline&#39;s site.&lt;/p&gt;
&lt;img alt=&quot;Owen Campbell-Moore&amp;#x27;s example of bad UX for push.&quot; decoding=&quot;async&quot; height=&quot;1371&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/mgcQQkVIletBpXFUrmGX.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/mgcQQkVIletBpXFUrmGX.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/mgcQQkVIletBpXFUrmGX.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/mgcQQkVIletBpXFUrmGX.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/mgcQQkVIletBpXFUrmGX.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/mgcQQkVIletBpXFUrmGX.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/mgcQQkVIletBpXFUrmGX.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/mgcQQkVIletBpXFUrmGX.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/mgcQQkVIletBpXFUrmGX.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/mgcQQkVIletBpXFUrmGX.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/mgcQQkVIletBpXFUrmGX.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/mgcQQkVIletBpXFUrmGX.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/mgcQQkVIletBpXFUrmGX.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/mgcQQkVIletBpXFUrmGX.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/mgcQQkVIletBpXFUrmGX.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/mgcQQkVIletBpXFUrmGX.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/mgcQQkVIletBpXFUrmGX.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/mgcQQkVIletBpXFUrmGX.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;This approach provides no context as to why notifications are needed or
useful to the user. The user is also blocked from achieving their original
task (i.e. book a flight) by this permission prompt.&lt;/p&gt;
&lt;h3 id=&quot;double-permission&quot;&gt;Double Permission &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-permissions-ux/#double-permission&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You may feel that your site has a clear use case for push messaging and as
a result want to ask the user for permission as soon as possible.&lt;/p&gt;
&lt;p&gt;For example instant messaging and email clients. Showing a message for a
new message or email is an established user experience across a range of
platforms.&lt;/p&gt;
&lt;p&gt;For these category of apps, it&#39;s worth considering the double permission
pattern.&lt;/p&gt;
&lt;p&gt;First show a fake permission prompt that your website controls, consisting
of buttons to allow or ignore the permission request. If the user clicks
allow, request permission, triggering the real browser permission prompt.&lt;/p&gt;
&lt;p&gt;With this approach you display a custom permission prompt in your web app
which asks the user to enable notifications. By doing this the user can
chose enable or disable without your website running the risk of being
permanently blocked. If the user selects enable on the custom UI, display
the actual permission prompt, otherwise hide your custom pop-up and ask
some other time.&lt;/p&gt;
&lt;p&gt;A good example of this is . They show a prompt at
the top of their page once you&#39;ve signed in asking if you&#39;d like to enable notifications.&lt;/p&gt;
&lt;h3 id=&quot;settings-panel&quot;&gt;Settings Panel &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-permissions-ux/#settings-panel&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You can move notifications into a settings panel, giving users an easy way
to enable and disable push messaging, without the need of cluttering your
web app&#39;s UI.&lt;/p&gt;
&lt;img alt=&quot;When you first load the page, there is no prompt.&quot; decoding=&quot;async&quot; height=&quot;1388&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/l4PARykO7nv9Z5ogOR6D.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/l4PARykO7nv9Z5ogOR6D.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/l4PARykO7nv9Z5ogOR6D.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/l4PARykO7nv9Z5ogOR6D.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/l4PARykO7nv9Z5ogOR6D.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/l4PARykO7nv9Z5ogOR6D.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/l4PARykO7nv9Z5ogOR6D.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/l4PARykO7nv9Z5ogOR6D.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/l4PARykO7nv9Z5ogOR6D.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/l4PARykO7nv9Z5ogOR6D.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/l4PARykO7nv9Z5ogOR6D.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/l4PARykO7nv9Z5ogOR6D.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/l4PARykO7nv9Z5ogOR6D.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/l4PARykO7nv9Z5ogOR6D.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/l4PARykO7nv9Z5ogOR6D.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/l4PARykO7nv9Z5ogOR6D.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/l4PARykO7nv9Z5ogOR6D.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/l4PARykO7nv9Z5ogOR6D.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;A good example of this is .
When you first load up the Google I/O site, you aren&#39;t asked to do anything,
the user is left to explore the site.&lt;/p&gt;
&lt;img alt=&quot;The settings panel on Google IO&amp;#x27;s web app for push messaging.&quot; decoding=&quot;async&quot; height=&quot;1388&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eLcOvHOUA2L5hRpa2AHO.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eLcOvHOUA2L5hRpa2AHO.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eLcOvHOUA2L5hRpa2AHO.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eLcOvHOUA2L5hRpa2AHO.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eLcOvHOUA2L5hRpa2AHO.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eLcOvHOUA2L5hRpa2AHO.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eLcOvHOUA2L5hRpa2AHO.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eLcOvHOUA2L5hRpa2AHO.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eLcOvHOUA2L5hRpa2AHO.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eLcOvHOUA2L5hRpa2AHO.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eLcOvHOUA2L5hRpa2AHO.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eLcOvHOUA2L5hRpa2AHO.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eLcOvHOUA2L5hRpa2AHO.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eLcOvHOUA2L5hRpa2AHO.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eLcOvHOUA2L5hRpa2AHO.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eLcOvHOUA2L5hRpa2AHO.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eLcOvHOUA2L5hRpa2AHO.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/eLcOvHOUA2L5hRpa2AHO.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;After a few visits, clicking the menu item on the right reveals a settings
panel allowing the user to set up and manage notifications.&lt;/p&gt;
&lt;img alt=&quot;Google IO&amp;#x27;s web app displaying the permission prompt.&quot; decoding=&quot;async&quot; height=&quot;1388&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/oYEbRCEdXeUOrHOY2fCa.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/oYEbRCEdXeUOrHOY2fCa.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/oYEbRCEdXeUOrHOY2fCa.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/oYEbRCEdXeUOrHOY2fCa.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/oYEbRCEdXeUOrHOY2fCa.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/oYEbRCEdXeUOrHOY2fCa.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/oYEbRCEdXeUOrHOY2fCa.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/oYEbRCEdXeUOrHOY2fCa.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/oYEbRCEdXeUOrHOY2fCa.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/oYEbRCEdXeUOrHOY2fCa.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/oYEbRCEdXeUOrHOY2fCa.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/oYEbRCEdXeUOrHOY2fCa.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/oYEbRCEdXeUOrHOY2fCa.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/oYEbRCEdXeUOrHOY2fCa.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/oYEbRCEdXeUOrHOY2fCa.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/oYEbRCEdXeUOrHOY2fCa.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/oYEbRCEdXeUOrHOY2fCa.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/oYEbRCEdXeUOrHOY2fCa.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Clicking on the checkbox displays the permission prompt. No hidden surprises.&lt;/p&gt;
&lt;p&gt;After the permission has been granted, the checkbox is checked and the user
is good to go. The great thing about this UI is that users can enable and
disable notifications from one location on the website.&lt;/p&gt;
&lt;h3 id=&quot;passive-approach&quot;&gt;Passive approach &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-permissions-ux/#passive-approach&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One of the easiest ways to offer push to a user is to have a button
or toggle switch that enables / disables push messages in a location
on the page that is consistent throughout a site.&lt;/p&gt;
&lt;p&gt;This doesn&#39;t drive users to enable push notifications, but offers a
reliable and easy way for users to opt in and out of engaging with your
website. For sites like blogs that might have some regular viewers as well
as high bounce rates, this is a solid option as it targets regular viewers
without annoying drive-by visitors.&lt;/p&gt;
&lt;p&gt;On my personal site, I have a toggle switch for push messaging in the footer.&lt;/p&gt;
&lt;img alt=&quot;Example of Gauntface.com push notification toggle in footer&quot; decoding=&quot;async&quot; height=&quot;483&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ErFqMyTCspoGislhKxLX.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ErFqMyTCspoGislhKxLX.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ErFqMyTCspoGislhKxLX.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ErFqMyTCspoGislhKxLX.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ErFqMyTCspoGislhKxLX.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ErFqMyTCspoGislhKxLX.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ErFqMyTCspoGislhKxLX.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ErFqMyTCspoGislhKxLX.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ErFqMyTCspoGislhKxLX.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ErFqMyTCspoGislhKxLX.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ErFqMyTCspoGislhKxLX.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ErFqMyTCspoGislhKxLX.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ErFqMyTCspoGislhKxLX.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ErFqMyTCspoGislhKxLX.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ErFqMyTCspoGislhKxLX.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ErFqMyTCspoGislhKxLX.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ErFqMyTCspoGislhKxLX.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/ErFqMyTCspoGislhKxLX.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;It&#39;s fairly out of the way, but for regular visitors it should get enough
attention from readers wanting to get updates. One-time visitors are
completely unaffected.&lt;/p&gt;
&lt;p&gt;If the user subscribes to push messaging, the state of the toggle switch
changes and maintains state throughout the site.&lt;/p&gt;
&lt;img alt=&quot;Example of Gauntface.com with notifications enabled&quot; decoding=&quot;async&quot; height=&quot;483&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/lLVT0jG2MY1DvLJZzkeu.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/lLVT0jG2MY1DvLJZzkeu.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/lLVT0jG2MY1DvLJZzkeu.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/lLVT0jG2MY1DvLJZzkeu.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/lLVT0jG2MY1DvLJZzkeu.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/lLVT0jG2MY1DvLJZzkeu.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/lLVT0jG2MY1DvLJZzkeu.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/lLVT0jG2MY1DvLJZzkeu.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/lLVT0jG2MY1DvLJZzkeu.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/lLVT0jG2MY1DvLJZzkeu.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/lLVT0jG2MY1DvLJZzkeu.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/lLVT0jG2MY1DvLJZzkeu.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/lLVT0jG2MY1DvLJZzkeu.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/lLVT0jG2MY1DvLJZzkeu.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/lLVT0jG2MY1DvLJZzkeu.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/lLVT0jG2MY1DvLJZzkeu.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/lLVT0jG2MY1DvLJZzkeu.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/lLVT0jG2MY1DvLJZzkeu.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;h3 id=&quot;the-bad-ux&quot;&gt;The bad UX &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-permissions-ux/#the-bad-ux&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Those are some of the common practices I&#39;ve noticed on the web. Sadly, there is one very common
bad practice.&lt;/p&gt;
&lt;p&gt;The worst thing you can do is to show the permission dialog to users as soon as they
land on your site.&lt;/p&gt;
&lt;p&gt;They have zero context on why they are being asked for a permission, they may
not even know what your website is for, what it does or what it offers. Blocking
permissions at this point out of frustration is not uncommon, this pop-up is
getting in the way of what they are trying to do.&lt;/p&gt;
&lt;p&gt;Remember, if the user &lt;em&gt;blocks&lt;/em&gt; the permission request, your web app can&#39;t ask for permission
again. To get permission after being blocked, the user has to change the permission in the
browser&#39;s UI and doing so is not easy, obvious or fun for the user.&lt;/p&gt;
&lt;p&gt;No matter what, don&#39;t ask for permission as soon as the user opens your site, consider some
other UI or approach that has an incentive for the user to grant permission.&lt;/p&gt;
&lt;h3 id=&quot;offer-a-way-out&quot;&gt;Offer a way out &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-permissions-ux/#offer-a-way-out&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In addition to considering the UX to subscribe a user to push, &lt;strong&gt;please&lt;/strong&gt; consider how a user
should unsubscribe or opt out of push messaging.&lt;/p&gt;
&lt;p&gt;The number of sites that ask for permission as soon as the page loads and then
offer no UI for disabling push notifications is astounding.&lt;/p&gt;
&lt;p&gt;Your site should explain to your users how they can disable push. If you don&#39;t, users are
likely to take the nuclear option and block permission permanently.&lt;/p&gt;
&lt;h2 id=&quot;where-to-go-next&quot;&gt;Where to go next &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-permissions-ux/#where-to-go-next&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-overview/&quot;&gt;Web Push Notification Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-how-push-works/&quot;&gt;How Push Works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-subscribing-a-user/&quot;&gt;Subscribing a User&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Permission UX&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sending-messages-with-web-push-libraries/&quot;&gt;Sending Messages with Web Push Libraries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-web-push-protocol/&quot;&gt;Web Push Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-handling-messages/&quot;&gt;Handling Push Events&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-display-a-notification/&quot;&gt;Displaying a Notification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-notification-behaviour/&quot;&gt;Notification Behavior&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-common-notification-patterns/&quot;&gt;Common Notification Patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-faq/&quot;&gt;Push Notifications FAQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/&quot;&gt;Common Issues and Reporting Bugs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;code-labs&quot;&gt;Code labs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-permissions-ux/#code-labs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-client-codelab/&quot;&gt;Build a push notification client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-server-codelab/&quot;&gt;Build a push notification server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Matt Gaunt</name>
    </author>
  </entry>
  
  <entry>
    <title>Sending messages with web push libraries</title>
    <link href="https://web.dev/sending-messages-with-web-push-libraries/"/>
    <updated>2016-06-30T00:00:00Z</updated>
    <id>https://web.dev/sending-messages-with-web-push-libraries/</id>
    <content type="html" mode="escaped">&lt;p&gt;One of the pain points when working with web push is that triggering a push message is extremely
&amp;quot;fiddly&amp;quot;. To trigger a push message an application needs to make a POST request to a push
service following the &lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-webpush-protocol&quot; rel=&quot;noopener&quot;&gt;web push
protocol&lt;/a&gt;. To use push across all
browsers you need to use &lt;a href=&quot;https://tools.ietf.org/html/draft-thomson-webpush-vapid&quot; rel=&quot;noopener&quot;&gt;VAPID&lt;/a&gt;
(a.k.a. application server keys) which basically requires setting a header with a value proving
your application can message a user. To send data with a push message, the data needs to be
&lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-webpush-encryption&quot; rel=&quot;noopener&quot;&gt;encrypted&lt;/a&gt; and specific headers
need to be added so the browser can decrypt the message correctly.&lt;/p&gt;
&lt;p&gt;The main issue with triggering push is that if you hit a problem, it&#39;s difficult to diagnose
the issue. This is improving with time and wider browser support but it&#39;s far from easy. For
this reason, I strongly recommend using a library to handle the encryption, formatting and
triggering of your push message.&lt;/p&gt;
&lt;p&gt;If you really want to learn about what the libraries are doing, we&#39;ll cover it
in the next section. For now, we are going to look at managing subscriptions and using an
existing web push library to make the push requests.&lt;/p&gt;
&lt;p&gt;In this section we&#39;ll be using the &lt;a href=&quot;https://github.com/web-push-libs/web-push&quot; rel=&quot;noopener&quot;&gt;web-push Node
library&lt;/a&gt;. Other languages will have differences, but
they won&#39;t be too dissimilar. We are looking at Node since it&#39;s JavaScript and should be the
most accessible for readers.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Note: If you want a library for a different language, checkout the &lt;a href=&quot;https://github.com/web-push-libs/&quot;&gt;web-push-libs organization on Github&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;We&#39;ll go through the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Send a subscription to our backend and save it.&lt;/li&gt;
&lt;li&gt;Retrieve saved subscriptions and trigger a push message.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;saving-subscriptions&quot;&gt;Saving subscriptions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sending-messages-with-web-push-libraries/#saving-subscriptions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Saving and querying &lt;code&gt;PushSubscription&lt;/code&gt;&#39;s from a database will vary depending on
your server side language and database choice, but it might be useful to see
an example of how it could be done.&lt;/p&gt;
&lt;p&gt;In the demo web page, the &lt;code&gt;PushSubscription&lt;/code&gt; is sent to our backend by making a simple POST request:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sendSubscriptionToBackEnd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;subscription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/api/save-subscription/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;POST&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token string-property property&quot;&gt;&#39;Content-Type&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subscription&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;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;response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ok&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Bad status code from server.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;      &lt;span class=&quot;token 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;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;responseData&lt;/span&gt;&lt;span class=&quot;token punctuation&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;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;responseData&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; responseData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;success&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Bad response from server.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The &lt;a href=&quot;http://expressjs.com/&quot; rel=&quot;noopener&quot;&gt;Express&lt;/a&gt; server in our demo has a matching request listener for the
&lt;code&gt;/api/save-subscription/&lt;/code&gt; endpoint:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/api/save-subscription/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In this route we validate the subscription just to make sure the request is OK and not full of
garbage:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;isValidSaveRequest&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Check the request body has at least an endpoint.&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;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;endpoint&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Not a valid subscription.&lt;/span&gt;&lt;br /&gt;    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Content-Type&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#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;    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;error&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;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;no-endpoint&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token literal-property property&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Subscription must have an endpoint.&#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;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 keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; In this route we only check for an endpoint. If you &lt;strong&gt;require&lt;/strong&gt; payload support, make sure you check for the auth and p256dh keys as well. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;If the subscription is valid, we need to save it and return an appropriate
JSON response:&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;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;saveSubscriptionToDatabase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;subscriptionId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Content-Type&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#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;    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Content-Type&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#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;    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;error&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;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;unable-to-save-subscription&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token literal-property property&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token string&quot;&gt;&#39;The subscription was received but we were unable to save it to our database.&#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;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;This demo uses &lt;a href=&quot;https://github.com/louischatriot/nedb&quot; rel=&quot;noopener&quot;&gt;nedb&lt;/a&gt; to store the subscriptions, it&#39;s a
simple file based database, but you could use any database of your choice. We are only using this as
it requires zero set-up. For production you&#39;d want to use something more reliable. (I tend to
stick with good old MySQL.)&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;saveSubscriptionToDatabase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;subscription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    db&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subscription&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; newDoc&lt;/span&gt;&lt;span class=&quot;token punctuation&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;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token function&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token 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 punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&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;newDoc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;sending-push-messages&quot;&gt;Sending push messages &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sending-messages-with-web-push-libraries/#sending-push-messages&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When it comes to sending a push message, we ultimately need some event to trigger the process of
sending a message to users. A common approach is creating an admin page that let&#39;s you
configure and trigger the push message. But you could create a program to run locally or any
other approach that allows accessing the list of &lt;code&gt;PushSubscription&lt;/code&gt;&#39;s and running the code to
trigger the push message.&lt;/p&gt;
&lt;p&gt;Our demo has an &amp;quot;admin like&amp;quot; page that lets you trigger a push. Since it&#39;s just a demo it&#39;s a
public page.&lt;/p&gt;
&lt;p&gt;I&#39;m going to go through each step involved in getting the demo working. These will be baby
steps so everyone can follow along, including anyone who is new to Node.&lt;/p&gt;
&lt;p&gt;When we discussed subscribing a user, we covered adding an &lt;code&gt;applicationServerKey&lt;/code&gt; to the
&lt;code&gt;subscribe()&lt;/code&gt; options. It&#39;s on the back end that we&#39;ll need this private key.&lt;/p&gt;
&lt;p&gt;In the demo, these values are added to our Node app like so (boring code I know, but just want
you to know there is no magic):&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; vapidKeys &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;publicKey&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token string&quot;&gt;&#39;BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;privateKey&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;UUxI4O8-FbRouAevSmBQ6o18hgE4nSG3qwvJTfKc-ls&#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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Next we need to install the &lt;code&gt;web-push&lt;/code&gt; module for our Node server:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; web-push --save&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Then, in our Node script we require the &lt;code&gt;web-push&lt;/code&gt; module
like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; webpush &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;web-push&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Now we can start to use the &lt;code&gt;web-push&lt;/code&gt; module. First we need to tell the &lt;code&gt;web-push&lt;/code&gt; module about
our application server keys. (Remember they are also known as VAPID keys because that&#39;s the name
of the spec.)&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; vapidKeys &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;publicKey&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token string&quot;&gt;&#39;BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;privateKey&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;UUxI4O8-FbRouAevSmBQ6o18hgE4nSG3qwvJTfKc-ls&#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;br /&gt;webpush&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setVapidDetails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token string&quot;&gt;&#39;mailto:web-push-book@gauntface.com&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  vapidKeys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;publicKey&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  vapidKeys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;privateKey&lt;span 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;Note that we also included a &amp;quot;mailto:&amp;quot; string. This string needs to be either a URL or a mailto
email address. This piece of information will actually be sent to the web push service as part of
the request to trigger a push. The reason this is done is so that if a web push service needs
to get in touch with the sender, they have some information that will enable them to.&lt;/p&gt;
&lt;p&gt;With this, the &lt;code&gt;web-push&lt;/code&gt; module is ready to use, the next step is to trigger a push message.&lt;/p&gt;
&lt;p&gt;The demo uses the pretend admin panel to trigger push messages.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Screenshot of the admin page.&quot; decoding=&quot;async&quot; height=&quot;853&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDaU1o6yodiD0Ov0OtVR.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDaU1o6yodiD0Ov0OtVR.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDaU1o6yodiD0Ov0OtVR.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDaU1o6yodiD0Ov0OtVR.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDaU1o6yodiD0Ov0OtVR.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDaU1o6yodiD0Ov0OtVR.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDaU1o6yodiD0Ov0OtVR.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDaU1o6yodiD0Ov0OtVR.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDaU1o6yodiD0Ov0OtVR.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDaU1o6yodiD0Ov0OtVR.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDaU1o6yodiD0Ov0OtVR.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDaU1o6yodiD0Ov0OtVR.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDaU1o6yodiD0Ov0OtVR.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDaU1o6yodiD0Ov0OtVR.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDaU1o6yodiD0Ov0OtVR.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDaU1o6yodiD0Ov0OtVR.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDaU1o6yodiD0Ov0OtVR.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/KDaU1o6yodiD0Ov0OtVR.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Clicking the &amp;quot;Trigger Push Message&amp;quot; button will make a POST request to &lt;code&gt;/api/trigger-push-msg/&lt;/code&gt;,
which is the signal for our backend to send push messages, so we create the route in
express for this endpoint:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/api/trigger-push-msg/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;When this request is received, we grab the subscriptions from the database and
for each one, we trigger a push message.&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;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getSubscriptionsFromDatabase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;subscriptions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; promiseChain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Promise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; subscriptions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; subscription &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; subscriptions&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    promiseChain &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; promiseChain&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;triggerPushMsg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subscription&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dataToSend&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; promiseChain&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The function &lt;code&gt;triggerPushMsg()&lt;/code&gt; can then use the web-push library to send a message to the
provided subscription.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;triggerPushMsg&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;subscription&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dataToSend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; webpush&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subscription&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dataToSend&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;statusCode &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;404&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;statusCode &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;410&lt;/span&gt;&lt;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;Subscription has expired or is no longer valid: &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;deleteSubscriptionFromDatabase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subscription&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The call to &lt;code&gt;webpush.sendNotification()&lt;/code&gt; will return a promise. If the
message was sent successfully the promise will resolve and there is
nothing we need to do. If the promise rejects, you need to examine the
error as it&#39;ll inform you as to whether the &lt;code&gt;PushSubscription&lt;/code&gt; is still
valid or not.&lt;/p&gt;
&lt;p&gt;To determine the type of error from a push service it&#39;s best to look at the status code. Error
messages vary between push services and some are more helpful than others.&lt;/p&gt;
&lt;p&gt;In this example, it checks for status codes &lt;code&gt;404&lt;/code&gt; and &lt;code&gt;410&lt;/code&gt;, which are the HTTP status codes for
&#39;Not Found&#39; and &#39;Gone&#39;. If we receive one of these, it means the subscription has expired
or is no longer valid. In these scenarios, we need to remove the subscriptions from our database.&lt;/p&gt;
&lt;p&gt;In case of some other error, we just &lt;code&gt;throw err&lt;/code&gt;, which will make the promise returned by
&lt;code&gt;triggerPushMsg()&lt;/code&gt; reject.&lt;/p&gt;
&lt;p&gt;We&#39;ll cover some of the other status codes in the next section when we look at the web push
protocol in more detail.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; If you hit problems at this stage, it&#39;s worth looking at the error logs from Firefox before Chrome. The Mozilla push service has much more helpful error messages compared to Chrome / FCM. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;After looping through the subscriptions, we need to return a JSON response.&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 punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Content-Type&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#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;    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Content-Type&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#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;res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;error&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;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;unable-to-send-messages&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;We were unable to send messages to all subscriptions : &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token 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;We&#39;ve gone over the major implementation steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create an API to send subscriptions from our web page to our back-end
so it can save them to a database.&lt;/li&gt;
&lt;li&gt;Create an API to trigger the sending of push messages (in this case, an
API called from the pretended admin panel).&lt;/li&gt;
&lt;li&gt;Retrieve all the subscriptions from our backend
and send a message to each subscription with one of the &lt;a href=&quot;https://github.com/web-push-libs/&quot; rel=&quot;noopener&quot;&gt;web-push
libraries&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Regardless of your backend (Node, PHP, Python, …), the steps for implementing push are going
to be the same.&lt;/p&gt;
&lt;p&gt;Next up, what exactly are these web-push libraries doing for us?&lt;/p&gt;
&lt;h2 id=&quot;where-to-go-next&quot;&gt;Where to go next &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sending-messages-with-web-push-libraries/#where-to-go-next&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-overview/&quot;&gt;Web Push Notification Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-how-push-works/&quot;&gt;How Push Works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-subscribing-a-user/&quot;&gt;Subscribing a User&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-permissions-ux/&quot;&gt;Permission UX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Sending Messages with Web Push Libraries&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-web-push-protocol/&quot;&gt;Web Push Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-handling-messages/&quot;&gt;Handling Push Events&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-display-a-notification/&quot;&gt;Displaying a Notification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-notification-behaviour/&quot;&gt;Notification Behavior&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-common-notification-patterns/&quot;&gt;Common Notification Patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-faq/&quot;&gt;Push Notifications FAQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/&quot;&gt;Common Issues and Reporting Bugs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;code-labs&quot;&gt;Code labs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/sending-messages-with-web-push-libraries/#code-labs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-client-codelab/&quot;&gt;Build a push notification client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-server-codelab/&quot;&gt;Build a push notification server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Matt Gaunt</name>
    </author>
  </entry>
  
  <entry>
    <title>Subscribing a User</title>
    <link href="https://web.dev/push-notifications-subscribing-a-user/"/>
    <updated>2016-06-30T00:00:00Z</updated>
    <id>https://web.dev/push-notifications-subscribing-a-user/</id>
    <content type="html" mode="escaped">&lt;p&gt;The first step is to get permission from the user to send them push messages and then we can
get our hands on a &lt;code&gt;PushSubscription&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The JavaScript API to do this is reasonably straight forward, so let&#39;s step
through the logic flow.&lt;/p&gt;
&lt;h2 id=&quot;feature-detection&quot;&gt;Feature detection &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-subscribing-a-user/#feature-detection&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;First we need to check if the current browser actually supports push messaging. We can check if
push is supported with two simple checks.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Check for &lt;em&gt;serviceWorker&lt;/em&gt; on &lt;em&gt;navigator&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Check for &lt;em&gt;PushManager&lt;/em&gt; on &lt;em&gt;window&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;serviceWorker&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Service Worker isn&#39;t supported on this browser, disable or hide UI.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;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;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;PushManager&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Push isn&#39;t supported on this browser, disable or hide UI.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;While browser support is growing quickly for both service worker and
push messaging, it&#39;s always a good idea to feature detect for both and
&lt;a href=&quot;https://en.wikipedia.org/wiki/Progressive_enhancement&quot; rel=&quot;noopener&quot;&gt;progressively enhance&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;register-a-service-worker&quot;&gt;Register a service worker &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-subscribing-a-user/#register-a-service-worker&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With the feature detect we know that both service workers and Push are supported. The next step
is to &amp;quot;register&amp;quot; our service worker.&lt;/p&gt;
&lt;p&gt;When we register a service worker, we are telling the browser where our service worker file is.
The file is still just JavaScript, but the browser will &amp;quot;give it access&amp;quot; to the service worker
APIs, including push. To be more exact, the browser runs the file in a service worker
environment.&lt;/p&gt;
&lt;p&gt;To register a service worker, call &lt;code&gt;navigator.serviceWorker.register()&lt;/code&gt;, passing in the path to
our file. Like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;registerServiceWorker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;serviceWorker&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/service-worker.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;registration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Service worker successfully registered.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; registration&lt;span 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 function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Unable to register service worker.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&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 function tells the browser that we have a service worker file and where it&#39;s located. In
this case, the service worker file is at &lt;code&gt;/service-worker.js&lt;/code&gt;. Behind the scenes the browser
will take the following steps after calling &lt;code&gt;register()&lt;/code&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Download the service worker file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the JavaScript.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If everything runs correctly and there are no errors, the promise returned by &lt;code&gt;register()&lt;/code&gt;
will resolve. If there are errors of any kind, the promise will reject.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;If &lt;code&gt;register()&lt;/code&gt; does reject, double check your JavaScript for typos / errors in Chrome DevTools.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When &lt;code&gt;register()&lt;/code&gt; does resolve, it returns a &lt;code&gt;ServiceWorkerRegistration&lt;/code&gt;. We&#39;ll use this
registration to access the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/PushManager&quot; rel=&quot;noopener&quot;&gt;PushManager API&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;pushmanager-api-browser-compatibility&quot;&gt;PushManager API browser compatibility &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-subscribing-a-user/#pushmanager-api-browser-compatibility&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;wdi-browser-compat&quot;&gt;
  &lt;span class=&quot;wdi-browser-compat__label&quot;&gt;Browser support&lt;/span&gt;
  &lt;ul class=&quot;wdi-browser-compat__items&quot;&gt;
    &lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;chrome&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Chrome 42, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      42
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox 44, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      44
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Edge 17, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      17
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Safari 16, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      16
    &lt;/span&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/PushManager#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;h2 id=&quot;requesting-permission&quot;&gt;Requesting permission &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-subscribing-a-user/#requesting-permission&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We&#39;ve registered our service worker and are ready to subscribe the user, the next step is to get
permission from the user to send them push messages.&lt;/p&gt;
&lt;p&gt;The API for getting permission is relatively simple, the downside is that
the API &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Notification/requestPermission&quot; rel=&quot;noopener&quot;&gt;recently changed from taking a callback to returning a Promise&lt;/a&gt;. The
problem with this, is that we can&#39;t tell what version of the API is implemented by the current
browser, so you have to implement both and handle both.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;askPermission&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reject&lt;/span&gt;&lt;span class=&quot;token punctuation&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; permissionResult &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Notification&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;requestPermission&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;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;permissionResult&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      permissionResult&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reject&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;permissionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&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;permissionResult &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;granted&#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;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;We weren&#39;t granted permission.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In the above code, the important snippet of code is the call to
&lt;code&gt;Notification.requestPermission()&lt;/code&gt;. This method will display a prompt to the user:&lt;/p&gt;
&lt;img alt=&quot;Permission prompt on desktop and mobile Chrome.&quot; decoding=&quot;async&quot; height=&quot;251&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VpF84naCF2Ri7mF3MlhW.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VpF84naCF2Ri7mF3MlhW.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VpF84naCF2Ri7mF3MlhW.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VpF84naCF2Ri7mF3MlhW.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VpF84naCF2Ri7mF3MlhW.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VpF84naCF2Ri7mF3MlhW.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VpF84naCF2Ri7mF3MlhW.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VpF84naCF2Ri7mF3MlhW.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VpF84naCF2Ri7mF3MlhW.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VpF84naCF2Ri7mF3MlhW.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VpF84naCF2Ri7mF3MlhW.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VpF84naCF2Ri7mF3MlhW.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VpF84naCF2Ri7mF3MlhW.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VpF84naCF2Ri7mF3MlhW.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VpF84naCF2Ri7mF3MlhW.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VpF84naCF2Ri7mF3MlhW.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VpF84naCF2Ri7mF3MlhW.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/VpF84naCF2Ri7mF3MlhW.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Once the user interacted with the permission prompt by pressing Allow, Block, or just closing it,
we&#39;ll be given the result as a string: &lt;code&gt;&#39;granted&#39;&lt;/code&gt;, &lt;code&gt;&#39;default&#39;&lt;/code&gt; or &lt;code&gt;&#39;denied&#39;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In the sample code above, the promise returned by &lt;code&gt;askPermission()&lt;/code&gt; resolves if the permission
is granted, otherwise we throw an error making the promise reject.&lt;/p&gt;
&lt;p&gt;One edge case that you need to handle is if the user clicks the &#39;Block&#39; button. If this
happens, your web app will not be able to ask the user for permission again. They&#39;ll have to
manually &amp;quot;unblock&amp;quot; your app by changing its permission state, which is buried
in a settings panel. Think carefully about how and when you ask the user for permission,
because if they click block, it&#39;s not an easy way to reverse that decision.&lt;/p&gt;
&lt;p&gt;The good news is that most users are happy to give permission as long as
they &lt;em&gt;know&lt;/em&gt; why the permission is being asked.&lt;/p&gt;
&lt;p&gt;We&#39;ll look at how some popular sites ask for permission later on.&lt;/p&gt;
&lt;h2 id=&quot;subscribe-a-user-with-pushmanager&quot;&gt;Subscribe a user with PushManager &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-subscribing-a-user/#subscribe-a-user-with-pushmanager&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once we have our service worker registered and we&#39;ve got permission, we can subscribe a user by
calling &lt;code&gt;registration.pushManager.subscribe()&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;subscribeUserToPush&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;serviceWorker&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/service-worker.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;registration&lt;/span&gt;&lt;span class=&quot;token punctuation&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; subscribeOptions &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;userVisibleOnly&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;applicationServerKey&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;urlBase64ToUint8Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token string&quot;&gt;&#39;BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pushManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subscribeOptions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;pushSubscription&lt;/span&gt;&lt;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;br /&gt;        &lt;span class=&quot;token string&quot;&gt;&#39;Received PushSubscription: &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pushSubscription&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; pushSubscription&lt;span 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;When calling the &lt;code&gt;subscribe()&lt;/code&gt; method, we pass in an &lt;em&gt;options&lt;/em&gt; object, which consists of both
required and optional parameters.&lt;/p&gt;
&lt;p&gt;Lets look at all the options we can pass in.&lt;/p&gt;
&lt;h3 id=&quot;uservisibleonly-options&quot;&gt;userVisibleOnly options &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-subscribing-a-user/#uservisibleonly-options&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When push was first added to browsers, there was uncertainty about whether developers should be
able to send a push message and not show a notification. This is commonly referred to as silent
push, due to the user not knowing that something had happened in the background.&lt;/p&gt;
&lt;p&gt;The concern was that developers could do nasty things like track a user&#39;s location on an
ongoing basis without the user knowing.&lt;/p&gt;
&lt;p&gt;To avoid this scenario and to give spec authors time to consider how best to support this
feature, the &lt;code&gt;userVisibleOnly&lt;/code&gt; option was added and passing in a value of &lt;code&gt;true&lt;/code&gt; is a symbolic
agreement with the browser that the web app will show a notification every time a push is
received (i.e. no silent push).&lt;/p&gt;
&lt;p&gt;At the moment you &lt;strong&gt;must&lt;/strong&gt; pass in a value of &lt;code&gt;true&lt;/code&gt;. If you don&#39;t include the
&lt;code&gt;userVisibleOnly&lt;/code&gt; key or pass in &lt;code&gt;false&lt;/code&gt; you&#39;ll get the following error:&lt;/p&gt;
&lt;p&gt;Chrome currently only supports the Push API for subscriptions that will result in
user-visible messages. You can indicate this by calling
&lt;code&gt;pushManager.subscribe({userVisibleOnly: true})&lt;/code&gt; instead. See
&lt;a href=&quot;https://goo.gl/yqv4Q4&quot; rel=&quot;noopener&quot;&gt;https://goo.gl/yqv4Q4&lt;/a&gt; for more details.&lt;/p&gt;
&lt;p&gt;It&#39;s currently looking like blanket silent push will never be implemented in Chrome. Instead,
spec authors are exploring the notion of a budget API which will allow web apps a certain
number of silent push messages based on the usage of a web app.&lt;/p&gt;
&lt;h3 id=&quot;applicationserverkey-option&quot;&gt;applicationServerKey option &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-subscribing-a-user/#applicationserverkey-option&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We briefly mentioned &amp;quot;application server keys&amp;quot; in the previous section. &amp;quot;Application
server keys&amp;quot; are used by a push service to identify the application subscribing a user and
ensure that the same application is messaging that user.&lt;/p&gt;
&lt;p&gt;Application server keys are a public and private key pair that are unique to your application.
The private key should be kept a secret to your application and the public key can be shared
freely.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;applicationServerKey&lt;/code&gt; option passed into the &lt;code&gt;subscribe()&lt;/code&gt; call is the application&#39;s public
key. The browser passes this onto a push service when subscribing the user, meaning the push
service can tie your application&#39;s public key to the user&#39;s &lt;code&gt;PushSubscription&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The diagram below illustrates these steps.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Your web app is loaded in a browser and you call &lt;code&gt;subscribe()&lt;/code&gt;, passing in your public
application server key.&lt;/li&gt;
&lt;li&gt;The browser then makes a network request to a push service who will generate an endpoint,
associate this endpoint with the applications public key and return the endpoint to the
browser.&lt;/li&gt;
&lt;li&gt;The browser will add this endpoint to the &lt;code&gt;PushSubscription&lt;/code&gt;, which is returned via the
&lt;code&gt;subscribe()&lt;/code&gt; promise.&lt;/li&gt;
&lt;/ol&gt;
&lt;img alt=&quot;Illustration of the public application server key is used in subscribe method.&quot; decoding=&quot;async&quot; height=&quot;264&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/nHEwbmGFjtttom6DTFAw.svg&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;When you later want to send a push message, you&#39;ll need to create an &lt;strong&gt;Authorization&lt;/strong&gt; header
which will contain information signed with your application server&#39;s &lt;strong&gt;private key&lt;/strong&gt;. When the
push service receives a request to send a push message, it can validate this signed &lt;strong&gt;Authorization&lt;/strong&gt; header
by looking up the public key linked to the endpoint receiving the request. If the signature is
valid the push service knows that it must have come from the application server with the
&lt;strong&gt;matching private key&lt;/strong&gt;. It&#39;s basically a security measure that prevents anyone else sending
messages to an application&#39;s users.&lt;/p&gt;
&lt;img alt=&quot;How the private application server key is used when sending a message&quot; decoding=&quot;async&quot; height=&quot;378&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/A8NWXbSlEadr12WXrer6.svg&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;Technically, the &lt;code&gt;applicationServerKey&lt;/code&gt; is optional. However, the easiest
implementation on Chrome requires it, and other browsers may require it in
the future. It&#39;s optional on Firefox.&lt;/p&gt;
&lt;p&gt;The specification that defines &lt;em&gt;what&lt;/em&gt; the application server key should be is
the &lt;a href=&quot;https://tools.ietf.org/html/draft-thomson-webpush-vapid&quot; rel=&quot;noopener&quot;&gt;VAPID spec&lt;/a&gt;.
Whenever you read something referring to &lt;em&gt;&amp;quot;application server keys&amp;quot;&lt;/em&gt; or
&lt;em&gt;&amp;quot;VAPID keys&amp;quot;&lt;/em&gt;, just remember that they are the same thing.&lt;/p&gt;
&lt;h3 id=&quot;how-to-create-application-server-keys&quot;&gt;How to create application server keys &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-subscribing-a-user/#how-to-create-application-server-keys&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You can create a public and private set of application server keys by visiting
&lt;a href=&quot;https://web-push-codelab.glitch.me/&quot; rel=&quot;noopener&quot;&gt;web-push-codelab.glitch.me&lt;/a&gt; or you can use the
&lt;a href=&quot;https://github.com/web-push-libs/web-push#command-line&quot; rel=&quot;noopener&quot;&gt;web-push command line&lt;/a&gt;
to generate keys by doing the following:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;    $ &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; -g web-push&lt;br /&gt;    $ web-push generate-vapid-keys&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You only need to create these keys once for your application, just make sure you keep the
private key private. (Yeah, I just said that.)&lt;/p&gt;
&lt;h3 id=&quot;permissions-and-subscribe&quot;&gt;Permissions and subscribe() &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-subscribing-a-user/#permissions-and-subscribe&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There is one side effect of calling &lt;code&gt;subscribe()&lt;/code&gt;. If your web app doesn&#39;t have permissions for
showing notifications at the time of calling &lt;code&gt;subscribe()&lt;/code&gt;, the browser will request the
permissions for you. This is useful if your UI works with this flow, but if you want more
control (and I think most developers will), stick to the &lt;code&gt;Notification.requestPermission()&lt;/code&gt; API
that we used earlier.&lt;/p&gt;
&lt;h3 id=&quot;what-is-a-pushsubscription&quot;&gt;What is a PushSubscription? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-subscribing-a-user/#what-is-a-pushsubscription&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We call &lt;code&gt;subscribe()&lt;/code&gt;, pass in some options, and in return we get a promise that resolves to a
&lt;code&gt;PushSubscription&lt;/code&gt; resulting in some code like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;subscribeUserToPush&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;serviceWorker&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/service-worker.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;registration&lt;/span&gt;&lt;span class=&quot;token punctuation&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; subscribeOptions &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;userVisibleOnly&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;applicationServerKey&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;urlBase64ToUint8Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token string&quot;&gt;&#39;BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pushManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subscribeOptions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;pushSubscription&lt;/span&gt;&lt;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;br /&gt;        &lt;span class=&quot;token string&quot;&gt;&#39;Received PushSubscription: &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pushSubscription&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; pushSubscription&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The &lt;code&gt;PushSubscription&lt;/code&gt; object contains all the required information needed to send a push
messages to that user. If you print out the contents using &lt;code&gt;JSON.stringify()&lt;/code&gt;, you&#39;ll see the
following:&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 punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token string-property property&quot;&gt;&quot;endpoint&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://some.pushservice.com/something-unique&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token string-property property&quot;&gt;&quot;keys&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token string-property property&quot;&gt;&quot;p256dh&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token string&quot;&gt;&quot;BIPUL12DLfytvTajnryr2PRdAgXS3HGKiLqndGcJGabyhHheJYlNGCeXl1dn18gSJ1WAkAPIxr4gK0_dQds4yiI=&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token string-property property&quot;&gt;&quot;auth&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;FPssNDTKnInHVndSTdbKFw==&quot;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The &lt;code&gt;endpoint&lt;/code&gt; is the push services URL. To trigger a push message, make a POST request
to this URL.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;keys&lt;/code&gt; object contains the values used to encrypt message data sent with a push message
(which we&#39;ll discuss later on in this section).&lt;/p&gt;
&lt;h2 id=&quot;send-a-subscription-to-your-server&quot;&gt;Send a subscription to Your Server &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-subscribing-a-user/#send-a-subscription-to-your-server&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once you have a push subscription you&#39;ll want to send it to your server. It&#39;s up to you how you
do that but a tiny tip is to use &lt;code&gt;JSON.stringify()&lt;/code&gt; to get all the necessary data out of the
subscription object. Alternatively you can piece together the same
result manually like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; subscriptionObject &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;endpoint&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; pushSubscription&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;endpoint&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;keys&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;p256dh&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; pushSubscription&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;p256dh&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;auth&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; pushSubscription&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;auth&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// The above is the same output as:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; subscriptionObjectToo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pushSubscription&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;Sending the subscription is done in the web page like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sendSubscriptionToBackEnd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;subscription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/api/save-subscription/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;POST&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token string-property property&quot;&gt;&#39;Content-Type&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subscription&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;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;response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ok&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Bad status code from server.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;      &lt;span class=&quot;token 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;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;responseData&lt;/span&gt;&lt;span class=&quot;token punctuation&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;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;responseData&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; responseData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;success&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Bad response from server.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The node server receives this request and saves the data to a database for use later on.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/api/save-subscription/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token 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;&lt;span class=&quot;token function&quot;&gt;isValidSaveRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;saveSubscriptionToDatabase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;subscriptionId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Content-Type&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#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;      res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Content-Type&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#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;      res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token literal-property property&quot;&gt;error&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;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;unable-to-save-subscription&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token literal-property property&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;              &lt;span class=&quot;token string&quot;&gt;&#39;The subscription was received but we were unable to save it to our database.&#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;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;With the &lt;code&gt;PushSubscription&lt;/code&gt; details on our server we are good to send our user
a message whenever we want.&lt;/p&gt;
&lt;h3 id=&quot;faqs&quot;&gt;FAQs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-subscribing-a-user/#faqs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A few common questions people have asked at this point:&lt;/p&gt;
&lt;h4 id=&quot;can-i-change-the-push-service-a-browser-uses&quot;&gt;Can I change the push service a browser uses? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-subscribing-a-user/#can-i-change-the-push-service-a-browser-uses&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;No. The push service is selected by the browser and as we saw with the
&lt;code&gt;subscribe()&lt;/code&gt; call, the browser will make network requests to the push service
to retrieve the details that make up the &lt;em&gt;PushSubscription&lt;/em&gt;.&lt;/p&gt;
&lt;h4 id=&quot;each-browser-uses-a-different-push-service,-dont-they-have-different-apis&quot;&gt;Each browser uses a different Push Service, don&#39;t they have different API&#39;s? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-subscribing-a-user/#each-browser-uses-a-different-push-service,-dont-they-have-different-apis&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;All push services will expect the same API.&lt;/p&gt;
&lt;p&gt;This common API is called the
&lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-webpush-protocol&quot; rel=&quot;noopener&quot;&gt;Web Push Protocol&lt;/a&gt;
and describes the network request
your application will need to make to trigger a push message.&lt;/p&gt;
&lt;h4 id=&quot;if-i-subscribe-a-user-on-their-desktop,-are-they-subscribed-on-their-phone-as-well&quot;&gt;If I subscribe a user on their desktop, are they subscribed on their phone as well? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-subscribing-a-user/#if-i-subscribe-a-user-on-their-desktop,-are-they-subscribed-on-their-phone-as-well&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Unfortunately not. A user must register for push on each browser they wish to
receive messages on. It&#39;s also worth noting that this will require
the user granting permission on each device.&lt;/p&gt;
&lt;h2 id=&quot;where-to-go-next&quot;&gt;Where to go next &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-subscribing-a-user/#where-to-go-next&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-overview/&quot;&gt;Web Push Notification Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-how-push-works/&quot;&gt;How Push Works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Subscribing a User&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-permissions-ux/&quot;&gt;Permission UX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sending-messages-with-web-push-libraries/&quot;&gt;Sending Messages with Web Push Libraries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-web-push-protocol/&quot;&gt;Web Push Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-handling-messages/&quot;&gt;Handling Push Events&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-display-a-notification/&quot;&gt;Displaying a Notification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-notification-behaviour/&quot;&gt;Notification Behavior&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-common-notification-patterns/&quot;&gt;Common Notification Patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-faq/&quot;&gt;Push Notifications FAQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/&quot;&gt;Common Issues and Reporting Bugs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;code-labs&quot;&gt;Code labs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-subscribing-a-user/#code-labs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-client-codelab/&quot;&gt;Build a push notification client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-server-codelab/&quot;&gt;Build a push notification server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Matt Gaunt</name>
    </author>
  </entry>
  
  <entry>
    <title>How push works</title>
    <link href="https://web.dev/push-notifications-how-push-works/"/>
    <updated>2016-06-30T00:00:00Z</updated>
    <id>https://web.dev/push-notifications-how-push-works/</id>
    <content type="html" mode="escaped">&lt;p&gt;Before getting into the API, let&#39;s look at push from a high level, start to finish. Then as we
step through individual topics or APIs later on, you&#39;ll have an idea of how and why it&#39;s
important.&lt;/p&gt;
&lt;p&gt;The three key steps to implementing push are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Adding the client side logic to subscribe a user to push (i.e. the JavaScript and UI in your
web app that registers a user to push messages).&lt;/li&gt;
&lt;li&gt;The API call from your back-end / application that triggers a push message to a user&#39;s device.&lt;/li&gt;
&lt;li&gt;The service worker JavaScript file that will receive a &amp;quot;push event&amp;quot; when the push arrives on
the device. It&#39;s in this JavaScript that you&#39;ll be able to show a notification.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&#39;s look at what each of these steps entails in a little more detail.&lt;/p&gt;
&lt;h2 id=&quot;step-1-client-side&quot;&gt;Step 1: Client Side &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-how-push-works/#step-1-client-side&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The first step is to &amp;quot;subscribe&amp;quot; a user to push messaging.&lt;/p&gt;
&lt;p&gt;Subscribing a user requires two things. First, getting &lt;strong&gt;permission&lt;/strong&gt; from the user to send
them push messages. Second, getting a &lt;code&gt;PushSubscription&lt;/code&gt; from the browser.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;PushSubscription&lt;/code&gt; contains all the information we need to send a push message to that user.
You can &amp;quot;kind of&amp;quot; think of this as an ID for that user&#39;s device.&lt;/p&gt;
&lt;p&gt;This is all done in JavaScript with the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Push_API&quot; rel=&quot;noopener&quot;&gt;Push API&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;wdi-browser-compat&quot;&gt;
  &lt;span class=&quot;wdi-browser-compat__label&quot;&gt;Browser support&lt;/span&gt;
  &lt;ul class=&quot;wdi-browser-compat__items&quot;&gt;
    &lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;chrome&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Chrome 42, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      42
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox 44, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      44
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Edge 17, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      17
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Safari 16, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      16
    &lt;/span&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/PushEvent#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;Before subscribing a user you&#39;ll need to generate a set of
&amp;quot;application server keys&amp;quot;, which we&#39;ll cover later on.&lt;/p&gt;
&lt;p&gt;The application server keys, also known as VAPID keys, are unique to your server. They allow a
push service to know which application server subscribed a user and ensure that it&#39;s the same
server triggering the push messages to that user.&lt;/p&gt;
&lt;p&gt;Once you&#39;ve subscribed the user and have a &lt;code&gt;PushSubscription&lt;/code&gt;, you&#39;ll need to send the
&lt;code&gt;PushSubscription&lt;/code&gt; details to your backend / server. On your server, you&#39;ll save this
subscription to a database and use it to send a push message to that user.&lt;/p&gt;
&lt;img alt=&quot;Make sure you send the PushSubscription to your backend.&quot; decoding=&quot;async&quot; height=&quot;213&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/3eZW2SvRzowpHsYxhUwr.svg&quot; width=&quot;800&quot; /&gt;
&lt;h2 id=&quot;step-2-send-a-push-message&quot;&gt;Step 2: Send a push message &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-how-push-works/#step-2-send-a-push-message&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you want to send a push message to your users you need to make an API call to a push
service. This API call would include what data to send, who to send the message to and any
criteria about how to send the message. Normally this API call is done from your server.&lt;/p&gt;
&lt;p&gt;Some questions you might be asking yourself:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Who and what is the push service?&lt;/li&gt;
&lt;li&gt;What does the API look like? Is it JSON, XML, something else?&lt;/li&gt;
&lt;li&gt;What can the API do?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;who-and-what-is-the-push-service&quot;&gt;Who and what is the push service? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-how-push-works/#who-and-what-is-the-push-service&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A push service receives a network request, validates it and delivers a push message to the appropriate browser. If the browser is offline, the message is queued until the browser comes online.&lt;/p&gt;
&lt;p&gt;Each browser can use any push service they want, it&#39;s something developers have no control
over. This isn&#39;t a problem because every push service expects the &lt;strong&gt;same&lt;/strong&gt; API call. Meaning
you don&#39;t have to care who the push service is. You just need to make sure that your API call
is valid.&lt;/p&gt;
&lt;p&gt;To get the appropriate URL to trigger a push message (i.e. the URL for the push service) you
just need to look at the &lt;code&gt;endpoint&lt;/code&gt; value in a &lt;code&gt;PushSubscription&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Below is an example of the values you&#39;ll get from a &lt;strong&gt;PushSubscription&lt;/strong&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;endpoint&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://random-push-service.com/some-kind-of-unique-id-1234/v2/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;keys&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;p256dh&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;BNcRdreALRFXTkOOUHK1EtK2wtaz5Ry4YfYCA_0QTpQtUbVlUls0VJXg7A8u-Ts1XbjhazAkj7I99e8QcYP7DkM=&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;auth&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tBHItJI5svbpez7KI4CCXg==&quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The &lt;strong&gt;endpoint&lt;/strong&gt; in this case is
[https://random-push-service.com/some-kind-of-unique-id-1234/v2/]. The push service would be
&#39;random-push-service.com&#39; and each endpoint is unique to a user, indicated with
&#39;some-kind-of-unique-id-1234&#39;. As you start working with push you&#39;ll notice this pattern.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;keys&lt;/strong&gt; in the subscription will be covered later on.&lt;/p&gt;
&lt;h3 id=&quot;what-does-the-api-look-like&quot;&gt;What does the API look like? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-how-push-works/#what-does-the-api-look-like&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I mentioned that every web push service expects the same API call. That API is the
&lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-webpush-protocol&quot; rel=&quot;noopener&quot;&gt;&lt;strong&gt;Web Push Protocol&lt;/strong&gt;&lt;/a&gt;.
It&#39;s an IETF standard that defines how you make an API call to a push service.&lt;/p&gt;
&lt;p&gt;The API call requires certain headers to be set and the data to be a stream of bytes. We&#39;ll
look at libraries that can perform this API call for us as well as how to do it ourselves.&lt;/p&gt;
&lt;h3 id=&quot;what-can-the-api-do&quot;&gt;What can the API do? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-how-push-works/#what-can-the-api-do&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The API provides a way to send a message to a user, with / without data, and provides
instructions of &lt;em&gt;how&lt;/em&gt; to send the message.&lt;/p&gt;
&lt;p&gt;The data you send with a push message must be encrypted. The reason for this is that it
prevents push services, who could be anyone, from being able to view the data sent with the
push message. This is important given that it&#39;s the browser who decides which push service to
use, which could open the door to browsers using a push service that isn&#39;t safe or secure.&lt;/p&gt;
&lt;p&gt;When you trigger a push message, the push service will receive the API call and queue the
message. This message will remain queued until the user&#39;s device comes online and the push
service can deliver the messages. The instructions you can give to the push service define how
the push message is queued.&lt;/p&gt;
&lt;p&gt;The instructions include details like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The time-to-live for a push message. This defines how long a message should be queued before
it&#39;s removed and not delivered.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Define the urgency of the message. This is useful in case the push service is preserving the
users battery life by only delivering high priority messages.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Give a push message a &amp;quot;topic&amp;quot; name which will replace any pending message with this new message.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;img alt=&quot;When your server wishes to send a push message, it makes a web push protocol request to a push service.&quot; decoding=&quot;async&quot; height=&quot;220&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/f42EMdaS3bjYhIw47E1J.svg&quot; width=&quot;800&quot; /&gt;
&lt;h3 id=&quot;step-3-push-event-on-the-users-device&quot;&gt;Step 3: Push event on the user&#39;s device &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-how-push-works/#step-3-push-event-on-the-users-device&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Once we&#39;ve sent a push message, the push service will keep your message on its server until
one of following events occurs:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The device comes online and the push service delivers the message.&lt;/li&gt;
&lt;li&gt;The message expires. If this occurs the push service removes the message from its queue and
it&#39;ll never be delivered.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When the push service does deliver a message, the browser will receive the message, decrypt any
data and dispatch a &lt;code&gt;push&lt;/code&gt; event in your service worker.&lt;/p&gt;
&lt;p&gt;A &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Service_Worker_API&quot; rel=&quot;noopener&quot;&gt;service worker&lt;/a&gt; is a
&amp;quot;special&amp;quot; JavaScript file. The browser can execute this JavaScript without your page being
open. It can even execute this JavaScript when the browser is closed. A service worker also has
API&#39;s, like push, that aren&#39;t available in the web page (i.e. API&#39;s that aren&#39;t available out
of a service worker script).&lt;/p&gt;
&lt;p&gt;It&#39;s inside the service worker&#39;s &#39;push&#39; event that you can perform any background tasks. You
can make analytics calls, cache pages offline and show notifications.&lt;/p&gt;
&lt;img alt=&quot;When a push message is sent from a push service to a user&amp;#x27;s device, your service worker receives a push event&quot; decoding=&quot;async&quot; height=&quot;238&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/DCOmdNQ0YXPfd082WUgz.svg&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;That&#39;s the whole flow for push messaging.&lt;/p&gt;
&lt;h2 id=&quot;where-to-go-next&quot;&gt;Where to go next &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-how-push-works/#where-to-go-next&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-overview/&quot;&gt;Web Push Notification Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;How Push Works&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-subscribing-a-user/&quot;&gt;Subscribing a User&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-permissions-ux/&quot;&gt;Permission UX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sending-messages-with-web-push-libraries/&quot;&gt;Sending Messages with Web Push Libraries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-web-push-protocol/&quot;&gt;Web Push Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-handling-messages/&quot;&gt;Handling Push Events&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-display-a-notification/&quot;&gt;Displaying a Notification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-notification-behaviour/&quot;&gt;Notification Behavior&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-common-notification-patterns/&quot;&gt;Common Notification Patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-faq/&quot;&gt;Push Notifications FAQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/&quot;&gt;Common Issues and Reporting Bugs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;code-labs&quot;&gt;Code labs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-how-push-works/#code-labs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-client-codelab/&quot;&gt;Build a push notification client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-server-codelab/&quot;&gt;Build a push notification server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Matt Gaunt</name>
    </author>
  </entry>
  
  <entry>
    <title>Enabling HTTPS on your servers</title>
    <link href="https://web.dev/enable-https/"/>
    <updated>2015-03-27T00:00:00Z</updated>
    <id>https://web.dev/enable-https/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;steps-covered-in-this-article&quot;&gt;Steps covered in this article &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/enable-https/#steps-covered-in-this-article&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Create a 2048-bit RSA public/private key pair.&lt;/li&gt;
&lt;li&gt;Generate a certificate signing request (CSR) that embeds your public key.&lt;/li&gt;
&lt;li&gt;Share your CSR with your Certificate Authority (CA) to receive a final
certificate or a certificate chain.&lt;/li&gt;
&lt;li&gt;Install your final certificate in a non-web-accessible place such as
&lt;code&gt;/etc/ssl&lt;/code&gt; (Linux and Unix) or wherever IIS requires it (Windows).&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;generating-keys-and-certificate-signing-requests&quot;&gt;Generating keys and certificate signing requests &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/enable-https/#generating-keys-and-certificate-signing-requests&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This section uses the openssl command-line program, which comes with most
Linux, BSD, and Mac OS X systems, to generate private/public keys and a CSR.&lt;/p&gt;
&lt;h3 id=&quot;generate-a-publicprivate-key-pair&quot;&gt;Generate a public/private key pair &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/enable-https/#generate-a-publicprivate-key-pair&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Let&#39;s start by generating a 2,048-bit RSA key pair. A smaller key, such
as 1,024 bits, is insufficiently resistant to brute-force guessing attacks. A
larger key, such as 4,096 bits, is overkill. Over time, key sizes increase as
computer processing gets cheaper. 2,048 is currently the sweet spot.&lt;/p&gt;
&lt;p&gt;The command to generate the RSA key pair is:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;openssl genrsa -out www.example.com.key &lt;span class=&quot;token number&quot;&gt;2048&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This gives the following output:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;Generating RSA private key, &lt;span class=&quot;token number&quot;&gt;2048&lt;/span&gt; bit long modulus&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;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.+++&lt;br /&gt;e is &lt;span class=&quot;token number&quot;&gt;65537&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0x10001&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;generate-a-certificate-signing-request&quot;&gt;Generate a certificate signing request &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/enable-https/#generate-a-certificate-signing-request&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In this step, you embed your public key and information about your organization
and your website into a certificate signing request or CSR. The &lt;em&gt;openssl&lt;/em&gt;
command interactively asks you for the required metadata.&lt;/p&gt;
&lt;p&gt;Running the following command:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;openssl req -new -sha256 -key www.example.com.key -out www.example.com.csr&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Outputs the following:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;You are about to be asked to enter information that will be incorporated&lt;br /&gt;into your certificate request&lt;br /&gt;&lt;br /&gt;What you are about to enter is what is called a Distinguished Name or a DN.&lt;br /&gt;There are quite a few fields but you can leave some blank&lt;br /&gt;For some fields there will be a default value,&lt;br /&gt;If you enter &lt;span class=&quot;token string&quot;&gt;&#39;.&#39;&lt;/span&gt;, the field will be left blank.&lt;br /&gt;-----&lt;br /&gt;Country Name &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; letter code&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;AU&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;:CA&lt;br /&gt;State or Province Name &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;full name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Some-State&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;:California&lt;br /&gt;Locality Name &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;for example, city&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;:Mountain View&lt;br /&gt;Organization Name &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;for example, company&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Internet Widgits Pty Ltd&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;:Example, Inc.&lt;br /&gt;Organizational Unit Name &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;for example, section&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;:Webmaster Help Center Example&lt;br /&gt;Team&lt;br /&gt;Common Name &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e.g. server FQDN or YOUR name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;:www.example.com&lt;br /&gt;Email Address &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;:webmaster@example.com&lt;br /&gt;&lt;br /&gt;Please enter the following &lt;span class=&quot;token string&quot;&gt;&#39;extra&#39;&lt;/span&gt; attributes&lt;br /&gt;to be sent with your certificate request&lt;br /&gt;A challenge password &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;:&lt;br /&gt;An optional company name &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;:&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;To ensure the validity of the CSR, run this command:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;openssl req -text -in www.example.com.csr -noout&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;And the response should look like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;Certificate Request:&lt;br /&gt;    Data:&lt;br /&gt;        Version: &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0x0&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;        Subject: &lt;span class=&quot;token assign-left variable&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;CA, &lt;span class=&quot;token assign-left variable&quot;&gt;ST&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;California, &lt;span class=&quot;token assign-left variable&quot;&gt;L&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;Mountain View, &lt;span class=&quot;token assign-left variable&quot;&gt;O&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;Google, Inc.,&lt;br /&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;OU&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;Webmaster Help Center Example Team,&lt;br /&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;CN&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;www.example.com/emailAddress&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;webmaster@example.com&lt;br /&gt;        Subject Public Key Info:&lt;br /&gt;            Public Key Algorithm: rsaEncryption&lt;br /&gt;                Public-Key: &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2048&lt;/span&gt; bit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;                Modulus:&lt;br /&gt;                    00:ad:fc:58:e0:da:f2:0b:73:51:93:29:a5:d3:9e:&lt;br /&gt;                    f8:f1:14:13:64:cc:e0:bc:be:26:5d:04:e1:58:dc:&lt;br /&gt;                    &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;br /&gt;                Exponent: &lt;span class=&quot;token number&quot;&gt;65537&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0x10001&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;        Attributes:&lt;br /&gt;            a0:00&lt;br /&gt;    Signature Algorithm: sha256WithRSAEncryption&lt;br /&gt;         5f:05:f3:71:d5:f7:b7:b6:dc:17:cc:88:03:b8:87:29:f6:87:&lt;br /&gt;         2f:7f:00:49:08:0a:20:41:0b:70:03:04:7d:94:af:69:3d:f4:&lt;br /&gt;         &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;submit-your-csr-to-a-certificate-authority&quot;&gt;Submit your CSR to a certificate authority &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/enable-https/#submit-your-csr-to-a-certificate-authority&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Different certificate authorities (CAs) require different methods for sending
them your CSRs. Methods may include using a form on their website, sending the
CSR by email, or something else. Some CAs (or their resellers) may even automate
some or all of the process (including, in some cases, key pair and CSR
generation).&lt;/p&gt;
&lt;p&gt;Send the CSR to your CA, and follow their instructions to receive your final
certificate or certificate chain.&lt;/p&gt;
&lt;p&gt;Different CAs charge different amounts of money for the service of vouching
for your public key.&lt;/p&gt;
&lt;p&gt;There are also options for mapping your key to more than one DNS name, including
several distinct names (e.g. all of example.com, www.example.com, example.net,
and www.example.net) or &amp;quot;wildcard&amp;quot; names such as *.example.com.&lt;/p&gt;
&lt;p&gt;For example, one CA currently offers these prices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Standard: $16/year, valid for example.com and www.example.com.&lt;/li&gt;
&lt;li&gt;Wildcard: $150/year, valid for example.com and *.example.com.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At these prices, wildcard certificates are economical when you have more than 9
subdomains; otherwise, you can just buy one or more single-name certificates. (If
you have more than, say, five subdomains, you might find a wildcard certificate
more convenient when you come to enable HTTPS on your servers.)&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Keep in mind that in wildcard certificates the wildcard applies to only one DNS label. A certificate good for *.example.com will work for foo.example.com and bar.example.com, but &lt;em&gt;not&lt;/em&gt; for foo.bar.example.com. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Copy the certificates to all your front-end servers in a non-web-accessible
place such as &lt;code&gt;/etc/ssl&lt;/code&gt; (Linux and Unix) or wherever IIS (Windows) requires
them.&lt;/p&gt;
&lt;h2 id=&quot;enable-https-on-your-servers&quot;&gt;Enable HTTPS on your servers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/enable-https/#enable-https-on-your-servers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Enabling HTTPS on your servers is a critical step in providing security for
your web pages.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use Mozilla&#39;s Server Configuration tool to set up your server for HTTPS support.&lt;/li&gt;
&lt;li&gt;Regularly test your site with the Qualys&#39; handy SSL Server Test and ensure
you get at least an A or A+.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At this point, you must make a crucial operations decision. Choose one of the
following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dedicate a distinct IP address to each hostname your web server serves content
from.&lt;/li&gt;
&lt;li&gt;Use name-based virtual hosting.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have been using distinct IP addresses for each hostname, you can
easily support both HTTP and HTTPS for all clients.&lt;/p&gt;
&lt;p&gt;However, most site operators use name-based virtual hosting to conserve IP
addresses and because it&#39;s more convenient in general. The problem with IE on
Windows XP and Android earlier than 2.3 is that they do not understand &lt;a href=&quot;https://en.wikipedia.org/wiki/Server_Name_Indication&quot; rel=&quot;noopener&quot;&gt;Server
Name Indication&lt;/a&gt;
(SNI), which is crucial for HTTPS name-based virtual hosting.&lt;/p&gt;
&lt;p&gt;Someday—hopefully soon—clients that don&#39;t support SNI will be replaced
with modern software. Monitor the user agent string in your request logs to know
when enough of your user population has migrated to modern software. (You can
decide what your threshold is; perhaps less than 5%, or less then 1%.)&lt;/p&gt;
&lt;p&gt;If you don&#39;t already have HTTPS service available on your servers, enable it now
(without redirecting HTTP to HTTPS; see below). Configure your web server to use
the certificates you bought and installed. You might find &lt;a href=&quot;https://mozilla.github.io/server-side-tls/ssl-config-generator/&quot; rel=&quot;noopener&quot;&gt;Mozilla&#39;s handy
configuration
generator&lt;/a&gt;
useful.&lt;/p&gt;
&lt;p&gt;If you have many hostnames or subdomains, they each need to use the right
certificate.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-warn-bg color-state-warn-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block color-state-warn-text&quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; role=&quot;img&quot; aria-label=&quot;Warning sign&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M23 21L12 2 1 21h22zm-12-3v-2h2v2h-2zm0-4h2v-4h-2v4z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Warning&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Warning: If you&#39;ve already completed these steps, but are using HTTPS for the sole purpose of redirecting clients back to HTTP, stop doing that now. See the next section to make sure HTTPS and HTTP work smoothly. &lt;/div&gt;&lt;/aside&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Ultimately you should redirect HTTP requests to HTTPS and use HTTP Strict Transport Security (HSTS). However, this is not the right stage in the migration process to do that; see &amp;quot;Redirect HTTP To HTTPS&amp;quot; and &amp;quot;Turn On Strict Transport Security And Secure Cookies.&amp;quot; &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Now, and throughout your site&#39;s lifetime, check your HTTPS configuration with
&lt;a href=&quot;https://www.ssllabs.com/ssltest/&quot; rel=&quot;noopener&quot;&gt;Qualys&#39; handy SSL Server Test&lt;/a&gt;.
Your site should score an A or A+; treat anything that causes a lower grade as
a bug. (Today&#39;s A is tomorrow&#39;s B, because attacks against algorithms and
protocols are always improving!)&lt;/p&gt;
&lt;h2 id=&quot;make-intrasite-urls-relative&quot;&gt;Make intrasite URLs relative &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/enable-https/#make-intrasite-urls-relative&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now that you are serving your site on both HTTP and HTTPS, things need to work as
smoothly as possible, regardless of protocol. An important factor is using
relative URLs for intrasite links.&lt;/p&gt;
&lt;p&gt;Make sure intrasite URLs and external URLs are agnostic to protocol; that is,
make sure you use relative paths or leave out the protocol like
&lt;code&gt;//example.com/something.js&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A problem arises when you serve a page via HTTPS that includes HTTP
resources, known as
&lt;a href=&quot;https://web.dev/what-is-mixed-content/&quot;&gt;mixed content&lt;/a&gt;.
Browsers warn users that the full strength of HTTPS has been lost. In fact,
in the case of active mixed content (script, plug-ins, CSS, iframes), browsers
often simply won&#39;t load or execute the content at all, resulting in a
broken page. And remember, it&#39;s perfectly OK to include HTTPS resources in an
HTTP page.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; See &lt;a href=&quot;https://web.dev/fixing-mixed-content&quot;&gt;Fixing Mixed Content&lt;/a&gt; for more details about ways to fix and prevent mixed content. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Additionally, when you link to other pages in your site, users could get
downgraded from HTTPS to HTTP.&lt;/p&gt;
&lt;p&gt;These problems happen when your pages include fully-qualified, intrasite URLs
that use the &lt;em&gt;http://&lt;/em&gt; scheme.&lt;/p&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;worse&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Don&#39;t&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Welcome To Example.com&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;http://example.com/jquery.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;http://assets.example.com/style.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;http://img.example.com/logo.png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;A &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;http://example.com/2014/12/24/&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;new post on cats!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;figcaption class=&quot;compare__caption&quot;&gt;
&lt;p&gt;Avoid using fully qualified intrasite URLs.&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In other words, make intrasite URLs as relative as possible: either
protocol-relative (lacking a protocol, starting with &lt;code&gt;//example.com&lt;/code&gt;) or
host-relative (starting with just the path, like &lt;code&gt;/jquery.js&lt;/code&gt;).&lt;/p&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;better&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Do&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Welcome To Example.com&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/jquery.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/assets/style.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/images/logo.png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;A &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/2014/12/24/&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;new post on cats!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;figcaption class=&quot;compare__caption&quot;&gt;
&lt;p&gt;Use relative intrasite URLs.&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;better&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Do&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Welcome To Example.com&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;//example.com/jquery.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;//assets.example.com/style.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;//img.example.com/logo.png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;A &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;//example.com/2014/12/24/&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;new post on cats!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;figcaption class=&quot;compare__caption&quot;&gt;
&lt;p&gt;Or, use protocol-relative intrasite URLs.&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;better&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Do&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Welcome To Example.com&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/jquery.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/assets/style.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/images/logo.png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;A &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/2014/12/24/&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;new post on cats!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token entity named-entity&quot; title=&quot;&amp;lt;&quot;&gt;&amp;amp;lt;&lt;/span&gt;p&gt;Check out this &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&amp;lt;b&gt;https://foo.com/&amp;lt;/b&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;other cool site.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;figcaption class=&quot;compare__caption&quot;&gt;
&lt;p&gt;Use HTTPS URLs for intersite URLs (where possible).&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Do this with a script, not by hand. If your site&#39;s content is in a database,
test your script on a development copy of your database. If
your site&#39;s content consists of simple files, test your script on a
development copy of the files. Push the changes to production only after the
changes pass QA, as normal. You can use &lt;a href=&quot;https://github.com/bramus/mixed-content-scan&quot; rel=&quot;noopener&quot;&gt;Bram van Damme&#39;s
script&lt;/a&gt; or something similar to
detect mixed content in your site.&lt;/p&gt;
&lt;p&gt;When linking to other sites (as opposed to including resources from them),
don&#39;t change the protocol since you don&#39;t have control over how those sites
operate.&lt;/p&gt;
&lt;p&gt;To make migration smoother for large sites, we recommend
protocol-relative URLs. If you are not sure whether you can fully deploy
HTTPS yet, forcing your site to use HTTPS for all sub-resources may backfire.
There is likely to be a period of time in which HTTPS is new and weird for
you, and the HTTP site must still work as well as ever. Over time, you&#39;ll
complete the migration and lock in HTTPS (see the next two sections).&lt;/p&gt;
&lt;p&gt;If your site depends on scripts, images, or other resources served from a third
party, such as a CDN or jquery.com, you have two options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use protocol-relative URLs for these resources. If the third party does not
serve HTTPS, ask them to. Most already do, including jquery.com.&lt;/li&gt;
&lt;li&gt;Serve the resources from a server that you control, and which offers both HTTP
and HTTPS. This is often a good idea anyway, because then you have better
control over your site&#39;s appearance, performance, and security. In addition,
you don&#39;t have to trust a third party, which is always nice.&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Keep in mind that you also need to change intrasite URLs in your stylesheets, JavaScript, redirect rules, &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags, and CSP declarations, not just in the HTML pages. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;redirect-http-to-https&quot;&gt;Redirect HTTP to HTTPS &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/enable-https/#redirect-http-to-https&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You need to put a &lt;a href=&quot;https://support.google.com/webmasters/answer/139066&quot; rel=&quot;noopener&quot;&gt;canonical link&lt;/a&gt;
at the head of your page to tell search engines that HTTPS is the best way to
get to your site.&lt;/p&gt;
&lt;p&gt;Set &lt;code&gt;&amp;lt;link rel=&amp;quot;canonical&amp;quot; href=&amp;quot;https://…&amp;quot;/&amp;gt;&lt;/code&gt; tags in your pages. This
helps search engines determine the best way to get to your site.&lt;/p&gt;
&lt;h2 id=&quot;turn-on-strict-transport-security-and-secure-cookies&quot;&gt;Turn on Strict Transport Security and secure cookies &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/enable-https/#turn-on-strict-transport-security-and-secure-cookies&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At this point, you are ready to &amp;quot;lock in&amp;quot; the use of HTTPS.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use HTTP Strict Transport Security (HSTS) to avoid the cost of the 301 redirect.&lt;/li&gt;
&lt;li&gt;Always set the Secure flag on cookies.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;First, use &lt;a href=&quot;https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security&quot; rel=&quot;noopener&quot;&gt;Strict Transport Security&lt;/a&gt;
to tell clients that they should always connect to your server via HTTPS, even
when following an &lt;code&gt;http://&lt;/code&gt; reference. This defeats attacks such as
&lt;a href=&quot;http://www.thoughtcrime.org/software/sslstrip/&quot; rel=&quot;noopener&quot;&gt;SSL Stripping&lt;/a&gt;,
and also avoids the round-trip cost of the &lt;code&gt;301 redirect&lt;/code&gt; that we enabled in
&lt;a href=&quot;https://web.dev/enable-https/#redirect-http-to-https&quot;&gt;Redirect HTTP to HTTPS&lt;/a&gt;.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Clients that have noted your site as a known HSTS Host are likely to &lt;a href=&quot;https://tools.ietf.org/html/rfc6797#section-12.1&quot;&gt;hard-fail if your site ever has an error in its TLS configuration&lt;/a&gt; (such as an expired certificate). HSTS is explicitly designed this way to ensure that network attackers cannot trick clients into accessing the site without HTTPS. Do not enable HSTS until you are certain that your site operation is robust enough to avoid ever deploying HTTPS with certificate validation errors. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Turn on HTTP Strict Transport Security (HSTS) by setting the
&lt;code&gt;Strict-Transport-Security&lt;/code&gt; header. &lt;a href=&quot;https://www.owasp.org/index.php/HTTP_Strict_Transport_Security&quot; rel=&quot;noopener&quot;&gt;OWASP&#39;s HSTS page has links to
instructions&lt;/a&gt;
for various server software.&lt;/p&gt;
&lt;p&gt;Most web servers offer a similar ability to add custom headers.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; &lt;code&gt;max-age&lt;/code&gt; is measured in seconds. You can start with low values and gradually increase the &lt;code&gt;max-age&lt;/code&gt; as you become more comfortable operating an HTTPS-only site. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;It is also important to make sure that clients never send cookies (such as for
authentication or site preferences) over HTTP. For example, if a user&#39;s
authentication cookie were to be exposed in plain text, the security guarantee of
their entire session would be destroyed—even if you have done everything else
right!&lt;/p&gt;
&lt;p&gt;Therefore, change your web application to always set the Secure flag on cookies
that it sets. &lt;a href=&quot;https://www.owasp.org/index.php/SecureFlag&quot; rel=&quot;noopener&quot;&gt;This OWASP page explains how to set the Secure
flag&lt;/a&gt; in several application
frameworks. Every application framework has a way to set the flag.&lt;/p&gt;
&lt;p&gt;Most web servers offer a simple redirect feature. Use &lt;code&gt;301 (Moved Permanently)&lt;/code&gt;
to indicate to search engines and browsers that the HTTPS version is canonical,
and redirect your users to the HTTPS version of your site from HTTP.&lt;/p&gt;
&lt;h3 id=&quot;search-ranking&quot;&gt;Search ranking &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/enable-https/#search-ranking&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Google uses &lt;a href=&quot;https://googlewebmastercentral.blogspot.com/2014/08/https-as-ranking-signal.html&quot; rel=&quot;noopener&quot;&gt;HTTPS as a positive search quality
indicator&lt;/a&gt;.
Google also publishes a guide for &lt;a href=&quot;https://support.google.com/webmasters/topic/6029673&quot; rel=&quot;noopener&quot;&gt;how to transfer, move, or migrate your
site&lt;/a&gt; while maintaining
its search rank. Bing also publishes &lt;a href=&quot;http://www.bing.com/webmaster/help/webmaster-guidelines-30fba23a&quot; rel=&quot;noopener&quot;&gt;guidelines for
webmasters&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;performance&quot;&gt;Performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/enable-https/#performance&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When the content and application layers are well-tuned (see
&lt;a href=&quot;https://stevesouders.com/&quot; rel=&quot;noopener&quot;&gt;Steve Souders&#39; books&lt;/a&gt; for great
advice), the remaining TLS performance concerns are generally small, relative
to the overall cost of the application. Additionally, you can reduce and
amortize those costs. (For great advice on TLS optimization and generally, see
&lt;a href=&quot;https://hpbn.co/&quot; rel=&quot;noopener&quot;&gt;High Performance Browser Networking&lt;/a&gt; by Ilya Grigorik.)
See also Ivan Ristic&#39;s &lt;a href=&quot;https://www.feistyduck.com/books/openssl-cookbook/&quot; rel=&quot;noopener&quot;&gt;OpenSSL
Cookbook&lt;/a&gt; and
&lt;a href=&quot;https://www.feistyduck.com/books/bulletproof-ssl-and-tls/&quot; rel=&quot;noopener&quot;&gt;Bulletproof SSL And TLS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In some cases, TLS can &lt;em&gt;improve&lt;/em&gt; performance, mostly as a result of making
HTTP/2 possible. Chris Palmer gave a talk on &lt;a href=&quot;https://developers.google.com/web/shows/cds/2014/tls-all-the-things&quot; rel=&quot;noopener&quot;&gt;HTTPS and HTTP/2 performance at
Chrome Dev Summit 2014&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;referer-headers&quot;&gt;Referer headers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/enable-https/#referer-headers&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When users follow links from your HTTPS site to other HTTP sites, user agents
don&#39;t send the Referer header. If this is a problem, there are several ways to
solve it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The other sites should migrate to HTTPS. If referee sites can complete the
&lt;a href=&quot;https://web.dev/enable-https/#enable-https-on-your-servers&quot;&gt;Enable HTTPS on your servers&lt;/a&gt; section of
this guide, you can change links in your site to theirs from &lt;code&gt;http://&lt;/code&gt; to
&lt;code&gt;https://&lt;/code&gt;, or you can use protocol-relative links.&lt;/li&gt;
&lt;li&gt;To work around a variety of problems with Referer headers, use the new
&lt;a href=&quot;http://www.w3.org/TR/referrer-policy/#referrer-policy-delivery-meta&quot; rel=&quot;noopener&quot;&gt;Referrer Policy standard&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because search engines are migrating to HTTPS, in the future, you are likely
to see &lt;em&gt;more&lt;/em&gt; Referer headers when you migrate to HTTPS.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; According to the &lt;a href=&quot;https://tools.ietf.org/html/rfc2616#section-15.1.3&quot;&gt;HTTP RFC&lt;/a&gt;, clients &lt;strong&gt;SHOULD NOT&lt;/strong&gt; include a Referer header field in a (non-secure) HTTP request if the referring page is transferred with a secure protocol. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;ad-revenue&quot;&gt;Ad revenue &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/enable-https/#ad-revenue&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Site operators that monetize their site by showing ads want to make sure that
migrating to HTTPS does not reduce ad impressions. But due to mixed content
security concerns, an HTTP &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; doesn&#39;t work in an HTTPS page. There is a
tricky collective action problem here: until advertisers publish over HTTPS,
site operators cannot migrate to HTTPS without losing ad revenue; but until site
operators migrate to HTTPS, advertisers have little motivation to publish HTTPS.&lt;/p&gt;
&lt;p&gt;Advertisers should at least offer ad service via HTTPS (such as by completing
the &amp;quot;Enable HTTPS on your servers&amp;quot; section on this page). Many already do. You
should ask advertisers that do not serve HTTPS at all to at least start.
You may wish to defer completing
&lt;a href=&quot;https://web.dev/enable-https/#make-intrasite-urls-relative&quot;&gt;Make IntraSite URLs relative&lt;/a&gt; until enough
advertisers interoperate properly.&lt;/p&gt;
</content>
    <author>
      <name>Chris Palmer</name>
    </author><author>
      <name>Matt Gaunt</name>
    </author>
  </entry>
  
  <entry>
    <title>Important Security Terminology</title>
    <link href="https://web.dev/intro-to-security-terminology/"/>
    <updated>2015-03-27T00:00:00Z</updated>
    <id>https://web.dev/intro-to-security-terminology/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/intro-to-security-terminology/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Used public/private keys to sign and decrypt messages between the browser and the server.&lt;/li&gt;
&lt;li&gt;A certificate authority (CA) is an organization that vouches for the mapping between the public keys and public DNS names (such as &amp;quot;www.foobar.com&amp;quot;).&lt;/li&gt;
&lt;li&gt;A certificate signing request (CSR) is a data format that bundles a public key together with some metadata about the entity that owns the key&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-are-the-public-and-private-key-pairs&quot;&gt;What are the public and private key pairs? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/intro-to-security-terminology/#what-are-the-public-and-private-key-pairs&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A &lt;strong&gt;public/private key pair&lt;/strong&gt; is a pair of very large numbers that are used
as encryption and decryption keys, and that share a special mathematical
relationship. A common system for key pairs is the &lt;strong&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/RSA_(cryptosystem)&quot; rel=&quot;noopener&quot;&gt;RSA
cryptosystem&lt;/a&gt;&lt;/strong&gt;. The &lt;strong&gt;public
key&lt;/strong&gt; is used to encrypt messages, and the messages can only be feasibly
decrypted with the corresponding &lt;strong&gt;private key&lt;/strong&gt;. Your web server advertises
its public key to the world, and clients (such as web browsers) use that to
bootstrap a secure channel to your server.&lt;/p&gt;
&lt;h2 id=&quot;what-is-a-certificate-authority&quot;&gt;What is a certificate authority? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/intro-to-security-terminology/#what-is-a-certificate-authority&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A &lt;strong&gt;certification authority (CA)&lt;/strong&gt; is an organization that vouches for the
mapping between public keys and public DNS names (such as &amp;quot;www.foobar.com&amp;quot;).
For example, how is a client to know if a particular public key is the &lt;em&gt;true&lt;/em&gt;
public key for www.foobar.com? A priori, there is no way to know. A CA vouches
for a particular key as being the true one for a particular site by using its
own private key to &lt;strong&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/RSA_(cryptosystem)#Signing_messages&quot; rel=&quot;noopener&quot;&gt;cryptographically
sign&lt;/a&gt;&lt;/strong&gt; the
website&#39;s public key. This signature is computationally infeasible to forge.
Browsers (and other clients) maintain &lt;strong&gt;trust anchor stores&lt;/strong&gt; containing the
public keys owned by the well-known CAs, and they use those public keys to
&lt;strong&gt;cryptographically verify&lt;/strong&gt; the CA&#39;s signatures.&lt;/p&gt;
&lt;p&gt;An &lt;strong&gt;X.509 certificate&lt;/strong&gt; is a data format that bundles a public key together
with some metadata about the entity that owns the key. In the case of the web,
the owner of the key is the site operator, and the important metadata is the DNS
name of the web server. When a client connects to an HTTPS web server, the web
server presents its certificate for the client to verify. The client verifies
that the certificate has not expired, that the DNS name matches the name of the
server the client is trying to connect to, and that a known trust anchor CA has
signed the certificate. In most cases, CAs do not directly sign web server
certificates; usually, there is a &lt;strong&gt;chain of certificates&lt;/strong&gt; linking a trust
anchor to an intermediate signer or signers, and finally to the web server&#39;s
own certificate (the &lt;strong&gt;end entity&lt;/strong&gt;).&lt;/p&gt;
&lt;h2 id=&quot;what-is-a-certificate-signing-request&quot;&gt;What is a certificate signing request? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/intro-to-security-terminology/#what-is-a-certificate-signing-request&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A &lt;strong&gt;certificate signing request (CSR)&lt;/strong&gt; is a data format which, like a
certificate, bundles a public key together with some metadata about the entity
that owns the key. However, clients do not interpret CSRs; CAs do. When you seek
to have a CA vouch for your web server&#39;s public key, you send the CA a CSR. The
CA validates the information in the CSR and uses it to generate a certificate.
The CA then sends you the final certificate, and you install that certificate (or,
more likely, a certificate chain) and your private key on your web server.&lt;/p&gt;
</content>
    <author>
      <name>Chris Palmer</name>
    </author><author>
      <name>Matt Gaunt</name>
    </author>
  </entry>
  
  <entry>
    <title>Introduction to fetch()</title>
    <link href="https://web.dev/introduction-to-fetch/"/>
    <updated>2015-03-10T00:00:00Z</updated>
    <id>https://web.dev/introduction-to-fetch/</id>
    <content type="html" mode="escaped">&lt;p&gt;&lt;em&gt;So long XMLHttpRequest&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;fetch()&lt;/code&gt; allows you to make network requests similar to XMLHttpRequest
(XHR). The main difference is that the Fetch API uses Promises, which enables a
simpler and cleaner API, avoiding callback hell and having to remember the complex API of
XMLHttpRequest.&lt;/p&gt;
&lt;div class=&quot;wdi-browser-compat&quot;&gt;
  &lt;span class=&quot;wdi-browser-compat__label&quot;&gt;Browser support&lt;/span&gt;
  &lt;ul class=&quot;wdi-browser-compat__items&quot;&gt;
    &lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;chrome&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Chrome 42, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      42
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox 39, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      39
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Edge 14, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      14
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Safari 10.1, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      10.1
    &lt;/span&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/fetch#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;a href=&quot;https://fetch.spec.whatwg.org/&quot; rel=&quot;noopener&quot;&gt;Fetch API&lt;/a&gt; has been available in the
&lt;a href=&quot;https://w3c.github.io/ServiceWorker/&quot; rel=&quot;noopener&quot;&gt;Service Worker&lt;/a&gt; global
scope since Chrome 40, but it&#39;ll be enabled in the window scope in Chrome 42.
There is also a rather fetching &lt;a href=&quot;https://github.com/github/fetch&quot; rel=&quot;noopener&quot;&gt;polyfill by GitHub&lt;/a&gt; that you can use today.&lt;/p&gt;
&lt;p&gt;If you&#39;ve never used
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise&quot; rel=&quot;noopener&quot;&gt;Promises&lt;/a&gt;
before, check out &lt;a href=&quot;https://web.dev/promises/&quot;&gt;Introduction to JavaScript Promises&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;basic-fetch-request&quot;&gt;Basic Fetch Request &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/introduction-to-fetch/#basic-fetch-request&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let&#39;s start by comparing a simple example implemented with an &lt;code&gt;XMLHttpRequest&lt;/code&gt;
and then with &lt;code&gt;fetch&lt;/code&gt;. We just want to request a URL, get a response and parse
it as JSON.&lt;/p&gt;
&lt;h3 id=&quot;xmlhttprequest&quot;&gt;XMLHttpRequest &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/introduction-to-fetch/#xmlhttprequest&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;An &lt;code&gt;XMLHttpRequest&lt;/code&gt; would need two listeners to be set to handle the success
and error cases and a call to &lt;code&gt;open()&lt;/code&gt; and &lt;code&gt;send()&lt;/code&gt;. &lt;em&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest&quot; rel=&quot;noopener&quot;&gt;Example from MDN docs&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reqListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;responseText&lt;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;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;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;reqError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;token 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;Fetch Error :-S&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; oReq &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;XMLHttpRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;oReq&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onload &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; reqListener&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;oReq&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;onerror &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; reqError&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;oReq&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;get&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./api/some.json&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;oReq&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;fetch&quot;&gt;Fetch &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/introduction-to-fetch/#fetch&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Our fetch request looks a little 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 function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./api/some.json&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&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;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;status &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token 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;Looks like there was a problem. Status Code: &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;br /&gt;            response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;status&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;        &lt;span class=&quot;token comment&quot;&gt;// Examine the text in the response&lt;/span&gt;&lt;br /&gt;        response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;data&lt;/span&gt;&lt;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;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&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 function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;/span&gt;&lt;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;Fetch Error :-S&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token 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;We start by checking that the response status is 200 before parsing the response
as JSON.&lt;/p&gt;
&lt;p&gt;The response of a &lt;code&gt;fetch()&lt;/code&gt; request is a
&lt;a href=&quot;https://streams.spec.whatwg.org/&quot; rel=&quot;noopener&quot;&gt;Stream&lt;/a&gt;
object, which means that when we call the &lt;code&gt;json()&lt;/code&gt; method, a Promise is returned
since the reading of the stream will happen asynchronously.&lt;/p&gt;
&lt;h2 id=&quot;response-metadata&quot;&gt;Response Metadata &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/introduction-to-fetch/#response-metadata&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the previous example we looked at the status of the
&lt;a href=&quot;https://fetch.spec.whatwg.org/#responses&quot; rel=&quot;noopener&quot;&gt;Response&lt;/a&gt; object as well as how to
parse the response as JSON. Other metadata we may want to access, like headers,
are illustrated 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 function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;users.json&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    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;response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Content-Type&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Date&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    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;response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;status&lt;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;response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;statusText&lt;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;response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type&lt;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;response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;response-types&quot;&gt;Response Types &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/introduction-to-fetch/#response-types&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When we make a fetch request, the response will be given a &lt;code&gt;response.type&lt;/code&gt;
of &amp;quot;&lt;a href=&quot;https://fetch.spec.whatwg.org/#concept-filtered-response-basic&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;basic&lt;/code&gt;&lt;/a&gt;&amp;quot;,
&amp;quot;&lt;a href=&quot;https://fetch.spec.whatwg.org/#concept-filtered-response-cors&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;cors&lt;/code&gt;&lt;/a&gt;&amp;quot; or
&amp;quot;&lt;a href=&quot;https://fetch.spec.whatwg.org/#concept-filtered-response-opaque&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;opaque&lt;/code&gt;&lt;/a&gt;&amp;quot;.
These &lt;code&gt;types&lt;/code&gt; indicate where the resource has come from and can be used to
inform how you should treat the response object.&lt;/p&gt;
&lt;p&gt;When a request is made for a resource on the same origin, the response will have
a &lt;code&gt;basic&lt;/code&gt; type and there aren&#39;t any restrictions on what you can view from the
response.&lt;/p&gt;
&lt;p&gt;If a request is made for a resource on another origin which returns &lt;a href=&quot;https://enable-cors.org/&quot; rel=&quot;noopener&quot;&gt;the CORs headers&lt;/a&gt;, then the type is &lt;code&gt;cors&lt;/code&gt;. &lt;code&gt;cors&lt;/code&gt; and &lt;code&gt;basic&lt;/code&gt;
responses are almost identical except that a &lt;code&gt;cors&lt;/code&gt; response restricts the
headers you can view to &lt;code&gt;Cache-Control&lt;/code&gt;, &lt;code&gt;Content-Language&lt;/code&gt;,
&lt;code&gt;Content-Type&lt;/code&gt;, &lt;code&gt;Expires&lt;/code&gt;, &lt;code&gt;Last-Modified&lt;/code&gt;, and &lt;code&gt;Pragma&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;An &lt;code&gt;opaque&lt;/code&gt; response is for a request made for a resource on a different origin
that doesn&#39;t return CORS headers. With an opaque response we won&#39;t be able to
read the data returned or view the status of the request, meaning we can&#39;t check
if the request was successful or not.&lt;/p&gt;
&lt;p&gt;You can define a mode for a fetch request such that only certain requests will
resolve. The modes you can set are as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;same-origin&lt;/code&gt; only succeeds for requests for assets on the same origin, all
other requests will reject.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cors&lt;/code&gt; will allow requests for assets on the same-origin and other origins which return the
appropriate CORs headers.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cors-with-forced-preflight&lt;/code&gt; will always perform a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/CORS#Preflighted_requests&quot; rel=&quot;noopener&quot;&gt;preflight
check&lt;/a&gt; before making the actual request.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;no-cors&lt;/code&gt; is intended to make requests to other origins that do not have CORS
headers and result in an &lt;code&gt;opaque&lt;/code&gt; response, but as stated, this isn&#39;t
possible in the window global scope at the moment.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To define the mode, add an options object as the second parameter in the
&lt;code&gt;fetch&lt;/code&gt; request and define the mode in that object:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;http://some-site.com/cors-enabled/some.json&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;cors&#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 function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;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;Request successful&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;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;br /&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;Request failed&#39;&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;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;chaining-promises&quot;&gt;Chaining Promises &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/introduction-to-fetch/#chaining-promises&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One of the great features of promises is the ability to chain them together. For
fetch, this allows you to share logic across fetch requests.&lt;/p&gt;
&lt;p&gt;If you are working with a JSON API, you&#39;ll need to check the status and parse the JSON
for each response. You can simplify your code by defining the status and JSON
parsing in separate functions which return promises, freeing you to only worry
about handling the final data and the error case.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;status &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;status &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Promise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Promise&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;statusText&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;users.json&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;status&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;json&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;data&lt;/span&gt;&lt;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;Request succeeded with JSON response&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;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;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;Request failed&#39;&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 punctuation&quot;&gt;}&lt;/span&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;We define the &lt;code&gt;status&lt;/code&gt; function which checks the &lt;strong&gt;response.status&lt;/strong&gt; and
returns the result of
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Promise.resolve()&lt;/code&gt;&lt;/a&gt;
or
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise/reject&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Promise.reject()&lt;/code&gt;&lt;/a&gt;,
which return a resolved or rejected Promise. This is the first method
called in our &lt;code&gt;fetch()&lt;/code&gt; chain, if it resolves, we then call our &lt;code&gt;json()&lt;/code&gt;
method which again returns a Promise from the &lt;code&gt;response.json()&lt;/code&gt; call. After
this we have an object of the parsed JSON. If the parsing fails the Promise is
rejected and the catch statement executes.&lt;/p&gt;
&lt;p&gt;The great thing with this is that you can share the logic across all of your
fetch requests, making code easier to maintain, read and test.&lt;/p&gt;
&lt;h2 id=&quot;post-request&quot;&gt;POST Request &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/introduction-to-fetch/#post-request&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It&#39;s not uncommon for web apps to want to call an API with a POST method and
supply some parameters in the body of the request.&lt;/p&gt;
&lt;p&gt;To do this we can set the &lt;code&gt;method&lt;/code&gt; and &lt;code&gt;body&lt;/code&gt; parameters in the &lt;code&gt;fetch()&lt;/code&gt;
options.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;post&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token string-property property&quot;&gt;&quot;Content-type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/x-www-form-urlencoded; charset=UTF-8&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;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;foo=bar&amp;amp;lorem=ipsum&#39;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;json&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;data&lt;/span&gt;&lt;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;Request succeeded with JSON response&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;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;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;Request failed&#39;&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 punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;sending-credentials-with-a-fetch-request&quot;&gt;Sending Credentials with a Fetch Request &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/introduction-to-fetch/#sending-credentials-with-a-fetch-request&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Should you want to make a fetch request with credentials such as cookies, you
should set the &lt;code&gt;credentials&lt;/code&gt; of the request to &lt;code&gt;&amp;quot;include&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;credentials&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;include&#39;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;faq&quot;&gt;FAQ &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/introduction-to-fetch/#faq&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;how-do-i-cancel-a-fetch-request&quot;&gt;How do I cancel a fetch() request? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/introduction-to-fetch/#how-do-i-cancel-a-fetch-request&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;At the moment there is no way to cancel a fetch, but this is being
&lt;a href=&quot;https://github.com/whatwg/fetch/issues/20&quot; rel=&quot;noopener&quot;&gt;discussed on GitHub&lt;/a&gt;.
H/T &lt;a href=&quot;https://twitter.com/jaffathecake&quot; rel=&quot;noopener&quot;&gt;@jaffathecake&lt;/a&gt; for this link.&lt;/p&gt;
&lt;h3 id=&quot;is-there-a-polyfill&quot;&gt;Is there a polyfill? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/introduction-to-fetch/#is-there-a-polyfill&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/github/fetch&quot; rel=&quot;noopener&quot;&gt;GitHub has a polyfill for fetch&lt;/a&gt;.
H/T &lt;a href=&quot;https://twitter.com/Nexii&quot; rel=&quot;noopener&quot;&gt;@Nexii&lt;/a&gt; for pointing this out.&lt;/p&gt;
&lt;h3 id=&quot;why-is-no-cors-supported-in-service-workers-but-not-the-window&quot;&gt;Why is &amp;quot;no-cors&amp;quot; supported in service workers but not the window? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/introduction-to-fetch/#why-is-no-cors-supported-in-service-workers-but-not-the-window&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This is due to a security concern, you can &lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/detail?id=457157&amp;amp;q=fetch%20no-cors&amp;amp;colspec=ID%20Pri%20M%20Week%20ReleaseBlock%20Cr%20Status%20Owner%20Summary%20OS%20Modified&quot; rel=&quot;noopener&quot;&gt;learn more here&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Matt Gaunt</name>
    </author>
  </entry>
  
  <entry>
    <title>Push notifications FAQ</title>
    <link href="https://web.dev/push-notifications-faq/"/>
    <updated>2014-10-07T00:00:00Z</updated>
    <id>https://web.dev/push-notifications-faq/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;why-doesnt-push-work-when-the-browser-is-closed&quot;&gt;Why doesn&#39;t push work when the browser is closed? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-faq/#why-doesnt-push-work-when-the-browser-is-closed&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This question crops up quite a bit, largely because there are a few scenarios that make it
difficult to reason with and understand.&lt;/p&gt;
&lt;p&gt;Let&#39;s start with Android. The Android OS is designed to listen for push messages and upon
receiving one, wake up the appropriate Android app to handle the push message,
regardless of whether the app is closed or not.&lt;/p&gt;
&lt;p&gt;This is exactly the same with any browser on Android, the browser will be woken
up when a push message is received and the browser will then wake up your
service worker and dispatch the push event.&lt;/p&gt;
&lt;p&gt;On desktop OS&#39;s, it&#39;s more nuanced and it&#39;s easiest to explain on Mac OS X
because there is a visual indicator to help explain the different scenarios.&lt;/p&gt;
&lt;p&gt;On Mac OS X, you can tell if a program is running or not by a marking
under the app icon in the dock.&lt;/p&gt;
&lt;p&gt;If you compare the two Chrome icons in the following dock, the one on the left
is running, as illustrated by the marking under the icon, whereas the Chrome
on the right is &lt;strong&gt;not running&lt;/strong&gt;, hence the lack of the marking underneath.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Example of OS X&quot; decoding=&quot;async&quot; height=&quot;117&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 467px) 467px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/04puAvaHbqNI3JvCYp5T.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/04puAvaHbqNI3JvCYp5T.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/04puAvaHbqNI3JvCYp5T.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/04puAvaHbqNI3JvCYp5T.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/04puAvaHbqNI3JvCYp5T.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/04puAvaHbqNI3JvCYp5T.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/04puAvaHbqNI3JvCYp5T.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/04puAvaHbqNI3JvCYp5T.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/04puAvaHbqNI3JvCYp5T.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/04puAvaHbqNI3JvCYp5T.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/04puAvaHbqNI3JvCYp5T.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/04puAvaHbqNI3JvCYp5T.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/04puAvaHbqNI3JvCYp5T.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/04puAvaHbqNI3JvCYp5T.png?auto=format&amp;w=934 934w&quot; width=&quot;467&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;In the context of receiving push messages on desktop, you will receive messages
when the browser is running, i.e. has the marking underneath the icon.&lt;/p&gt;
&lt;p&gt;This means the browser can have no windows open, and you&#39;ll still receive the push message in
your service worker, because the browser in running in the background.&lt;/p&gt;
&lt;p&gt;The only time a push won&#39;t be received is when the browser is completely closed, i.e. not running
at all (no marking). The same applies for Windows, although it&#39;s a little trickier to determine
whether or not Chrome is running in the background.&lt;/p&gt;
&lt;h2 id=&quot;how-do-i-make-my-home-screen-web-app-open-fullscreen-from-a-push&quot;&gt;How do I make my home screen web app open fullscreen from a push? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-faq/#how-do-i-make-my-home-screen-web-app-open-fullscreen-from-a-push&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On Chrome for Android, a web app can be added to the home screen and when the web app is opened
from the home screen, it can launch in fullscreen mode without the URL bar, as shown below.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Home screen Icon to Fullscreen&quot; decoding=&quot;async&quot; height=&quot;549&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tWNS7V1x70AfEkirDg6L.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tWNS7V1x70AfEkirDg6L.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tWNS7V1x70AfEkirDg6L.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tWNS7V1x70AfEkirDg6L.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tWNS7V1x70AfEkirDg6L.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tWNS7V1x70AfEkirDg6L.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tWNS7V1x70AfEkirDg6L.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tWNS7V1x70AfEkirDg6L.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tWNS7V1x70AfEkirDg6L.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tWNS7V1x70AfEkirDg6L.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tWNS7V1x70AfEkirDg6L.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tWNS7V1x70AfEkirDg6L.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tWNS7V1x70AfEkirDg6L.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tWNS7V1x70AfEkirDg6L.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tWNS7V1x70AfEkirDg6L.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tWNS7V1x70AfEkirDg6L.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tWNS7V1x70AfEkirDg6L.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tWNS7V1x70AfEkirDg6L.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;To keep this experience consistent, developers want their clicked notifications to open their
web app in fullscreen as well.&lt;/p&gt;
&lt;p&gt;Chrome &amp;quot;sort of&amp;quot; implemented this, although you may find it unreliable
and difficult to reason with. The relevant implementation details are:&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Sites which have been added to homescreen on Android should be allowed to open in standalone mode in response to push notifications. As Chromium cannot detect what sites are on the homescreen after they have been added, the heuristic is sites which have been launched from homescreen within the last ten days will be opened in standalone from a tap on a notification. --&lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/detail?id=541711&quot;&gt;Chrome Issue&lt;/a&gt; &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;What this means is that unless your user is visiting your site through the home screen icon
fairly regularly, your notifications will open in the normal browser UI.&lt;/p&gt;
&lt;p&gt;This issue will be worked on further.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; This is just the behavior of Chrome, though other browsers may do different things as well. Feel free to &lt;a href=&quot;https://github.com/gauntface/web-push-book/issues&quot;&gt;raise an issue&lt;/a&gt; if you have anything to add to this discussion. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;why-is-this-any-better-than-web-sockets&quot;&gt;Why is this any better than web sockets? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-faq/#why-is-this-any-better-than-web-sockets&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A service worker can be brought to life when the browser window is
closed. A web socket will only live as long as the browser and
web page is kept open.&lt;/p&gt;
&lt;h2 id=&quot;what-is-the-deal-with-gcm,-fcm,-web-push-and-chrome&quot;&gt;What is the deal with GCM, FCM, Web Push and Chrome? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-faq/#what-is-the-deal-with-gcm,-fcm,-web-push-and-chrome&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This question has a number of facets to it and the easiest way to explain is to
step through the history of web push and Chrome. (Don&#39;t worry, it&#39;s short.)&lt;/p&gt;
&lt;h3 id=&quot;december-2014&quot;&gt;December 2014 &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-faq/#december-2014&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When Chrome first implemented web push, Chrome used Google Cloud Messaging (GCM)
to power the sending of push messages from the server to the browser.&lt;/p&gt;
&lt;p&gt;This &lt;strong&gt;was not web push&lt;/strong&gt;. There are a few reasons this early set-up of Chrome and GCM wasn&#39;t
&amp;quot;real&amp;quot; web push.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GCM requires developers to set up an account on the Google Developers Console.&lt;/li&gt;
&lt;li&gt;Chrome and GCM needed a special sender ID to be shared by a web app to be able to set up
messaging correctly.&lt;/li&gt;
&lt;li&gt;GCM&#39;s servers accepted a custom API request that wasn&#39;t a web standard.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;july-2016&quot;&gt;July 2016 &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-faq/#july-2016&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In July a new feature in web push landed - Application Server Keys (or VAPID, as
the spec is known). When Chrome added support for this new API, it used Firebase
Cloud Messaging (also known as FCM) instead of GCM as a messaging service. This
is important for a few reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Chrome and Application Sever Keys &lt;strong&gt;do not&lt;/strong&gt; need any kind of project to be set up with Google
or Firebase. It&#39;ll just work.&lt;/li&gt;
&lt;li&gt;FCM supports the &lt;em&gt;web push protocol&lt;/em&gt;, which is the API that all web push services will
support. This means that regardless of what push service a browser uses, you just make the same
kind of request and it&#39;ll send the message.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;why-is-it-confusing-today&quot;&gt;Why is it confusing today? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-faq/#why-is-it-confusing-today&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There is a large amount of confusion now that content has been written on the topic of web
push, much of which references GCM or FCM. If content references GCM, you should probably treat
it as a sign that it&#39;s either old content OR it&#39;s focusing too much on Chrome. (I&#39;m guilty of
doing this in a number of old posts.)&lt;/p&gt;
&lt;p&gt;Instead, think of web push as consisting of a browser, which uses a push service to manage
sending and receiving messages, where the push service will accept a &amp;quot;web push protocol&amp;quot;
request. If you think in these terms, you can ignore which browser and which push service it&#39;s
using and get to work.&lt;/p&gt;
&lt;p&gt;This guide has been written to focus on the standards approach of web push and
purposefully ignores anything else.&lt;/p&gt;
&lt;h2 id=&quot;firebase-has-a-javascript-sdk-what-and-why&quot;&gt;Firebase has a JavaScript SDK. What and Why? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-faq/#firebase-has-a-javascript-sdk-what-and-why&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For those of you who have found the Firebase web SDK and noticed it has a messaging API for
JavaScript, you may be wondering how it differs from web push.&lt;/p&gt;
&lt;p&gt;The messaging SDK (known as Firebase Cloud Messaging JS SDK) does a few tricks behind the
scenes to make it easier to implement web push.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Instead of worrying about a &lt;code&gt;PushSubscription&lt;/code&gt; and its various fields,
you only need to worry about an FCM Token (a string).&lt;/li&gt;
&lt;li&gt;Using the tokens for each user, you can use the proprietary FCM API to
trigger push messages. This API doesn&#39;t require encrypting payloads. You
can send a plain text payload in a POST request body.&lt;/li&gt;
&lt;li&gt;FCM&#39;s proprietary API supports custom features, for example
&lt;a href=&quot;https://firebase.google.com/docs/cloud-messaging/android/topic-messaging&quot; rel=&quot;noopener&quot;&gt;FCM Topics&lt;/a&gt;
(It works on the web too, though it&#39;s poorly documented).&lt;/li&gt;
&lt;li&gt;Finally, FCM supports Android, iOS and web, so for some teams it is
easier to work with in existing projects.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This uses web push behind the scenes, but its goal is to abstract it away.&lt;/p&gt;
&lt;p&gt;Like I said in the previous question, if you consider web push as just a browser and a push
service, then you can consider the Messaging SDK in Firebase as a library to simplify
implementing web push.&lt;/p&gt;
&lt;h2 id=&quot;where-to-go-next&quot;&gt;Where to go next &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-faq/#where-to-go-next&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-overview/&quot;&gt;Web Push Notification Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-how-push-works/&quot;&gt;How Push Works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-subscribing-a-user/&quot;&gt;Subscribing a User&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-permissions-ux/&quot;&gt;Permission UX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/sending-messages-with-web-push-libraries/&quot;&gt;Sending Messages with Web Push Libraries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-web-push-protocol/&quot;&gt;Web Push Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-handling-messages/&quot;&gt;Handling Push Events&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-display-a-notification/&quot;&gt;Displaying a Notification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-notification-behaviour/&quot;&gt;Notification Behavior&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-common-notification-patterns/&quot;&gt;Common Notification Patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Push Notifications FAQ&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-common-issues-and-reporting-bugs/&quot;&gt;Common Issues and Reporting Bugs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;code-labs&quot;&gt;Code labs &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/push-notifications-faq/#code-labs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-client-codelab/&quot;&gt;Build a push notification client&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/push-notifications-server-codelab/&quot;&gt;Build a push notification server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Matt Gaunt</name>
    </author>
  </entry>
  
  <entry>
    <title>Add touch to your site</title>
    <link href="https://web.dev/add-touch-to-your-site/"/>
    <updated>2014-01-01T00:00:00Z</updated>
    <id>https://web.dev/add-touch-to-your-site/</id>
    <content type="html" mode="escaped">&lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;Rwc4fHUnGuU&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
&lt;p&gt;Touchscreens are available on more and more devices, ranging from phones to
desktop screens. When your users choose to interact with your UI, your app
should respond to their touch in intuitive ways.&lt;/p&gt;
&lt;h2 id=&quot;respond-to-element-states&quot;&gt;Respond to element states &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-touch-to-your-site/#respond-to-element-states&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Have you ever touched or clicked an element on a web page and questioned
whether the site actually detected it?&lt;/p&gt;
&lt;p&gt;Simply altering the color of an element as users touch or interact with parts
of your UI gives a basic reassurance that your site is working. Not only does
this alleviate frustration, it can also give a snappy and responsive feel.&lt;/p&gt;
&lt;p&gt;DOM elements can inherit any of the following states: default, focus, hover
and active. To change our UI for each of these states, we need to apply styles
to the following pseudo classes &lt;code&gt;:hover&lt;/code&gt;, &lt;code&gt;:focus&lt;/code&gt; and &lt;code&gt;:active&lt;/code&gt; as shown below:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.btn&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #4285f4&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.btn:hover&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #296cdb&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.btn:focus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #0f52c1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;/* The outline parameter suppresses the border&lt;br /&gt;  color / outline when focused */&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;outline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.btn:active&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #0039a8&lt;span 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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/input/touch/states-example.html&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Image illustrating different colors for button states&quot; decoding=&quot;async&quot; height=&quot;300&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BoIVrO4Nh0MdDMHofs6q.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BoIVrO4Nh0MdDMHofs6q.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BoIVrO4Nh0MdDMHofs6q.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BoIVrO4Nh0MdDMHofs6q.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BoIVrO4Nh0MdDMHofs6q.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BoIVrO4Nh0MdDMHofs6q.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BoIVrO4Nh0MdDMHofs6q.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BoIVrO4Nh0MdDMHofs6q.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BoIVrO4Nh0MdDMHofs6q.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BoIVrO4Nh0MdDMHofs6q.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BoIVrO4Nh0MdDMHofs6q.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BoIVrO4Nh0MdDMHofs6q.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BoIVrO4Nh0MdDMHofs6q.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BoIVrO4Nh0MdDMHofs6q.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/BoIVrO4Nh0MdDMHofs6q.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;On most mobile browsers &lt;em&gt;hover&lt;/em&gt; and/or &lt;em&gt;focus&lt;/em&gt; states will apply to an element
after it&#39;s been tapped.&lt;/p&gt;
&lt;p&gt;Consider carefully what styles you set and how they will look to the user after
they finish their touch.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Anchor tags and buttons may have different behavior in different browsers, so assume in some cases &lt;strong&gt;hover&lt;/strong&gt; will remain and in others &lt;strong&gt;focus&lt;/strong&gt; will remain. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;suppressing-default-browser-styles&quot;&gt;Suppressing default browser styles &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-touch-to-your-site/#suppressing-default-browser-styles&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Once you add styles for the different states, you&#39;ll notice that most browsers
implement their own styles in response to a user’s touch. This is largely
because when mobile devices first launched, a number of sites didn’t
have styling for the &lt;code&gt;:active&lt;/code&gt; state. As a result, many browsers added
additional highlight color or style to give the user feedback.&lt;/p&gt;
&lt;p&gt;Most browsers use the &lt;code&gt;outline&lt;/code&gt; CSS property to display a ring around an
element when an element is focused. You can suppress it with:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.btn:focus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;outline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;/* Add replacement focus styling here (i.e. border) */&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;Safari and Chrome add a tap highlight color which can be prevented with the
&lt;code&gt;-webkit-tap-highlight-color&lt;/code&gt; CSS property:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* Webkit / Chrome Specific CSS to remove tap&lt;br /&gt;highlight color */&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.btn&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;-webkit-tap-highlight-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transparent&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/input/touch/states-example.html&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Internet Explorer on Windows Phone has a similar behavior, but is suppressed
via a meta tag:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;msapplication-tap-highlight&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;no&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Firefox has two side effects to handle.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;-moz-focus-inner&lt;/code&gt; pseudo class, which adds an outline on
touchable elements, you can remove by setting &lt;code&gt;border: 0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you are using a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element on Firefox, you get a gradient
applied, which you can remove by setting &lt;code&gt;background-image: none&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* Firefox Specific CSS to remove button&lt;br /&gt;differences and focus ring */&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.btn&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.btn::-moz-focus-inner&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/input/touch/states-example.html&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Only suppress the default styles mentioned above if you have pseudo classes for &lt;code&gt;:hover&lt;/code&gt;, &lt;code&gt;:active&lt;/code&gt; and &lt;code&gt;:focus&lt;/code&gt;! &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;disabling-user-select&quot;&gt;Disabling user-select &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-touch-to-your-site/#disabling-user-select&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When you&#39;re creating your UI there may be scenarios where you want users
to interact with your elements but you want to suppress the default behavior
of selecting text on long press or dragging a mouse over your UI.&lt;/p&gt;
&lt;p&gt;You can do this with the &lt;code&gt;user-select&lt;/code&gt; CSS property, but beware that
doing this on content can be &lt;strong&gt;extremely&lt;/strong&gt; infuriating
for users if they &lt;em&gt;want&lt;/em&gt; to select the text in the element.
So make sure you use it with caution and sparingly.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* Example: Disable selecting text on a paragraph element: */&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;p.disable-text-selection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;user-select&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;implement-custom-gestures&quot;&gt;Implement custom gestures &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-touch-to-your-site/#implement-custom-gestures&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you have an idea for custom interactions and gestures for your site, there
are two topics to keep in mind:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;How to support all browsers.&lt;/li&gt;
&lt;li&gt;How to keep your frame rate high.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In this article, we&#39;ll look at exactly these topics covering the API&#39;s we need
to support to hit all browsers and then cover how we use these events
efficiently.&lt;/p&gt;
&lt;p&gt;Depending on what you would like your gesture to do, you likely want the
user to interact with one element at a time &lt;em&gt;or&lt;/em&gt; you&#39;ll want them to be able
to interact with multiple elements at the same time.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Don&#39;t forget that some users will want keyboard input and users running assistive technology on a touchscreen device may not be able to perform gestures because they&#39;re intercepted / consumed by the assistive technology. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;We are going to look at two examples in this article, both demonstrating
support for all browsers and how to keep the frame rate high.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Example GIF of touch on document&quot; decoding=&quot;async&quot; height=&quot;500&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 282px) 282px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ebgKNdiNNFkqDFg03VFl.gif?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ebgKNdiNNFkqDFg03VFl.gif?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ebgKNdiNNFkqDFg03VFl.gif?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ebgKNdiNNFkqDFg03VFl.gif?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ebgKNdiNNFkqDFg03VFl.gif?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ebgKNdiNNFkqDFg03VFl.gif?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ebgKNdiNNFkqDFg03VFl.gif?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ebgKNdiNNFkqDFg03VFl.gif?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ebgKNdiNNFkqDFg03VFl.gif?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ebgKNdiNNFkqDFg03VFl.gif?auto=format&amp;w=564 564w&quot; width=&quot;282&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;The first example will allow the user to interact with one element. In this
case you might want all touch events to be given to that one element, as long
as the gesture initially started on the element itself. For example, moving a
finger off the swipe-able element can still control the element.&lt;/p&gt;
&lt;p&gt;This is useful as it provides a great deal of flexibility for the user, but
enforces a restriction on how the user can interact with your UI.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Example GIF of touch on element&quot; decoding=&quot;async&quot; height=&quot;500&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 282px) 282px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nuyIBu5bPy7ihWIOgDkY.gif?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nuyIBu5bPy7ihWIOgDkY.gif?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nuyIBu5bPy7ihWIOgDkY.gif?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nuyIBu5bPy7ihWIOgDkY.gif?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nuyIBu5bPy7ihWIOgDkY.gif?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nuyIBu5bPy7ihWIOgDkY.gif?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nuyIBu5bPy7ihWIOgDkY.gif?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nuyIBu5bPy7ihWIOgDkY.gif?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nuyIBu5bPy7ihWIOgDkY.gif?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nuyIBu5bPy7ihWIOgDkY.gif?auto=format&amp;w=564 564w&quot; width=&quot;282&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;If, however, you expect users to interact with multiple elements at the same
time (using multi-touch), you should restrict the touch to the specific
element.&lt;/p&gt;
&lt;p&gt;This is more flexible for users, but complicates the logic for manipulating
the UI and is less resilient to user error.&lt;/p&gt;
&lt;h3 id=&quot;add-event-listeners&quot;&gt;Add event listeners &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-touch-to-your-site/#add-event-listeners&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In Chrome (version 55 and later), Internet Explorer &amp;amp; Edge,
&lt;code&gt;PointerEvents&lt;/code&gt; are the recommended approach for implementing custom gestures.&lt;/p&gt;
&lt;p&gt;In other browsers &lt;code&gt;TouchEvents&lt;/code&gt; and &lt;code&gt;MouseEvents&lt;/code&gt; are the correct approach.&lt;/p&gt;
&lt;p&gt;The great feature of &lt;code&gt;PointerEvents&lt;/code&gt; is that it merges multiple types of input,
including mouse, touch and pen events, into one set of
callbacks. The events to listen for are &lt;code&gt;pointerdown&lt;/code&gt;, &lt;code&gt;pointermove&lt;/code&gt;,
&lt;code&gt;pointerup&lt;/code&gt; and &lt;code&gt;pointercancel&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The equivalents in other browsers are &lt;code&gt;touchstart&lt;/code&gt;, &lt;code&gt;touchmove&lt;/code&gt;,
&lt;code&gt;touchend&lt;/code&gt; and &lt;code&gt;touchcancel&lt;/code&gt; for touch events and if you wanted to implement
the same gesture for mouse input you&#39;d need to implement &lt;code&gt;mousedown&lt;/code&gt;,
&lt;code&gt;mousemove&lt;/code&gt;, and &lt;code&gt;mouseup&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you have questions about which events to use, check out this table of
&lt;a href=&quot;https://web.dev/add-touch-to-your-site/#touch-mouse-and-pointer-events&quot;&gt;Touch, mouse and pointer events&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Using these events requires calling the &lt;code&gt;addEventListener()&lt;/code&gt; method on a DOM
element, along with the name of an event, a callback function and a boolean.
The boolean determines whether you should catch the event before or after
other elements have had the opportunity to catch and interpret the
events. (&lt;code&gt;true&lt;/code&gt; means you want the event before other elements.)&lt;/p&gt;
&lt;p&gt;Here&#39;s an example of listening for the start of an interaction.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Check if pointer events are supported.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PointerEvent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Add Pointer Event Listener&lt;/span&gt;&lt;br /&gt;  swipeFrontElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;pointerdown&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handleGestureStart&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  swipeFrontElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;pointermove&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handleGestureMove&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  swipeFrontElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;pointerup&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handleGestureEnd&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  swipeFrontElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;pointercancel&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handleGestureEnd&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Add Touch Listener&lt;/span&gt;&lt;br /&gt;  swipeFrontElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;touchstart&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handleGestureStart&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  swipeFrontElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;touchmove&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handleGestureMove&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  swipeFrontElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;touchend&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handleGestureEnd&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  swipeFrontElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;touchcancel&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handleGestureEnd&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Add Mouse Listener&lt;/span&gt;&lt;br /&gt;  swipeFrontElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;mousedown&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handleGestureStart&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/input/touch/touch-demo-1.html&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Because of the design of the API, PointerEvents only need a single &lt;code&gt;pointerdown&lt;/code&gt; event to handle both mouse and touch events. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;handle-single-element-interaction&quot;&gt;Handle single-element interaction &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-touch-to-your-site/#handle-single-element-interaction&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;In the short snippet of code above we only added the starting event listener
for mouse events. The reason for this is that mouse events will only trigger
when the cursor is hovering &lt;em&gt;over&lt;/em&gt; the element the event listener is added to.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;TouchEvents&lt;/code&gt; will track a gesture after it&#39;s started regardless of where the
touch occurs and &lt;code&gt;PointerEvents&lt;/code&gt; will track events regardless of where the touch
occurs after we call &lt;code&gt;setPointerCapture&lt;/code&gt; on a DOM element.&lt;/p&gt;
&lt;p&gt;For mouse move and end events we add the event listeners &lt;em&gt;in&lt;/em&gt; the
gesture start method and add the listeners to the document, meaning it can
track the cursor until the gesture is complete.&lt;/p&gt;
&lt;p&gt;The steps taken to implement this are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add all TouchEvent and PointerEvent listeners. For MouseEvents add &lt;strong&gt;only&lt;/strong&gt;
the start event.&lt;/li&gt;
&lt;li&gt;Inside the start gesture callback, bind the mouse move and end events to
the document. This way all mouse events are received regardless of whether
the event occurs on the original element or not. For PointerEvents we
need to call &lt;code&gt;setPointerCapture()&lt;/code&gt; on our original element to receive
all further events. Then handle the start of the gesture.&lt;/li&gt;
&lt;li&gt;Handle the move events.&lt;/li&gt;
&lt;li&gt;On the end event, remove the mouse move and end listeners from the document
and end the gesture.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Below is a snippet of our &lt;code&gt;handleGestureStart()&lt;/code&gt; method which adds the move
and end events to the document:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Handle the start of gestures&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;handleGestureStart&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;evt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;touches &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;touches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token 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 punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Add the move and end listeners&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PointerEvent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setPointerCapture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pointerId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Add Mouse Listeners&lt;/span&gt;&lt;br /&gt;    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;mousemove&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handleGestureMove&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;mouseup&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handleGestureEnd&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  initialTouchPos &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getGesturePointFromEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;evt&lt;span 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;  swipeFrontElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;transition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;initial&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/input/touch/touch-demo-1.html&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The end callback we add is &lt;code&gt;handleGestureEnd()&lt;/code&gt;, which removes the move
and end event listeners from the document and releases the pointer capture
when the gesture has finished like so:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Handle end gestures&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;handleGestureEnd&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;evt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;touches &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;touches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token 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 punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  rafPending &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Remove Event Listeners&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PointerEvent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;releasePointerCapture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pointerId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Remove Mouse Listeners&lt;/span&gt;&lt;br /&gt;    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;mousemove&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handleGestureMove&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;mouseup&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handleGestureEnd&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;updateSwipeRestPosition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span 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;  initialTouchPos &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;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 function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/input/touch/touch-demo-1.html&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;By following this pattern of adding the move event to the document, if the
user starts interacting with an element and moves their gesture outside of
the element, we&#39;ll continue to get mouse movements regardless of where they
are on the page, because the events are being received from the document.&lt;/p&gt;
&lt;p&gt;This diagram shows what the touch events are doing as we add the
move and end events to the document once a gesture begins.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Illustrating binding touch events to document in &amp;#x60;touchstart&amp;#x60;&quot; decoding=&quot;async&quot; height=&quot;500&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 282px) 282px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/uJFeeOLZfIarpdQ0Orz3.gif?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/uJFeeOLZfIarpdQ0Orz3.gif?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/uJFeeOLZfIarpdQ0Orz3.gif?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/uJFeeOLZfIarpdQ0Orz3.gif?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/uJFeeOLZfIarpdQ0Orz3.gif?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/uJFeeOLZfIarpdQ0Orz3.gif?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/uJFeeOLZfIarpdQ0Orz3.gif?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/uJFeeOLZfIarpdQ0Orz3.gif?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/uJFeeOLZfIarpdQ0Orz3.gif?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/uJFeeOLZfIarpdQ0Orz3.gif?auto=format&amp;w=564 564w&quot; width=&quot;282&quot; /&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;responding-to-touch-efficiently&quot;&gt;Responding to touch efficiently &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-touch-to-your-site/#responding-to-touch-efficiently&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Now that we have the start and end events taken care of we can actually
respond to the touch events.&lt;/p&gt;
&lt;p&gt;For any of the start and move events, you can easily extract &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt;
from an event.&lt;/p&gt;
&lt;p&gt;The following example checks whether the event is from a &lt;code&gt;TouchEvent&lt;/code&gt; by
checking if &lt;code&gt;targetTouches&lt;/code&gt; exists. If it does, then it extracts the
&lt;code&gt;clientX&lt;/code&gt; and &lt;code&gt;clientY&lt;/code&gt; from the first touch.
If the event is a &lt;code&gt;PointerEvent&lt;/code&gt; or &lt;code&gt;MouseEvent&lt;/code&gt; it extracts &lt;code&gt;clientX&lt;/code&gt; and
&lt;code&gt;clientY&lt;/code&gt; directly from the event itself.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getGesturePointFromEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;evt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; point &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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;targetTouches&lt;span class=&quot;token punctuation&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;// Prefer Touch Events&lt;/span&gt;&lt;br /&gt;      point&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;targetTouches&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clientX&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      point&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;y &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;targetTouches&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clientY&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token comment&quot;&gt;// Either Mouse event or Pointer Event&lt;/span&gt;&lt;br /&gt;      point&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clientX&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      point&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;y &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clientY&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; point&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/input/touch/touch-demo-2.html&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;TouchEvent&lt;/code&gt; has three lists containing touch data:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;touches&lt;/code&gt;: list of all current touches on the screen, regardless of
DOM element they are on.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;targetTouches&lt;/code&gt;: list of touches currently on the DOM element the event
is bound to.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;changedTouches&lt;/code&gt;: list of touches which changed resulting in the event
being fired.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In most cases, &lt;code&gt;targetTouches&lt;/code&gt; gives you everything you need and want. (For
more info on these lists see &lt;a href=&quot;https://web.dev/add-touch-to-your-site/#touch-lists&quot;&gt;Touch lists&lt;/a&gt;).&lt;/p&gt;
&lt;h4 id=&quot;use-requestanimationframe&quot;&gt;Use requestAnimationFrame &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-touch-to-your-site/#use-requestanimationframe&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Since the event callbacks are fired on the main thread, we want to run as
little code as possible in the callbacks for our events, keeping our frame
rate high and preventing jank.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;requestAnimationFrame()&lt;/code&gt; we have an opportunity to update the UI just
before the browser is intending to draw a frame and will help us move some
work out of our event callbacks.&lt;/p&gt;
&lt;p&gt;If you are unfamiliar with &lt;code&gt;requestAnimationFrame()&lt;/code&gt;, you
can &lt;a href=&quot;https://web.dev/optimize-javascript-execution/#use-requestanimationframe-for-visual-changes&quot;&gt;learn more here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A typical implementation is to save the &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; coordinates from the
start and move events and request an animation frame inside the move event
callback.&lt;/p&gt;
&lt;p&gt;In our demo, we store the initial touch position in &lt;code&gt;handleGestureStart()&lt;/code&gt; (look for &lt;code&gt;initialTouchPos&lt;/code&gt;):&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Handle the start of gestures&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;handleGestureStart&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;evt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;touches &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;touches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token 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 punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Add the move and end listeners&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PointerEvent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setPointerCapture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pointerId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Add Mouse Listeners&lt;/span&gt;&lt;br /&gt;    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;mousemove&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handleGestureMove&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;mouseup&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handleGestureEnd&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  initialTouchPos &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getGesturePointFromEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;evt&lt;span 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;  swipeFrontElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;transition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;initial&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The &lt;code&gt;handleGestureMove()&lt;/code&gt; method stores the position of it&#39;s event
before requesting an animation frame if we need to, passing in our
&lt;code&gt;onAnimFrame()&lt;/code&gt; function as the callback:&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;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;handleGestureMove&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;evt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &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;initialTouchPos&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  lastTouchPos &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getGesturePointFromEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;evt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rafPending&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  rafPending &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;requestAnimFrame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;onAnimFrame&lt;span class=&quot;token punctuation&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 function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;The &lt;code&gt;onAnimFrame&lt;/code&gt; value is a function that when called, changes our UI
to move it around. By passing this function into &lt;code&gt;requestAnimationFrame()&lt;/code&gt;, we
tell the browser to call it just before it&#39;s about to update the page
(i.e. paint any changes to the page).&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;handleGestureMove()&lt;/code&gt; callback we initially check if &lt;code&gt;rafPending&lt;/code&gt; is false,
which indicates if &lt;code&gt;onAnimFrame()&lt;/code&gt; has been called by &lt;code&gt;requestAnimationFrame()&lt;/code&gt;
since the last move event. This means we only have one &lt;code&gt;requestAnimationFrame()&lt;/code&gt;
waiting to run at any one time.&lt;/p&gt;
&lt;p&gt;When our &lt;code&gt;onAnimFrame()&lt;/code&gt; callback is executed, we set the transform on any
elements we want to move before updating &lt;code&gt;rafPending&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt;, allowing the
the next touch event to request a new animation frame.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onAnimFrame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&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;rafPending&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; differenceInX &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; initialTouchPos&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; lastTouchPos&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; newXTransform &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentXPosition &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; differenceInX&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;px&#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;var&lt;/span&gt; transformStyle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;translateX(&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;newXTransform&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;)&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  swipeFrontElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;webkitTransform &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; transformStyle&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  swipeFrontElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;MozTransform &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; transformStyle&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  swipeFrontElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;msTransform &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; transformStyle&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  swipeFrontElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;transform &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; transformStyle&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  rafPending &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;control-gestures-using-touch-actions&quot;&gt;Control gestures using touch actions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-touch-to-your-site/#control-gestures-using-touch-actions&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The CSS property &lt;code&gt;touch-action&lt;/code&gt; allows you to control the default touch
behavior of an element. In our examples, we use &lt;code&gt;touch-action: none&lt;/code&gt; to
prevent the browser from doing anything with a users&#39; touch, allowing us
to intercept all of the touch events.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* Pass all touches to javascript: */&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;button.custom-touch-logic&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;touch-action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Using &lt;code&gt;touch-action: none&lt;/code&gt; is somewhat a nuclear option as it prevents all
the default browser behaviors. In many cases one of the options
below is a better solution.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;touch-action&lt;/code&gt; allows you to disable gestures implemented by a browser.
For example, IE10+ supports a double-tap to zoom gesture. By setting a
&lt;code&gt;touch-action&lt;/code&gt; of &lt;code&gt;manipulation&lt;/code&gt; you prevent the default double-tap
behavior.&lt;/p&gt;
&lt;p&gt;This allows you to implement a double-tap gesture yourself.&lt;/p&gt;
&lt;p&gt;Below is a list of commonly used &lt;code&gt;touch-action&lt;/code&gt; values:&lt;/p&gt;
&lt;div class=&quot;table-wrapper scrollbar&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th colspan=&quot;2&quot;&gt;Touch Action Parameters&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Property&quot;&gt;&lt;code&gt;touch-action: none&lt;/code&gt;&lt;/td&gt;
      &lt;td data-th=&quot;Description&quot;&gt;No touch interactions will be handled by
      the browser.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Property&quot;&gt;&lt;code&gt;touch-action: pinch-zoom&lt;/code&gt;&lt;/td&gt;
      &lt;td data-th=&quot;Description&quot;&gt;Disables all browser interactions like
      `touch-action: none` apart from `pinch-zoom`, which is still handled by
      the browser.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Property&quot;&gt;&lt;code&gt;touch-action: pan-y pinch-zoom&lt;/code&gt;&lt;/td&gt;
      &lt;td data-th=&quot;Description&quot;&gt;Handle horizontal scrolls in JavaScript without
      disabling vertical scrolling or pinch-zooming (eg. image carousels).&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Property&quot;&gt;&lt;code&gt;touch-action: manipulation&lt;/code&gt;&lt;/td&gt;
      &lt;td data-th=&quot;Description&quot;&gt;Disables double-tap gesture which avoids any
      click delay by the browser. Leaves scrolling and pinch-zoom up to the
      browser.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h2 id=&quot;supporting-older-versions-of-ie&quot;&gt;Supporting older versions of IE &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-touch-to-your-site/#supporting-older-versions-of-ie&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you want to support IE10, you&#39;ll need to handle vendor prefixed versions of
&lt;code&gt;PointerEvents&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To check for support of &lt;code&gt;PointerEvents&lt;/code&gt; you&#39;d typically look for
&lt;code&gt;window.PointerEvent&lt;/code&gt;, but in IE10, you&#39;d look for
&lt;code&gt;window.navigator.msPointerEnabled&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The event names with vendor prefixes are: &lt;code&gt;&#39;MSPointerDown&#39;&lt;/code&gt;, &lt;code&gt;&#39;MSPointerUp&#39;&lt;/code&gt; and
&lt;code&gt;&#39;MSPointerMove&#39;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The example below shows you how to check for support and switch
the event names.&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;var&lt;/span&gt; pointerDownName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;pointerdown&#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;var&lt;/span&gt; pointerUpName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;pointerup&#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;var&lt;/span&gt; pointerMoveName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;pointermove&#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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;msPointerEnabled&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  pointerDownName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;MSPointerDown&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  pointerUpName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;MSPointerUp&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  pointerMoveName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;MSPointerMove&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Simple way to check if some form of pointerevents is enabled or not&lt;/span&gt;&lt;br /&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PointerEventsSupport &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PointerEvent &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;msPointerEnabled&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PointerEventsSupport &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;For more information, checkout this &lt;a href=&quot;https://msdn.microsoft.com/library/dn304886(v=vs.85).aspx&quot; rel=&quot;noopener&quot;&gt;updates article from
Microsoft&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;reference&quot;&gt;Reference &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-touch-to-your-site/#reference&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;pseudo-classes-for-touch-states&quot;&gt;Pseudo classes for touch states &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-touch-to-your-site/#pseudo-classes-for-touch-states&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;table-wrapper scrollbar&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Class&lt;/th&gt;
      &lt;th&gt;Example&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Class&quot;&gt;:hover&lt;/td&gt;
      &lt;td data-th=&quot;Example&quot;&gt;&lt;figure&gt;&lt;img alt=&quot;Button in Pressed State&quot; decoding=&quot;async&quot; height=&quot;40&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 70px) 70px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XVGNwzMZeMZ6YfjsaYlP.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XVGNwzMZeMZ6YfjsaYlP.png?auto=format&amp;w=70 70w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XVGNwzMZeMZ6YfjsaYlP.png?auto=format&amp;w=80 80w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XVGNwzMZeMZ6YfjsaYlP.png?auto=format&amp;w=91 91w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XVGNwzMZeMZ6YfjsaYlP.png?auto=format&amp;w=104 104w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XVGNwzMZeMZ6YfjsaYlP.png?auto=format&amp;w=118 118w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XVGNwzMZeMZ6YfjsaYlP.png?auto=format&amp;w=135 135w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XVGNwzMZeMZ6YfjsaYlP.png?auto=format&amp;w=140 140w&quot; width=&quot;70&quot; /&gt;&lt;/figure&gt;&lt;/td&gt;
      &lt;td data-th=&quot;Description&quot;&gt;
        Entered when a cursor is placed over an element.
        Changes in the UI on hover are helpful to encourage users to interact
        with elements.
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Class&quot;&gt;:focus&lt;/td&gt;
      &lt;td data-th=&quot;Example&quot;&gt;
        &lt;figure&gt;
        &lt;img alt=&quot;Button with Focus State&quot; decoding=&quot;async&quot; height=&quot;40&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 70px) 70px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a4xf5cg5aB3VzXcnFmZW.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a4xf5cg5aB3VzXcnFmZW.png?auto=format&amp;w=70 70w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a4xf5cg5aB3VzXcnFmZW.png?auto=format&amp;w=80 80w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a4xf5cg5aB3VzXcnFmZW.png?auto=format&amp;w=91 91w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a4xf5cg5aB3VzXcnFmZW.png?auto=format&amp;w=104 104w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a4xf5cg5aB3VzXcnFmZW.png?auto=format&amp;w=118 118w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a4xf5cg5aB3VzXcnFmZW.png?auto=format&amp;w=135 135w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/a4xf5cg5aB3VzXcnFmZW.png?auto=format&amp;w=140 140w&quot; width=&quot;70&quot; /&gt;
        &lt;/figure&gt;
      &lt;/td&gt;
      &lt;td data-th=&quot;Description&quot;&gt;
        Entered when the user tabs through elements on a page. The focus state
        allows the user to know what element they are currently interacting
        with; also allows users to navigate your UI easily using a keyboard.
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Class&quot;&gt;:active&lt;/td&gt;
      &lt;td data-th=&quot;Example&quot;&gt;
        &lt;figure&gt;
        &lt;img alt=&quot;Button in Pressed State&quot; decoding=&quot;async&quot; height=&quot;40&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 70px) 70px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bjKXowmUU9rnewiaK2g5.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bjKXowmUU9rnewiaK2g5.png?auto=format&amp;w=70 70w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bjKXowmUU9rnewiaK2g5.png?auto=format&amp;w=80 80w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bjKXowmUU9rnewiaK2g5.png?auto=format&amp;w=91 91w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bjKXowmUU9rnewiaK2g5.png?auto=format&amp;w=104 104w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bjKXowmUU9rnewiaK2g5.png?auto=format&amp;w=118 118w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bjKXowmUU9rnewiaK2g5.png?auto=format&amp;w=135 135w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/bjKXowmUU9rnewiaK2g5.png?auto=format&amp;w=140 140w&quot; width=&quot;70&quot; /&gt;
        &lt;/figure&gt;
      &lt;/td&gt;
      &lt;td data-th=&quot;Description&quot;&gt;
        Entered when an element is being selected, for
        example, when a user is clicking or touching an element.
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;The definitive touch events reference can be found here:
&lt;a href=&quot;http://www.w3.org/TR/touch-events/&quot; rel=&quot;noopener&quot;&gt;W3C Touch Events&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;touch,-mouse,-and-pointer-events&quot;&gt;Touch, mouse, and pointer events &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-touch-to-your-site/#touch,-mouse,-and-pointer-events&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;These events are the building blocks for adding new gestures into your
application:&lt;/p&gt;
&lt;div class=&quot;table-wrapper scrollbar&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th colspan=&quot;2&quot;&gt;Touch, Mouse, Pointer Events&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Event Names&quot;&gt;
        &lt;code&gt;touchstart&lt;/code&gt;,
        &lt;code&gt;mousedown&lt;/code&gt;,
        &lt;code&gt;pointerdown&lt;/code&gt;
      &lt;/td&gt;
      &lt;td data-th=&quot;Description&quot;&gt;
        This is called when a finger first touches an element or when the
        user clicks down on the mouse.
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Event Names&quot;&gt;
        &lt;code&gt;touchmove&lt;/code&gt;,
        &lt;code&gt;mousemove&lt;/code&gt;,
        &lt;code&gt;pointermove&lt;/code&gt;
      &lt;/td&gt;
      &lt;td data-th=&quot;Description&quot;&gt;
        This is called when the user moves their finger across the screen or
        drags with the mouse.
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Event Names&quot;&gt;
        &lt;code&gt;touchend&lt;/code&gt;,
        &lt;code&gt;mouseup&lt;/code&gt;,
        &lt;code&gt;pointerup&lt;/code&gt;
      &lt;/td&gt;
      &lt;td data-th=&quot;Description&quot;&gt;
        This is called when the user lifts their finger off of the screen
        or releases the mouse.
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Event Names&quot;&gt;
        &lt;code&gt;touchcancel&lt;/code&gt;
        &lt;code&gt;pointercancel&lt;/code&gt;
      &lt;/td&gt;
      &lt;td data-th=&quot;Description&quot;&gt;
        This is called when the browser cancels the touch gestures. For example,
        a user touch a web app and then change tabs.
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h3 id=&quot;touch-lists&quot;&gt;Touch lists &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-touch-to-your-site/#touch-lists&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Each touch event includes three list attributes:&lt;/p&gt;
&lt;div class=&quot;table-wrapper scrollbar&quot;&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th colspan=&quot;2&quot;&gt;Touch Event Attributes&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Attribute&quot;&gt;&lt;code&gt;touches&lt;/code&gt;&lt;/td&gt;
      &lt;td data-th=&quot;Description&quot;&gt;
        List of all current touches on the screen, regardless of elements
        being touched.
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Attribute&quot;&gt;&lt;code&gt;targetTouches&lt;/code&gt;&lt;/td&gt;
      &lt;td data-th=&quot;Description&quot;&gt;
        List of touches that started on the element that is the target of
        the current event. For example, if you bind to a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;,
        you&#39;ll only get touches currently on that button. If you bind to the
        document, you&#39;ll get all touches currently on the document.
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Attribute&quot;&gt;&lt;code&gt;changedTouches&lt;/code&gt;&lt;/td&gt;
      &lt;td data-th=&quot;Description&quot;&gt;
        List of touches which changed resulting in the event being fired:
        &lt;ul&gt;
          &lt;li&gt;
            For the &lt;code&gt;
            &lt;a href=&quot;http://www.w3.org/TR/touch-events/#dfn-touchstart&quot;&gt;
            touchstart&lt;/a&gt;&lt;/code&gt;
            event-- list of the touch points that just became active with the
            current event.
          &lt;/li&gt;
          &lt;li&gt;
            For the &lt;code&gt;
            &lt;a href=&quot;http://www.w3.org/TR/touch-events/#dfn-touchmove&quot;&gt;
            touchmove&lt;/a&gt;&lt;/code&gt;
            event-- list of the touch points that have moved since the last
            event.
          &lt;/li&gt;
          &lt;li&gt;
            For the &lt;code&gt;
            &lt;a href=&quot;http://www.w3.org/TR/touch-events/#dfn-touchend&quot;&gt;
            touchend&lt;/a&gt;&lt;/code&gt;
            and &lt;code&gt;
            &lt;a href=&quot;http://www.w3.org/TR/touch-events/#dfn-touchcancel&quot;&gt;
            touchcancel&lt;/a&gt;&lt;/code&gt;
            events-- list of the touch points that have just been removed
            from the surface.
          &lt;/li&gt;
        &lt;/ul&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h3 id=&quot;enabling-active-state-support-on-ios&quot;&gt;Enabling active state support on iOS &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-touch-to-your-site/#enabling-active-state-support-on-ios&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Unfortunately, Safari on iOS does not apply the &lt;em&gt;active&lt;/em&gt; state by default, to
get it working you need to add a &lt;code&gt;touchstart&lt;/code&gt; event listener to the &lt;em&gt;document
body&lt;/em&gt; or to each element.&lt;/p&gt;
&lt;p&gt;You should do this behind a user agent test so it&#39;s only run on iOS devices.&lt;/p&gt;
&lt;p&gt;Adding a touch start to the body has the advantage of applying to all elements
in the DOM, however this may have performance issues when scrolling the page.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onload&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;iP&lt;span class=&quot;token group punctuation&quot;&gt;(&lt;/span&gt;hone&lt;span class=&quot;token alternation keyword&quot;&gt;|&lt;/span&gt;ad&lt;span class=&quot;token group punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userAgent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;touchstart&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token 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;The alternative is to add the touch start listeners to all the interactable
elements in the page, alleviating some of the performance concerns.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onload&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;iP&lt;span class=&quot;token group punctuation&quot;&gt;(&lt;/span&gt;hone&lt;span class=&quot;token alternation keyword&quot;&gt;|&lt;/span&gt;ad&lt;span class=&quot;token group punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userAgent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; elements &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;button&#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;var&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;emptyFunction&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; elements&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        elements&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;touchstart&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; emptyFunction&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token 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;</content>
    <author>
      <name>Matt Gaunt</name>
    </author>
  </entry>
</feed>
