{"id":326606,"date":"2021-08-17T15:29:30","date_gmt":"2021-08-17T12:29:30","guid":{"rendered":"https:\/\/en.buradabiliyorum.com\/how-to-handle-web-push-notifications-in-websites-and-pwas-cloudsavvy-it\/"},"modified":"2021-08-17T15:29:30","modified_gmt":"2021-08-17T12:29:30","slug":"how-to-handle-web-push-notifications-in-websites-and-pwas-cloudsavvy-it","status":"publish","type":"post","link":"https:\/\/buradabiliyorum.com\/en\/how-to-handle-web-push-notifications-in-websites-and-pwas-cloudsavvy-it\/","title":{"rendered":"#How to Handle Web Push Notifications in Websites and PWAs \u2013 CloudSavvy IT"},"content":{"rendered":"<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_84 counter-hierarchy ez-toc-counter ez-toc-custom ez-toc-container-direction\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Table of Contents<\/p>\n<label for=\"ez-toc-cssicon-toggle-item-6a2920746d112\" class=\"ez-toc-cssicon-toggle-label\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #dd3333;color:#dd3333\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #dd3333;color:#dd3333\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/label><input type=\"checkbox\"  id=\"ez-toc-cssicon-toggle-item-6a2920746d112\" checked aria-label=\"Toggle\" \/><nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/buradabiliyorum.com\/en\/how-to-handle-web-push-notifications-in-websites-and-pwas-cloudsavvy-it\/#The_Service_Worker\" >The Service Worker<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/buradabiliyorum.com\/en\/how-to-handle-web-push-notifications-in-websites-and-pwas-cloudsavvy-it\/#Registering_for_Push_Subscriptions\" >Registering for Push Subscriptions<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/buradabiliyorum.com\/en\/how-to-handle-web-push-notifications-in-websites-and-pwas-cloudsavvy-it\/#When_to_Subscribe_the_User\" >When to Subscribe the User?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/buradabiliyorum.com\/en\/how-to-handle-web-push-notifications-in-websites-and-pwas-cloudsavvy-it\/#Handling_Expirations_and_Renewals\" >Handling Expirations and Renewals<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/buradabiliyorum.com\/en\/how-to-handle-web-push-notifications-in-websites-and-pwas-cloudsavvy-it\/#Adding_Action_Buttons\" >Adding Action Buttons<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/buradabiliyorum.com\/en\/how-to-handle-web-push-notifications-in-websites-and-pwas-cloudsavvy-it\/#Summary\" >Summary<\/a><\/li><\/ul><\/nav><\/div>\n<p><strong>&#8220;#How to Handle Web Push Notifications in Websites and PWAs \u2013 CloudSavvy IT&#8221;<\/strong><\/p>\n<div id=\"article-content-area\">\n<figure style=\"width: 1200px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"type:primaryImage wp-image-13819 size-full\" srcset=\"https:\/\/www.cloudsavvyit.com\/p\/uploads\/2021\/08\/3faeb368.png?width=398&amp;trim=1,1&amp;bg-color=000&amp;pad=1,1 400w, https:\/\/www.cloudsavvyit.com\/p\/uploads\/2021\/08\/3faeb368.png?width=1198&amp;trim=1,1&amp;bg-color=000&amp;pad=1,1 1200w\" sizes=\"auto, 400w, 1200w\" src=\"https:\/\/www.cloudsavvyit.com\/p\/uploads\/2021\/08\/3faeb368.png?width=1198&amp;trim=1,1&amp;bg-color=000&amp;pad=1,1\" alt=\"Photo of a smartphone with notification icons appearing to come out of it\" width=\"1200\" height=\"675\" data-crediturl=\"https:\/\/www.shutterstock.com\/image-photo\/person-using-social-media-marketing-concept-1728918238\" data-credittext=\"RoBird\/Shutterstock.com\" onload=\"pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);\" onerror=\"this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);\"\/><figcaption class=\"wp-caption-text\"><span class=\"type:primaryImage imagecredit\"><a rel=\"nofollow noopener\" target=\"_blank\" href=\"https:\/\/www.shutterstock.com\/image-photo\/person-using-social-media-marketing-concept-1728918238\">RoBird\/Shutterstock.com<\/a><\/span><\/figcaption><\/figure>\n<p>Push notifications are a common sight on the modern web. They let you communicate timely information to the user, even if your site\u2019s not actually open. The user\u2019s browser handles incoming push events and displays notifications using system UI surfaces like the Windows Action Center and Android lockscreen.<\/p>\n<p>Implementing Web Push into your site or PWA requires the combination of two distinct browser APIs. The code responsible for subscribing to and receiving notifications uses the <a rel=\"nofollow noopener\" target=\"_blank\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Push_API\">Push API<\/a> component of <a rel=\"nofollow noopener\" target=\"_blank\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Service_Worker_API\">service workers<\/a>. This code runs continually in the background and will be invoked by the browser when a new notification needs to be handled.<\/p>\n<p>When an event\u2019s received, the service worker should use the <a rel=\"nofollow noopener\" target=\"_blank\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/notification\">Notification API<\/a> to actually display the notification. This creates a visual alert via OS-level interfaces.<\/p>\n<p>Here\u2019s a complete guide to getting Web Push working on your site. We\u2019ll assume you\u2019ve already got a server-side component that can register push sub<a href=\"https:\/\/buradabiliyorum.com\/en\/category\/download-scripts-themes-apps\/\" data-internallinksmanager029f6b8e52c=\"9\" title=\"Download Scripts &amp; Themes &amp; Apps\" target=\"_blank\" rel=\"noopener\">script<\/a>ions and send out your alerts.<\/p>\n<h2 id=\"the-service-worker\"><span class=\"ez-toc-section\" id=\"The_Service_Worker\"><\/span>The Service Worker<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Let\u2019s start with the service worker. Service workers have multiple roles \u2013 they can cache data for offline use, run periodic background syncs, and act as notification handlers. Service workers use an event-driven architecture. Once registered by a site, the user\u2019s browser will invoke the service worker in the background when events it subscribes to are generated.<\/p>\n<p>For Web Push, one core event is needed: <code>push<\/code>. This receives a <a rel=\"nofollow noopener\" target=\"_blank\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/PushEvent\"><code>PushEvent<\/code> object<\/a> that lets you access the payload pushed from the server.<\/p>\n<div class=\"wp-geshi-highlight-wrap5\">\n<div class=\"wp-geshi-highlight-wrap4\">\n<div class=\"wp-geshi-highlight-wrap3\">\n<div class=\"wp-geshi-highlight-wrap2\">\n<div class=\"wp-geshi-highlight-wrap\">\n<div class=\"wp-geshi-highlight\">\n<div class=\"javascript\">\n<pre class=\"de1\">self.<span class=\"me1\">addEventListener<\/span><span class=\"br0\">(<\/span><span class=\"st0\">\"push\"<\/span><span class=\"sy0\">,<\/span> e <span class=\"sy0\">=&gt;<\/span> <span class=\"br0\">{<\/span>\n\u00a0\n    <span class=\"kw1\">const<\/span> payload <span class=\"sy0\">=<\/span> JSON.<span class=\"me1\">parse<\/span><span class=\"br0\">(<\/span>e.<span class=\"me1\">data<\/span>.<span class=\"me1\">text<\/span><span class=\"br0\">(<\/span><span class=\"br0\">)<\/span><span class=\"br0\">)<\/span><span class=\"sy0\">;<\/span>\n\u00a0\n    e.<span class=\"me1\">waitUntil<\/span><span class=\"br0\">(<\/span>self.<span class=\"me1\">registration<\/span>.<span class=\"me1\">showNotification<\/span><span class=\"br0\">(<\/span>\n        payload.<span class=\"me1\">title<\/span><span class=\"sy0\">,<\/span>\n        <span class=\"br0\">{<\/span>\n            body<span class=\"sy0\">:<\/span> payload.<span class=\"me1\">body<\/span><span class=\"sy0\">,<\/span>\n            icon<span class=\"sy0\">:<\/span> <span class=\"st0\">\"\/icon.png\"<\/span>\n        <span class=\"br0\">}<\/span>\n    <span class=\"br0\">)<\/span><span class=\"br0\">)<\/span><span class=\"sy0\">;<\/span>\n\u00a0\n<span class=\"br0\">}<\/span><span class=\"br0\">)<\/span><span class=\"sy0\">;<\/span><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>The code above sets up a service worker capable of reacting to incoming push events. It expects the server to send JSON payloads looking like this:<\/p>\n<div class=\"wp-geshi-highlight-wrap5\">\n<div class=\"wp-geshi-highlight-wrap4\">\n<div class=\"wp-geshi-highlight-wrap3\">\n<div class=\"wp-geshi-highlight-wrap2\">\n<div class=\"wp-geshi-highlight-wrap\">\n<div class=\"wp-geshi-highlight\">\n<div class=\"json\">\n<pre class=\"de1\">{&#13;\n    \"title\": \"Title text for the notification\",&#13;\n    \"body\": \"This is the longer text of the notification.\"&#13;\n}<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>When a push event is received, the service worker displays a browser notification by calling the <code>showNotification()<\/code> function available on its <code>self.registration<\/code> property. The function\u2019s wrapped in a <code>waitUntil()<\/code> call so the browser waits for the notification to be displayed before terminating the service worker.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-13826\" src=\"https:\/\/www.cloudsavvyit.com\/p\/uploads\/2021\/08\/fd0d87fc.png?trim=1,1&amp;bg-color=000&amp;pad=1,1\" alt=\"\" width=\"440\" height=\"222\" onload=\"pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);\" onerror=\"this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);\"\/><\/p>\n<p>The <code>showNotification()<\/code> function takes two arguments: the notification\u2019s title text and an options object. Two options are passed in this example, some longer body text and an icon to display in the notification. Many <a rel=\"nofollow noopener\" target=\"_blank\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/ServiceWorkerRegistration\/showNotification\">other options<\/a> are available that let you setup vibration patterns, custom badges, and interaction requirements. Not all browsers and operating systems support all the capabilities exposed by the API.<\/p>\n<p>Complete the service worker side of the code by registering it back in your main JavaScript:<\/p>\n<div class=\"wp-geshi-highlight-wrap5\">\n<div class=\"wp-geshi-highlight-wrap4\">\n<div class=\"wp-geshi-highlight-wrap3\">\n<div class=\"wp-geshi-highlight-wrap2\">\n<div class=\"wp-geshi-highlight-wrap\">\n<div class=\"wp-geshi-highlight\">\n<div class=\"javascript\">\n<pre class=\"de1\"><span class=\"kw1\">if<\/span> <span class=\"br0\">(<\/span>navigator.<span class=\"me1\">serviceWorker<\/span><span class=\"br0\">)<\/span> <span class=\"br0\">{<\/span>\n    <span class=\"co1\">\/\/ replace with the path to your service worker file<\/span>\n    navigator.<span class=\"me1\">serviceWorker<\/span>.<span class=\"me1\">register<\/span><span class=\"br0\">(<\/span><span class=\"st0\">\"\/sw.js\"<\/span><span class=\"br0\">)<\/span>.<span class=\"kw1\">catch<\/span><span class=\"br0\">(<\/span><span class=\"br0\">(<\/span><span class=\"br0\">)<\/span> <span class=\"sy0\">=&gt;<\/span> <span class=\"br0\">{<\/span>\n        console.<span class=\"me1\">error<\/span><span class=\"br0\">(<\/span><span class=\"st0\">\"Couldn't register the service worker.\"<\/span><span class=\"br0\">)<\/span>\n    <span class=\"br0\">}<\/span><span class=\"br0\">)<\/span><span class=\"sy0\">;<\/span>\n<span class=\"br0\">}<\/span><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>This code should run on each page load. It makes sure the browser supports service workers and then registers the worker file. Browsers will automatically update the service worker whenever the server copy exhibits byte differences to the currently installed version.<\/p>\n<h2 id=\"registering-for-push-subscriptions\"><span class=\"ez-toc-section\" id=\"Registering_for_Push_Subscriptions\"><\/span>Registering for Push Subscriptions<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Now you need to subscribe the browser to push notifications. The following code belongs in your main JavaScript file, outside the service worker.<\/p>\n<div class=\"wp-geshi-highlight-wrap5\">\n<div class=\"wp-geshi-highlight-wrap4\">\n<div class=\"wp-geshi-highlight-wrap3\">\n<div class=\"wp-geshi-highlight-wrap2\">\n<div class=\"wp-geshi-highlight-wrap\">\n<div class=\"wp-geshi-highlight\">\n<div class=\"javascript\">\n<pre class=\"de1\">async <span class=\"kw1\">function<\/span> subscribeToPush<span class=\"br0\">(<\/span><span class=\"br0\">)<\/span> <span class=\"br0\">{<\/span>\n    <span class=\"kw1\">if<\/span> <span class=\"br0\">(<\/span>navigator.<span class=\"me1\">serviceWorker<\/span><span class=\"br0\">)<\/span> <span class=\"br0\">{<\/span>\n\u00a0\n        <span class=\"kw1\">const<\/span> reg <span class=\"sy0\">=<\/span> await navigator.<span class=\"me1\">serviceWorker<\/span>.<span class=\"me1\">getRegistration<\/span><span class=\"br0\">(<\/span><span class=\"br0\">)<\/span><span class=\"sy0\">;<\/span>\n\u00a0\n        <span class=\"kw1\">if<\/span> <span class=\"br0\">(<\/span>reg <span class=\"sy0\">&amp;&amp;<\/span> reg.<span class=\"me1\">pushManager<\/span><span class=\"br0\">)<\/span> <span class=\"br0\">{<\/span>\n\u00a0\n            <span class=\"kw1\">const<\/span> subscription <span class=\"sy0\">=<\/span> await reg.<span class=\"me1\">pushManager<\/span>.<span class=\"me1\">getSubscription<\/span><span class=\"br0\">(<\/span><span class=\"br0\">)<\/span><span class=\"sy0\">;<\/span>\n\u00a0\n            <span class=\"kw1\">if<\/span> <span class=\"br0\">(<\/span><span class=\"sy0\">!<\/span>subscription<span class=\"br0\">)<\/span> <span class=\"br0\">{<\/span>\n\u00a0\n                <span class=\"kw1\">const<\/span> key <span class=\"sy0\">=<\/span> await fetch<span class=\"br0\">(<\/span><span class=\"st0\">\"https:\/\/example.com\/vapid_key\"<\/span><span class=\"br0\">)<\/span><span class=\"sy0\">;<\/span>\n                <span class=\"kw1\">const<\/span> keyData <span class=\"sy0\">=<\/span> await key.<span class=\"me1\">text<\/span><span class=\"br0\">(<\/span><span class=\"br0\">)<\/span><span class=\"sy0\">;<\/span>\n\u00a0\n                <span class=\"kw1\">const<\/span> sub <span class=\"sy0\">=<\/span> await reg.<span class=\"me1\">pushManager<\/span>.<span class=\"me1\">subscribe<\/span><span class=\"br0\">(<\/span><span class=\"br0\">{<\/span>\n                    applicationServerKey<span class=\"sy0\">:<\/span> keyData<span class=\"sy0\">,<\/span>\n                    userVisibleOnly<span class=\"sy0\">:<\/span> <span class=\"kw2\">true<\/span>\n                <span class=\"br0\">}<\/span><span class=\"br0\">)<\/span><span class=\"sy0\">;<\/span>\n\u00a0\n                await fetch<span class=\"br0\">(<\/span><span class=\"st0\">\"https:\/\/example.com\/push_subscribe\"<\/span><span class=\"sy0\">,<\/span> <span class=\"br0\">{<\/span>\n                    method<span class=\"sy0\">:<\/span> <span class=\"st0\">\"POST\"<\/span><span class=\"sy0\">,<\/span>\n                    headers<span class=\"sy0\">:<\/span> <span class=\"br0\">{<\/span><span class=\"st0\">\"Content-Type\"<\/span><span class=\"sy0\">:<\/span> <span class=\"st0\">\"application\/json\"<\/span><span class=\"br0\">}<\/span><span class=\"sy0\">,<\/span>\n                    body<span class=\"sy0\">:<\/span> JSON.<span class=\"me1\">stringify<\/span><span class=\"br0\">(<\/span><span class=\"br0\">{<\/span>\n                        endpoint<span class=\"sy0\">:<\/span> sub.<span class=\"me1\">endpoint<\/span><span class=\"sy0\">,<\/span>\n                        expirationTime<span class=\"sy0\">:<\/span> sub.<span class=\"me1\">expirationTime<\/span><span class=\"sy0\">,<\/span>\n                        keys<span class=\"sy0\">:<\/span> sub.<span class=\"me1\">toJSON<\/span><span class=\"br0\">(<\/span><span class=\"br0\">)<\/span>.<span class=\"me1\">keys<\/span>\n                    <span class=\"br0\">}<\/span><span class=\"br0\">)<\/span>\n                <span class=\"br0\">}<\/span><span class=\"br0\">)<\/span><span class=\"sy0\">;<\/span>\n\u00a0\n            <span class=\"br0\">}<\/span>\n\u00a0\n        <span class=\"br0\">}<\/span>\n\u00a0\n    <span class=\"br0\">}<\/span>\n<span class=\"br0\">}<\/span><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Then call your function to subscribe the browser to push notifications:<\/p>\n<p>Let\u2019s walk through what the subscription code is doing. The first few lines check for the presence of a service worker, retrieve its registration, and detect push notification support. <code>pushManager<\/code> won\u2019t be set in browsers which don\u2019t support Web Push.<\/p>\n<p>Calling <code>pushManager.getSubscription()<\/code> returns a promise that resolves to an object describing the browser\u2019s current push subscription for your site. If this is already set, we don\u2019t need to resubscribe the user.<\/p>\n<p>The real subscription flow begins with the fetch request for the server\u2019s VAPID keys. The VAPID specification <a rel=\"nofollow noopener\" target=\"_blank\" href=\"https:\/\/developers.google.com\/web\/fundamentals\/push-notifications\/web-push-protocol\">is a mechanism which<\/a> lets the browser verify push events are actually coming from your server. You should expose a server API endpoint that provides a VAPID key. This is given to the <code>pushManager.subscribe()<\/code> function so the browser knows the key to trust. The separate <code>userVisibleOnly<\/code> option indicates we\u2019ll only display notifications that visibly display on the screen.<\/p>\n<p>The <code>pushManager.subscribe()<\/code> call returns a <a rel=\"nofollow noopener\" target=\"_blank\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/PushSubscription\"><code>PushSubscription<\/code> object<\/a> describing your new subscription. This data is sent to the server in another fetch request. In a real app, you\u2019d also send the active user\u2019s ID so you could link the push subscription to their device.<\/p>\n<p>Your server-side code for sending a push notification to a user should look something like this:<\/p>\n<ol type=\"1\">\n<li>Query your data store for all push subscriptions linked to the target user.<\/li>\n<li>Send your notification payload to the endpoint indicated by each subscription, making sure to include the subscription\u2019s authentication keys (<code>keys<\/code> in the data sent by the browser when subscribing). Sign the event with the same VAPID key you sent to the browser.<\/li>\n<\/ol>\n<p>Each subscription\u2019s <code>endpoint<\/code> will reference the browser vendor\u2019s notification delivery platform. This URL already includes a unique identifier for the subscription. When you send a payload to the endpoint, the browser\u2019s background process will eventually receive the data and invoke your service worker. For Chrome on Android, the browser process is directly integrated with the system notification daemon.<\/p>\n<h2 id=\"when-to-subscribe-the-user\"><span class=\"ez-toc-section\" id=\"When_to_Subscribe_the_User\"><\/span>When to Subscribe the User?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>When setting up subscription flows, remember the user will have to acknowledge a browser permission prompt before registration completes. Many browsers <a rel=\"nofollow noopener\" target=\"_blank\" href=\"https:\/\/blog.mozilla.org\/en\/products\/firefox\/block-notification-requests\/\">automatically hide or<\/a> reject unsolicited permission requests; in any case, asking a user to subscribe the moment they land on your site may not deliver the result you want.<\/p>\n<p>You get the best chance of a successful sign-up by coupling subscription requests to a direct user action. Consider providing an in-app banner that explains the benefits of enabling notifications and offers an \u201cEnable Now\u201d button. You can check whether the user\u2019s already subscribed and hide the banner with the <code>pushManager.getSubscription()<\/code> function shown above.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-13823\" src=\"https:\/\/www.cloudsavvyit.com\/p\/uploads\/2021\/08\/edaac355.png?trim=1,1&amp;bg-color=000&amp;pad=1,1\" alt=\"\" width=\"976\" height=\"164\" onload=\"pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);\" onerror=\"this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);\"\/><\/p>\n<p>Clicking the enable button should call your subscription function. The process could take a few seconds while the browser sets up the registration and your network calls complete. Displaying a loading spinner during this time will help keep the user informed.<\/p>\n<p>Users should also be given a way to unsubscribe. Although they can revoke the browser permission at any time, some users will look for an in-app option, especially if they\u2019ve installed your site as a PWA.<\/p>\n<p>Here\u2019s a simple unsubscribe implementation:<\/p>\n<div class=\"wp-geshi-highlight-wrap5\">\n<div class=\"wp-geshi-highlight-wrap4\">\n<div class=\"wp-geshi-highlight-wrap3\">\n<div class=\"wp-geshi-highlight-wrap2\">\n<div class=\"wp-geshi-highlight-wrap\">\n<div class=\"wp-geshi-highlight\">\n<div class=\"javascript\">\n<pre class=\"de1\">async <span class=\"kw1\">function<\/span> unsubscribePush<span class=\"br0\">(<\/span><span class=\"br0\">)<\/span> <span class=\"br0\">{<\/span>\n\u00a0\n    <span class=\"kw1\">const<\/span> reg <span class=\"sy0\">=<\/span> await navigator.<span class=\"me1\">serviceWorker<\/span>.<span class=\"me1\">getRegistration<\/span><span class=\"br0\">(<\/span><span class=\"br0\">)<\/span><span class=\"sy0\">;<\/span>\n    <span class=\"kw1\">const<\/span> subscription <span class=\"sy0\">=<\/span> await reg.<span class=\"me1\">pushManager<\/span>.<span class=\"me1\">getSubscription<\/span><span class=\"br0\">(<\/span><span class=\"br0\">)<\/span><span class=\"sy0\">;<\/span>\n\u00a0\n    <span class=\"kw1\">if<\/span> <span class=\"br0\">(<\/span>subscription<span class=\"br0\">)<\/span> <span class=\"br0\">{<\/span>\n        await subscription.<span class=\"me1\">unsubscribe<\/span><span class=\"br0\">(<\/span><span class=\"br0\">)<\/span><span class=\"sy0\">;<\/span>\n        await fetch<span class=\"br0\">(<\/span>`https<span class=\"sy0\">:<\/span><span class=\"co1\">\/\/example.com\/push_unsubscribe\/${subscription.endpoint}`, {method: \"DELETE\"});<\/span>\n    <span class=\"br0\">}<\/span>\n    <span class=\"kw1\">else<\/span> <span class=\"br0\">{<\/span>\n        <span class=\"co1\">\/\/ already subscribed<\/span>\n    <span class=\"br0\">}<\/span>\n\u00a0\n<span class=\"br0\">}<\/span><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Calling <code>unsubscribe()<\/code> on a <code>PushSubscription<\/code> cancels the subscription, reverting the browser to its default state. Your service worker will stop receiving <code>push<\/code> events. The subscription\u2019s endpoint is sent to your server so you can remove it from your data store and avoid sending data to what\u2019s now a dead URL.<\/p>\n<h2 id=\"handling-expirations-and-renewals\"><span class=\"ez-toc-section\" id=\"Handling_Expirations_and_Renewals\"><\/span>Handling Expirations and Renewals<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>You might have noticed the <code>expirationTime<\/code> property on the <a rel=\"nofollow noopener\" target=\"_blank\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/PushSubscription\"><code>PushSubscription<\/code><\/a> object created by the browser. This won\u2019t always be set; when it is, the device will stop receiving notifications after this time.<\/p>\n<p>In practice, <code>expirationTime<\/code> isn\u2019t currently used in major browsers. Tokens produced by Chrome <a rel=\"nofollow noopener\" target=\"_blank\" href=\"https:\/\/developers.google.com\/web\/ilt\/pwa\/introduction-to-push-notifications\">don\u2019t expire<\/a> until manually unsubscribed so <code>expirationTime<\/code> is always <code>null<\/code>. Firefox doesn\u2019t set <code>expirationTime<\/code> either but its notification service can replace subscriptions during their lifetime.<\/p>\n<p>You can respond to the browser changing your active push subscription by implementing the <code>pushsubscriptionchange<\/code> event in your service worker. Unfortunately there are two versions of this event: the original implementation, currently used by Firefox, and the new v2, not yet supported in any browser.<\/p>\n<p>The original spec has serious usability issues which make it difficult to respond to the event. When you receive a v1 event, the browser has deleted the original subscription and you need to manually create a new one. The problem is without access to the expired subscription you can\u2019t issue a \u201creplace\u201d request to your server \u2013 you\u2019ve got no way of accessing the old <code>endpoint<\/code> URL.<\/p>\n<p>The v2 spec solves this by providing an event with <code>oldSubscription<\/code> and <code><a href=\"https:\/\/buradabiliyorum.com\/en\/category\/news\/\" data-internallinksmanager029f6b8e52c=\"2\" title=\"News\" target=\"_blank\" rel=\"noopener\">newS<\/a>ubscription<\/code> properties. When you receive the event, the old subscription has been canceled but you can still access its properties. The new subscription is now created for you by the browser.<\/p>\n<p>Here\u2019s an example of implementing <code>pushsubscriptionchange<\/code> with the new spec:<\/p>\n<div class=\"wp-geshi-highlight-wrap5\">\n<div class=\"wp-geshi-highlight-wrap4\">\n<div class=\"wp-geshi-highlight-wrap3\">\n<div class=\"wp-geshi-highlight-wrap2\">\n<div class=\"wp-geshi-highlight-wrap\">\n<div class=\"wp-geshi-highlight\">\n<div class=\"javascript\">\n<pre class=\"de1\">self.<span class=\"me1\">addEventListener<\/span><span class=\"br0\">(<\/span><span class=\"st0\">\"pushsubscriptionchange\"<\/span><span class=\"sy0\">,<\/span> e <span class=\"sy0\">=&gt;<\/span> <span class=\"br0\">{<\/span>\n    e.<span class=\"me1\">waitUntil<\/span><span class=\"br0\">(<\/span>async <span class=\"br0\">(<\/span><span class=\"br0\">)<\/span> <span class=\"sy0\">=&gt;<\/span> <span class=\"br0\">{<\/span>\n        await fetch<span class=\"br0\">(<\/span><span class=\"st0\">\"https:\/\/example.com\/push_change\"<\/span><span class=\"sy0\">,<\/span> <span class=\"br0\">{<\/span>\n            method<span class=\"sy0\">:<\/span> <span class=\"st0\">\"POST\"<\/span><span class=\"sy0\">,<\/span>\n            headers<span class=\"sy0\">:<\/span> <span class=\"br0\">{<\/span>\n                <span class=\"st0\">\"Content-Type\"<\/span><span class=\"sy0\">:<\/span> <span class=\"st0\">\"application\/json\"<\/span>\n            <span class=\"br0\">}<\/span><span class=\"sy0\">,<\/span>\n            body<span class=\"sy0\">:<\/span> JSON.<span class=\"me1\">stringify<\/span><span class=\"br0\">(<\/span><span class=\"br0\">{<\/span>\n                auth<span class=\"sy0\">:<\/span> <span class=\"br0\">(<\/span>e.<span class=\"me1\">newSubscription<\/span>.<span class=\"me1\">toJSON<\/span><span class=\"br0\">(<\/span><span class=\"br0\">)<\/span>.<span class=\"me1\">keys<\/span><span class=\"sy0\">?<\/span>.<span class=\"me1\">auth<\/span> <span class=\"sy0\">||<\/span> <span class=\"kw2\">null<\/span><span class=\"br0\">)<\/span><span class=\"sy0\">,<\/span>\n                endpoint<span class=\"sy0\">:<\/span> e.<span class=\"me1\">newSubscription<\/span>.<span class=\"me1\">endpoint<\/span><span class=\"sy0\">,<\/span>\n                endpointOld<span class=\"sy0\">:<\/span> e.<span class=\"me1\">oldSubscription<\/span>.<span class=\"me1\">endpoint<\/span><span class=\"sy0\">,<\/span>\n                expirationTime<span class=\"sy0\">:<\/span> e.<span class=\"me1\">newSubscription<\/span>.<span class=\"me1\">expirationTime<\/span><span class=\"sy0\">,<\/span>\n                p256dh<span class=\"sy0\">:<\/span> <span class=\"br0\">(<\/span>e.<span class=\"me1\">newSubscription<\/span>.<span class=\"me1\">toJSON<\/span><span class=\"br0\">(<\/span><span class=\"br0\">)<\/span>.<span class=\"me1\">keys<\/span><span class=\"sy0\">?<\/span>.<span class=\"me1\">p256dh<\/span> <span class=\"sy0\">||<\/span> <span class=\"kw2\">null<\/span><span class=\"br0\">)<\/span>\n            <span class=\"br0\">}<\/span><span class=\"br0\">)<\/span>\n        <span class=\"br0\">}<\/span><span class=\"br0\">)<\/span><span class=\"sy0\">;<\/span>\n    <span class=\"br0\">}<\/span><span class=\"br0\">)<\/span><span class=\"sy0\">;<\/span>\n<span class=\"br0\">}<\/span><span class=\"br0\">)<\/span><span class=\"sy0\">;<\/span><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Endpoints are unique so your server can lookup the old subscription and update its properties with those of the new subscription. If you want to add support for the old spec too, you\u2019ll need to manually track the active subscription endpoint outside of the push API. Storing it into <code>localStorage<\/code> or IndexedDB will let you access it inside your <code>pushsubscriptionchange<\/code> handler so you can ask the server to replace the subscription.<\/p>\n<p>The revised spec is much easier to implement than its older counterpart. Even though it\u2019s not yet supported in browsers, it\u2019s worth adding it to your service worker anyway. A few lines of code will future-proof your push handling against new browser releases.<\/p>\n<h2 id=\"adding-action-buttons\"><span class=\"ez-toc-section\" id=\"Adding_Action_Buttons\"><\/span>Adding Action Buttons<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Push notifications can include interactive buttons that let the user take im<a href=\"https:\/\/buradabiliyorum.com\/en\/category\/social-mediaa\/\" data-internallinksmanager029f6b8e52c=\"1\" title=\"Social Media\" target=\"_blank\" rel=\"noopener\">media<\/a>te actions. Here\u2019s a <code>showNotification()<\/code> call which creates one:<\/p>\n<div class=\"wp-geshi-highlight-wrap5\">\n<div class=\"wp-geshi-highlight-wrap4\">\n<div class=\"wp-geshi-highlight-wrap3\">\n<div class=\"wp-geshi-highlight-wrap2\">\n<div class=\"wp-geshi-highlight-wrap\">\n<div class=\"wp-geshi-highlight\">\n<div class=\"javascript\">\n<pre class=\"de1\">self.<span class=\"me1\">registration<\/span>.<span class=\"me1\">showNotification<\/span><span class=\"br0\">(<\/span>\n    <span class=\"st0\">\"Notification with actions\"<\/span><span class=\"sy0\">,<\/span>\n    <span class=\"br0\">{<\/span>\n        body<span class=\"sy0\">:<\/span> <span class=\"st0\">\"This notification has a button.\"<\/span><span class=\"sy0\">,<\/span>\n        actions<span class=\"sy0\">:<\/span> <span class=\"br0\">[<\/span>\n            <span class=\"br0\">{<\/span>\n                action<span class=\"sy0\">:<\/span> <span class=\"st0\">\"\/home\"<\/span><span class=\"sy0\">,<\/span>\n                title<span class=\"sy0\">:<\/span> <span class=\"st0\">\"Go to Homescreen\"<\/span><span class=\"sy0\">,<\/span>\n                icon<span class=\"sy0\">:<\/span> <span class=\"st0\">\"\/home.png\"<\/span>\n            <span class=\"br0\">}<\/span>\n        <span class=\"br0\">]<\/span>\n    <span class=\"br0\">}<\/span>\n<span class=\"br0\">)<\/span><span class=\"sy0\">;<\/span><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>Each notification can include multiple actions, each with a label, icon and <code>action<\/code>. The latter property should identify an action your app can initiate in response to the user\u2019s press.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-13825\" src=\"https:\/\/www.cloudsavvyit.com\/p\/uploads\/2021\/08\/c7f8a318.png?trim=1,1&amp;bg-color=000&amp;pad=1,1\" alt=\"\" width=\"432\" height=\"267\" onload=\"pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);\" onerror=\"this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);\"\/><\/p>\n<p>When the user taps an action, your service worker receives a <code>notificationclick<\/code> event:<\/p>\n<div class=\"wp-geshi-highlight-wrap5\">\n<div class=\"wp-geshi-highlight-wrap4\">\n<div class=\"wp-geshi-highlight-wrap3\">\n<div class=\"wp-geshi-highlight-wrap2\">\n<div class=\"wp-geshi-highlight-wrap\">\n<div class=\"wp-geshi-highlight\">\n<div class=\"javascript\">\n<pre class=\"de1\">self.<span class=\"me1\">addEventListener<\/span><span class=\"br0\">(<\/span><span class=\"st0\">\"notificationclick\"<\/span><span class=\"sy0\">,<\/span> e <span class=\"sy0\">=&gt;<\/span> <span class=\"br0\">{<\/span>\n    <span class=\"kw1\">const<\/span> uri <span class=\"sy0\">=<\/span> e.<span class=\"me1\">action<\/span><span class=\"sy0\">;<\/span>\n    <span class=\"kw1\">const<\/span> notification <span class=\"sy0\">=<\/span> e.<span class=\"me1\">notification<\/span><span class=\"sy0\">;<\/span>\n    notification.<span class=\"me1\">close<\/span><span class=\"br0\">(<\/span><span class=\"br0\">)<\/span><span class=\"sy0\">;<\/span>\n    clients.<span class=\"me1\">openWindow<\/span><span class=\"br0\">(<\/span>`$<span class=\"br0\">{<\/span>self.<span class=\"me1\">location<\/span>.<span class=\"me1\">origin<\/span><span class=\"br0\">}<\/span>$<span class=\"br0\">{<\/span>action<span class=\"br0\">}<\/span>`<span class=\"br0\">)<\/span><span class=\"sy0\">;<\/span>\n<span class=\"br0\">}<\/span><span class=\"br0\">)<\/span><span class=\"sy0\">;<\/span><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<p>We\u2019re using the <code>action<\/code> property to declare a URI the user can navigate to. A new tab\u2019s opened to the URI when the notification is pressed. Calling <code>notification.close()<\/code> ensures the notification is dismissed too. Otherwise, some platforms will make the user manually swipe it away.<\/p>\n<h2 id=\"summary\"><span class=\"ez-toc-section\" id=\"Summary\"><\/span>Summary<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Implementing Web Push can seem daunting if you\u2019ve not worked with the relevant APIs before. More than the technical concerns, you should keep the user experience at the forefront of your mind and make sure you communicate <em>why<\/em> it\u2019s worth enabling notifications.<\/p>\n<p>Subscribing and unsubscribing to push occurs in your application\u2019s main JavaScript code, using <code>navigator.serviceWorker<\/code> APIs. The code that responds to new push events and displays browser notifications lives in the service worker itself.<\/p>\n<p>Web Push is now supported by most major web browsers, Safari being the prominent exception. Remember that notifications will render differently in each browser and operating system family so don\u2019t assume that a particular feature of the <code>showNotification()<\/code> API will be universally available.\n<\/div>\n<blockquote><p><strong><span style=\"color: #ff6600;\">If you liked the article, do not forget to share it with your friends. Follow us on\u00a0<span style=\"color: #ff0000;\"><a style=\"color: #ff0000;\" href=\"https:\/\/news.google.com\/publications\/CAAqBwgKMLG0nwswvr63Aw\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">Google News<\/a><\/span>\u00a0too, click on the star and choose us from your favorites.<\/span><\/strong><\/p><\/blockquote>\n<blockquote>\n<p style=\"text-align: center;\">For forums sites go to <span style=\"color: #ff9900;\"><a style=\"color: #ff9900;\" href=\"https:\/\/forum.buradabiliyorum.com\/\" target=\"_blank\" rel=\"noopener\">Forum.BuradaBiliyorum.Com<\/a><\/span><\/strong><\/p>\n<\/blockquote>\n<blockquote>\n<p style=\"text-align: center;\"><strong>If you want to read more like this article, you can visit our <span style=\"color: #ff9900;\"><a style=\"color: #ff9900;\" href=\"https:\/\/en.buradabiliyorum.com\/technology\/\" target=\"_blank\" rel=\"noopener\">Technology category.<\/a><\/span><\/strong><\/p>\n<\/blockquote>\n<p><span style=\"color: black;\"><a style=\"color: #ff9900;\" href=\"https:\/\/www.cloudsavvyit.com\/13818\/how-to-handle-web-push-notifications-in-websites-and-pwas\/\" target=\"_blank\" rel=\"noopener\">Source<\/a><\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>&#8220;#How to Handle Web Push Notifications in Websites and PWAs \u2013 CloudSavvy IT&#8221; RoBird\/Shutterstock.com Push notifications are a common sight on the modern web. They let you communicate timely information to the user, even if your site\u2019s not actually open. The user\u2019s browser handles incoming push events and displays notifications using system UI surfaces like&#8230;<\/p>\n","protected":false},"author":1,"featured_media":326607,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"fifu_image_url":"https:\/\/www.cloudsavvyit.com\/p\/uploads\/2021\/08\/3faeb368.png","fifu_image_alt":"","footnotes":""},"categories":[18],"tags":[],"class_list":["post-326606","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-technology"],"_links":{"self":[{"href":"https:\/\/buradabiliyorum.com\/en\/wp-json\/wp\/v2\/posts\/326606","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/buradabiliyorum.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/buradabiliyorum.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/buradabiliyorum.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/buradabiliyorum.com\/en\/wp-json\/wp\/v2\/comments?post=326606"}],"version-history":[{"count":0,"href":"https:\/\/buradabiliyorum.com\/en\/wp-json\/wp\/v2\/posts\/326606\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/buradabiliyorum.com\/en\/wp-json\/wp\/v2\/media\/326607"}],"wp:attachment":[{"href":"https:\/\/buradabiliyorum.com\/en\/wp-json\/wp\/v2\/media?parent=326606"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/buradabiliyorum.com\/en\/wp-json\/wp\/v2\/categories?post=326606"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/buradabiliyorum.com\/en\/wp-json\/wp\/v2\/tags?post=326606"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}