Customize your app's theme and background colors - Progressive web apps

Customize your app's theme and background colors
================================================

When building Progressive Web Apps (PWAs), it's important to not only consider the appearance of your app's content, but also the way the app appears on the user's device once the app is installed.

One way to customize the window in which your app appears is by using the `theme_color` and `background_color` web app manifest members.

By defining the `theme_color` and `background_color` members in your PWA manifest, you can create a more polished experience for your users. These small details can help make your PWA feel more like an OS-native app, and more familiar to your users.

Customize the app window background color
-----------------------------------------

The `background_color` manifest member defines the color that appears in the application window before your app's stylesheets have loaded.

Because this color appears before your stylesheets have loaded, set its value to the same color value as the `background-color` CSS property in your application's stylesheet. This will ensure a smooth visual transition between launching the web application and loading its content.

The value can be any valid CSS `<color>`. In this web app manifest file example, the application's background color is set to the named color `peachpuff`. :

```json
{
  "name": "My app",
  "start\_url": "/",
  "icons": [
    {
      "src": "icon.webp",
      "sizes": "256x256",
      "type": "image/webp"
    }
  ],
  "display": "standalone",
  "background\_color": "peachpuff"
}
```

The application also loads a stylesheet that applies the same color to the background of the body element:

```css
body {
  background-color: peachpuff;
}
```

The following screenshot shows the above code in action. The PWA that uses this code is installed on Windows, and the screenshot shows what the application window looks like before the content of the app has loaded:

![The app window, on Windows, showing the peachpuff background color](/en-US/docs/Web/Progressive_web_apps/How_to/Customize_your_app_colors/background-color-windows.png)

Define a theme color
--------------------

The `theme_color` member in your PWA manifest defines the default color of operating system and browser UI elements used in the application.

Different devices, operating systems, and browsers apply the `theme_color` member differently. For example:

* On mobile devices, the theme color is applied to the status bar.
* On desktop operating systems, the theme color is used to customize the title bar of your standalone app window.

Choose a `theme_color` that complements the overall design and branding of your PWA and works well across a variety of devices and platforms to ensure a consistent user experience.

As with `background_color`, any `<color>` value is valid. In this web app manifest file example, the `theme_color` is set to the `rgb(255 218 185)`, the RGB equivalent of `peachpuff`:

```json
{
  "name": "My app",
  "start\_url": "/",
  "icons": [
    {
      "src": "icon.webp",
      "sizes": "48x48",
      "type": "image/webp"
    }
  ],
  "display": "standalone",
  "background\_color": "peachpuff",
  "theme\_color": "rgb(255 218 185)"
}
```

The following screenshot shows the above code in action when the app is installed on Windows, where the `theme_color` manifest member is used as the color of the title bar:

![The app window, on Windows, showing the theme color in the title bar](/en-US/docs/Web/Progressive_web_apps/How_to/Customize_your_app_colors/background-theme-colors-windows.png)

### Relationship with the `theme-color` meta element value

The `theme-color` value for the `name` attribute of the `<meta>` HTML element can be used to define a theme color per webpage. This is different from the `theme_color` manifest member which you define only once, globally, for your app.

If both are set, the `theme-color` meta element value overrides the `theme_color` manifest member. This lets you define a global color for your app and override it on specific pages.

Note that on some browsers, like Safari on macOS and Chrome on Android, the `theme-color` meta element value is used to style the browser UI too.

See also
--------

* Web App Manifests.
* Meta Theme Color and Trickery on css-tricks.com (2021)
* Recommended fields on web.dev


Display a badge on the app icon - Progressive web apps

Display a badge on the app icon
===============================

Applications native to mobile and desktop operating systems can display badges on top of their app icons to inform users that new content is available. For example, an email client application can display the total number of unread messages in a badge and update this number even if the app is not running.

Here is an example showing the Mail application on an iOS device with a badge in its top right corner:

![The dock area on an iPhone home screen, showing a badge on the Mail app icon](/en-US/docs/Web/Progressive_web_apps/How_to/Display_badge_on_app_icon/mail-badge-ios.png)

Progressive Web Apps (PWAs) can display and update badges on their app icons too.

Displaying and updating a badge is done by using the Badging API. You can call this API from the app's service worker, to display or update the badge even when the app is not running.

Support for badges
------------------

App badges are only supported when a PWA is installed on its host operating system. Badges appear on the app icon which only exists after the app has been installed.

**Note:** This article focuses on the `Navigator.setAppBadge()` and `Navigator.clearAppBadge()` methods from the Badging API and ignores the `Navigator.setClientBadge` and `Navigator.clearClientBadge`. Although these methods are defined in the Badging API specification too, they are for displaying badges on documents, not on application icons.

### Desktop support

On desktop operating systems, badges are only supported on Windows and macOS, and only when the PWA is installed from Chrome or Edge. While the Badging API is supported on Chromium-based browsers on Linux, badges are not displayed on this operating system.

Safari and Firefox on desktop do not support the Badging API and do not support installing PWAs.

### Mobile support

Badges are supported on mobile operating systems, including Chromium-based browsers running on Android and in Safari on iOS and iPadOS, starting with iPadOS 16.4.

Badge best practices
--------------------

Before learning how to use badges, consider these best practices to ensure your app uses badges in the most effective and useful way for your users.

### Check for support

To ensure the Badging API is supported in the user's browser and operating system, to prevent throwing a JavaScript error, check for support before using the API:

```js
if (navigator.setAppBadge) {
  // The API is supported, use it.
} else {
  // The API is not supported, don't use it.
}
```

Do not rely solely on badges to inform users about the availability of new content. Browsers that support the Badging API may be installed on operating systems that do not support displaying a badge. For example, while Chrome supports the Badging API, badges will not appear on installed application icons on Linux.

### Request notification permissions for iOS and/or iPadOS

While notification badges are supported on iOS and iPadOS, badges will not appear until the application is granted notification permissions. To request notification permissions, call the `Notification.requestPermission()` method:

```js
Notification.requestPermission().then((result) => {
  console.log(result);
});
```

Optionally, you can check if a user has previously granted notification permissions using the Permissions API.

### Use badges sparingly

Like notifications, badges can be a very effective way to re-engage users with your app when used sparingly. Make sure to use badges to only signal new content that's important for your users to know about.

### Update badges in real-time

Make sure to update your application badge in real-time. This means updating the badge count to reflect how many new items are actually left for the user to consume, and clearing the app badge when there are no new items.

For example, if an email client app receives new messages in the background, it should update its badge to display the right number of unread messages in the inbox, potentially filtering out messages from other folders such as a spam folder. It's possible to update badges in the background by using the `navigator.setAppBadge()` method from a service worker.

Once the user launches the app and starts reading messages, the email client app should update its badge accordingly by calling `navigator.setAppBadge()` with the new unread messages count, or by calling `navigator.clearAppBadge()` when there are no unread messages.

### Highlight new content in the app

When your app receives new content and adds a badge on the app icon, make sure to highlight that new content for users when they launch the app.

For example, if an email client app displays the unread messages count on the app icon badge, then those messages should be bolded or highlighted in some way when the app is opened.

Displaying and updating the badge
---------------------------------

To display a badge on your PWA's app icon that shows a number of unread messages, use the `Navigator.setAppBadge()` method:

```js
// Check for support first.
if (navigator.setAppBadge) {
  // Display the number of unread messages.
  navigator.setAppBadge(numberOfUnreadMessages);
}
```

You can also display an empty badge using the same method by omitting the count parameter, or setting it to `0`:

```js
// Check for support first.
if (navigator.setAppBadge) {
  // Just display the badge, with no number in it.
  navigator.setAppBadge();
}
```

To remove the badge on the app icon, use the `Navigator.clearAppBadge()` method:

```js
// Check for support first.
if (navigator.clearAppBadge) {
  // Remove the badge on the app icon.
  navigator.clearAppBadge();
}
```

Updating the badge in the background
------------------------------------

Badges can be useful to re-engage users with your app when they're not already using the app. This means that your app must be able to update its badge even when it's not running.

PWAs can use the following mechanisms to update in the background and display, update, or hide their badges:

Push API

PWAs can use this API to receive messages from a server even when the app is not running.

Background Synchronization API

PWAs can use this API to run code in the background when a stable network connection is detected.

Web Periodic Background Synchronization API

PWAs can use this API to run code in the background at periodic intervals of time.

Here is a service worker code example showing how to listen to a server's Push messages and update the app badge to reflect an unread messages count:

```js
// Listen to "push" events in the service worker.
self.addEventListener("push", (event) => {
  // Extract the unread count from the push message data.
  const message = event.data.json();
  const unreadCount = message.unreadCount;

  // Set or clear the badge.
  if (navigator.setAppBadge) {
    if (unreadCount && unreadCount > 0) {
      navigator.setAppBadge(unreadCount);
    } else {
      navigator.clearAppBadge();
    }
  }
});
```

See also
--------

* How to create an app badge
* Re-engage users with badges, notifications, and push messages
* Codelab: Build a push notification server


Create a standalone app - Progressive web apps

Create a standalone app
=======================

Progressive Web Apps (PWAs) that are installed on a user's device can define how they appear when the user launches them. They can choose to be displayed in a web browser, just like websites, or have their own dedicated windows similar to how OS-native applications work.

Users tend to have specific expectations of how installed applications behave on their devices. One of these expectations is that applications have their own dedicated windows.

By using the web app manifest's `display` member, you can define whether the installed PWA is displayed in a browser or has its own dedicated window when the PWA is launched from the user's device.

Use the standalone display mode
-------------------------------

To use a standalone display mode and give your PWA its own dedicated window, add the `display` member to your web app manifest and set its value to `standalone`:

```json
{
  "name": "My app",
  "start\_url": "/",
  "icons": [
    {
      "src": "icon.webp",
      "sizes": "48x48",
      "type": "image/webp"
    }
  ],
  "display": "standalone"
}
```

Note that there are other display modes available, such as `browser`, `minimal-ui`, and `fullscreen`. The display mode you choose changes how much of the browser user interface is shown to the user, from showing all of it, to having your own dedicated window. To learn more about all the available display mode and about how they fallback when one is not supported, see the documentation about the `display` member.

Best practices
--------------

### Handle multi-page navigation

If your application is built using several navigable HTML pages, make sure to include UI elements for controlling navigation within your application.

If you don't have your own navigation elements, use the `minimal-ui` display mode to make sure users can still go between pages using previous and next buttons rendered by the browser in your app's title bar.

Customize your app depending on the display mode
------------------------------------------------

When you define a display mode other than `browser` in your web app manifest, it only applies when the application is installed. Like any other webpage, a manifest's `display` member has no effect when the PWA is not installed. You can check the display mode at runtime to detect if the app is installed or not.

Using the CSS `display-mode` media feature or the `Window.matchMedia()` JavaScript feature, you can selectively apply CSS styles or run JavaScript code in your app based on its display mode.

Here is an example showing how to use the `@media` CSS at-rule to only reveal an element on a webpage when the `standalone` display mode is enabled:

```css
.app-button {
  display: none;
}

@media (display-mode: standalone) {
  .app-button {
    display: block;
  }
}
```

In this example, the `.app-button` element will be hidden by default unless the display mode is set to `standalone`, which happens if the `display` manifest member was set to `standalone` and the app is installed on the user's device.

Here is another example showing how to use the `window.matchMedia()` method to detect if the `standalone` display mode is enabled:

```js
function isStandaloneApp() {
  return window.matchMedia("(display-mode: standalone)").matches;
}
```

See also
--------

* Web App Manifests.
* Display modes.
* Customize your app's title bar on desktop operating systems by using the Window Controls Overlay API.


Associate files with your PWA - Progressive web apps

Associate files with your PWA
=============================

On a device, files are usually associated with apps, so when the user opens the file, the operating system launches the appropriate app and passes the file to it. For example, HTML files are often opened in a web browser, text files in a text editor, and videos in a video player.

Progressive Web Apps can participate in this feature, so when the user clicks on files of specific types, the PWA may be launched to handle it.

There are two parts to adding support for file handling:

* Declare support for certain file types using the `file_handlers` web app manifest member.
* Handling files using the `LaunchQueue` interface.

**Note:** At present this feature is only available on Chromium-based browsers, and only on desktop operating systems.

Declaring support for file types
--------------------------------

To declare support for particular file types, include the `file_handlers` member in your manifest file.

The `file_handlers` member is an array of file handler objects. Each file handler object has two mandatory properties, `action` and `accept`.

* The `accept` property contains MIME types and associated file extensions for files that the handler knows how to handle.
* The `action` property is a URL to which the PWA will navigate when the user opens the file. This page must be in the PWA's scope.

The manifest file below includes a `file_handlers` member with a single handler, which can handle JPEG and PNG files, and will navigate to the PWA's root page when the user clicks one of these files.

```json
{
  "name": "File handling demo",
  "icons": [
    {
      "src": "icons/lightbulb.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "start\_url": "/",
  "display": "standalone",
  "file\_handlers": [
    {
      "action": "/",
      "accept": {
        "image/jpeg": [".jpg", ".jpeg"],
        "image/png": [".png"]
      }
    }
  ]
}
```

With this manifest, once the PWA is installed, it may be opened when the user opens files of these types.

More than one app can usually open files of any given type, so the operating system usually provides a feature enabling the user to choose which app to use to open a file, and to set a default handler. For example, on macOS, the user can right-click a file, select "Get Info", and configure the default handler in the resulting dialog:

![Selecting the default handler on macOS](/en-US/docs/Web/Progressive_web_apps/How_to/Associate_files_with_your_PWA/macos-get-info-dialog.png)

Asking permission
-----------------

The first time the browser is about to launch your PWA to handle one or more files that the user opened, it will ask the user to confirm that they want to use your PWA to open it. For example, the Chrome dialog looks like this:

![Chrome warning dialog for launching PWA to handle a file](/en-US/docs/Web/Progressive_web_apps/How_to/Associate_files_with_your_PWA/macos-chrome-launch-warning.png)

Handling the files
------------------

When the browser launches your PWA and navigates to the page you specified in the `action` property of the `file_handlers` manifest member, you'll need to run some code to handle the file. This code will run in the page that was specified in the `action` property.

The key interface here is `LaunchQueue`, which is available as a property of the global `Window` object.

The `LaunchQueue` interface has a single method, `setConsumer()`, which takes as an argument a callback function which will be called when the browser has launched the PWA with one or more files to handle.

The callback is passed a `LaunchParams` object, which includes a `files` property containing an array of `FileSystemHandle` objects, each of which represents one of the files that the user opened.

For example, the code below reads the files and assigns their contents to `<img>` elements, which it adds to the page:

```js
const imageContainer = document.querySelector("#container");

if ("launchQueue" in window) {
  launchQueue.setConsumer(async (launchParams) => {
    for (const file of launchParams.files) {
      const img = document.createElement("img");
      img.src = URL.createObjectURL(await file.getFile());
      imageContainer.appendChild(img);
    }
  });
}
```

Note that the code checks that `launchQueue` exists before using it, to ensure the app behaves gracefully in browsers that don't support the API.

See also
--------

* `file_handlers` manifest member
* `LaunchQueue` interface
* File System API
* File API
* Let installed web applications be file handlers on developer.chrome.com (2022)
* Handle files in Progressive Web Apps on learn.microsoft.com (2023)


js13kGames - Progressive web apps

js13kGames
==========

* Overview: js13kGames
* Next

In this tutorial we will examine the source code of a website that lists information about games submitted to the *A-Frame* category in the js13kGames 2017 competition. You don't have to think about what the actual content on the website is; the main point is to learn how to use PWA features in your own projects.

You can see this app in action online, and the source code is available on GitHub. We'll be examining this code carefully over the course of this tutorial.

Let's start by looking at the structure of our example app.

* Overview: js13kGames
* Next


CycleTracker - Progressive web apps

CycleTracker
============

* Next

This intro-level tutorial walks through all the steps of building a basic progressive web app, or PWA. We will be using web technologies — HTML, CSS, and JavaScript — to build a period tracking web app called "CycleTracker". Like all web apps, CycleTracker is designed to work in all browsers on all devices.

We will walk through the steps of building a fully functional web app, then progressively enhance CycleTracker to make it installable and to work even when the user is offline.

By default, PWAs are regular websites, built with the same technologies. Just like a regular websites, PWAs are linkable, discoverable via search engines, and visible in a browser. By including a manifest file and service worker, and serving the website over TLS, any website can become a PWA.

PWA benefits
------------

Using the languages of the web, we will create a fully functioning application that works both online and offline, both in the browsers and on the user's operating systems (OS). Like any regular website, CycleTracker is hosted on and downloadable from a web server. All we need is a text editor: CycleTracker, like all PWAs, doesn't require any additional programming language knowledge, packaging, or proprietary SDK. CycleTracker, like any PWA, can be seamlessly installed on any operating system without the need of app stores (nor app store approval and fees).

Use standard and open web technologies

Historically, for an application to be installable on an OS, such as Windows, iOS, MacOS, Linux, and Android, the applications are developed in OS-supported programming languages, like C#, .Net, Objective C, Swift, Kotlin, Java, or Python. PWAs are based on a different model: they use a single code base, written using standard open web technologies (HTML, CSS, and JavaScript) that work across OSes.

No compiling required

With most programming languages—like Java, C/C++, and Kotlin, which are commonly used for building Android apps, and Objective-C and Swift, for iOS—the code needs to be compiled and packaged into an installable format, like .exe, .dmg, .elf, and .apk, or another installable file type, depending on the operating system. Depending on the language, compiling and packaging may require the OS's SDK. PWAs use web technologies supported by every operating system that doesn't need to be packaged or compiled. Yes, developer teams can have complex build systems, but, as we will demonstrate as we build CycleTracker, PWAs can be built out of just HTML and JavaScript (and CSS, though styling is not necessarily required for a PWA).

Available anywhere and everywhere

Single-OS-only applications are distributed to users through downloads, often in proprietary app stores. They are available through a vendor like the Apple App Store, Google Play, Microsoft Store, or similar. PWAs don't have these requirements. If you want to distribute your CycleTracker app, you won't require an intermediary. A user can access your app by visiting its online version. But if you want, it is possible to distribute your PWA on the Play Store and App Store, undifferentiated from other iOS or Android apps.

Easy for the user to install

Historically, downloaded single-OS-only applications have to be intentionally installed by the user. Depending on the OS, install format, and download origin, this can be a multi-step installation process. PWAs are streamlined. PWAs are available to anyone with a supporting browser, and are installable with a couple of clicks.

### Compared to OS-installed software applications

Once installed, PWAs can be made to appear and act similarly to other applications installed on the user's device:

Application window

With a manifest setting, we'll make CycleTracker open in its own application window, meaning installed PWAs are similar to other installed applications.

Application icon

PWAs display an application icon in the same location as other installed applications installed on the users' operating system. This can be an icon on the homescreen, in the toolbar, in the application's folder, or wherever the device displays application icons. We'll learn how to declare icons for CycleTracker, so, once installed, our PWA can appear like any other installed application on the user's device.

Works offline

Internet access is initially required to download the application and is also required when syncing data with the server or other users. This is required of all applications, not just PWAs. We'll add a service worker to create an offline experience, meaning CycleTracker will work even when the user loses internet access. We will only touch on the power of PWA offline support. Service workers can be used to make PWAs work offline just like any other installed application. If a user makes changes while offline, service workers enable PWAs to sync data once connectivity is restored. With service workers, the user doesn't need to be actively engaging with the PWA, in fact, the PWA doesn't even need to be open, for it to send and retrieve server data.

CycleTracker PWA lessons
------------------------

The base web application for this PWA tutorial is a period tracker, in which the user can track the beginning and end of each menstrual cycle. We'll create a static website shell and style it, then learn how to create a secure connection to view our progress. We'll add JavaScript functionality converting the HTML and CSS shell into a fully functioning application storing data in localStorage. Using this fully functional web application you built, we will progressively enhance this web app into an offline capable PWA by adding a manifest file, including iconography, and a service worker.

The steps include:

App HTML and CSS

Line by line explanation of HTML for the static content of the website, the CycleTracker static content, along with the CSS to style that content.

Local development environment or secure connection

While all websites should be served over https, with PWAs, https is a requirement. Service workers, and therefore PWAs, are restricted to secure contexts including TLS contexts served with the `https://` protocol and locally-delivered resources, including `127.0.0.1` and `localhost` URLs served with the `http://` protocol. We will look at the page in the current state with the `file://` protocol, then cover options for creating a secure localhost connection to test your code as we progress through the tutorial steps. We also look at serving your PWA with GitHub pages.

JavaScript and LocalStorage

Full explanation of the JavaScript functionality to make a client-side period tracker web application so we have a functioning application that can be progressively enhanced into a PWA, using `localStorage` to store period information.

Manifest: identity, appearance, and iconography

A PWA requires a manifest, which is a JSON file that describes the name, icon, description, and other definitions of how the application works within the operating system on which the PWA is installed. We will create a manifest file that defines the appearance of the application when installed, including which icons should be used depending on the user's device, and parameters for the application viewport. We also look at debugging the manifest file with browser developer tools.

Service workers

The service worker enables the application to work offline. With the secure connection in the previous step, the initial visit to a page provides its baseline functionality while the service worker downloads. After a service worker is installed and activated, it controls the page to offer improved reliability and speed.

To complete this tutorial, it is helpful to have a basic level of understanding of HTML, CSS, and JavaScript. The tutorial provides instructions on creating the manifest file and initiating the service worker, as well as setting up a local development environment so you can view your progress.

While a secure connection is a requirement, there are no software requirements for creating a PWA other than any text editor to code the PWA and a browser to view it.

You can try the live period tracker and view the period tracker source code on GitHub.

* Next


How to make PWAs re-engageable using Notifications and Push - Progressive web apps

How to make PWAs re-engageable using Notifications and Push
===========================================================

* Previous
* Overview: js13kGames
* Next

Having the ability to cache the contents of an app to work offline is a great feature. Allowing the user to install the web app on their device is even better. But instead of relying only on user actions, we can do more, using push messages and notifications to automatically re-engage and deliver new content whenever it is available.

Two APIs, one goal
------------------

The Push API and Notifications API are two separate APIs, but they work well together when you want to provide engaging functionality in your app. Push is used to deliver new content from the server to the app without any client-side intervention, and its operation is handled by the app's service worker. Notifications can be used by the service worker to show new information to the user, or at least alert them when something has been updated.

They work outside of the browser window, just like service workers, so updates can be pushed and notifications can be shown when the app's page is out of focus or even closed.

Notifications
-------------

Let's start with notifications — they can work by themselves, but become more useful when combined with push. Let's look at notifications in isolation to begin with.

### Request permission

To show a notification, we have to request permission to do so first. Instead of showing the notification immediately though, best practice dictates that we should show the popup when the user requests it by clicking on a button:

```js
const button = document.getElementById("notifications");
button.addEventListener("click", () => {
  Notification.requestPermission().then((result) => {
    if (result === "granted") {
      randomNotification();
    }
  });
});
```

This shows a popup using the operating system's own notifications service:

![Notification of js13kPWA.](/en-US/docs/Web/Progressive_web_apps/Tutorials/js13kGames/Re-engageable_Notifications_Push/js13kpwa-notification.png)

When the user confirms to receive notifications, the app can then show them. The result of the user action can be default, granted or denied. The default option is chosen when the user won't make a choice, and the other two are set when the user clicks yes or no respectively.

When accepted, the permission works for both notifications and push.

### Create a notification

The example app creates a notification out of the available data — a game is picked at random, and the chosen one feeds the notification with the content: it sets the game's name as the title, mentioning the author in the body, and showing the image as an icon:

```js
function randomNotification() {
  const randomItem = Math.floor(Math.random() \* games.length);
  const notifTitle = games[randomItem].name;
  const notifBody = `Created by ${games[randomItem].author}.`;
  const notifImg = `data/img/${games[randomItem].slug}.jpg`;
  const options = {
    body: notifBody,
    icon: notifImg,
  };
  new Notification(notifTitle, options);
  setTimeout(randomNotification, 30000);
}
```

A new random notification is created every 30 seconds until it becomes too annoying and is disabled by the user. (For a real app, the notifications should be much less frequent, and more useful.) The advantage of the Notifications API is that it uses the operating system's notification functionality. This means that notifications can be displayed to the user even when they are not looking at the web app, and the notifications look similar to ones displayed by native apps.

Push
----

Push is more complicated than notifications — we need to subscribe to a server that will then send the data back to the app. The app's Service Worker will receive data from the push server, which can then be shown using the notifications system, or another mechanism if desired.

 The technology is still at a very early stage — some working examples use the Google Cloud Messaging platform, but are being rewritten to support VAPID (Voluntary Application Identification), which offers an extra layer of security for your app.
 You can examine the Service Workers Cookbook examples, try to set up a push messaging server using Firebase, or build your own server (using Node.js for example).

As mentioned before, to be able to receive push messages, you have to have a service worker, the basics of which are already explained in the Making PWAs work offline with Service workers article. Inside the service worker, a push-service subscription mechanism is created by calling the `getSubscription()` method of the `PushManager` interface.

```js
navigator.serviceWorker.register("service-worker.js").then((registration) => {
  return registration.pushManager.getSubscription().then(/\* ... \*/);
});
```

Once the user is subscribed, they can receive push notifications from the server.

From the server-side, the whole process has to be encrypted with public and private keys for security reasons — allowing everyone to send push messages unsecured using your app would be a terrible idea. See the Web Push data encryption test page for detailed information about securing the server. The server stores all the information received when the user subscribed, so the messages can be sent later on when needed.

To receive push messages, we can listen to the `push` event in the Service Worker file:

```js
self.addEventListener("push", (e) => {
  /\* ... \*/
});
```

The data can be retrieved and then shown as a notification to the user immediately. This, for example, can be used to remind the user about something, or let them know about new content being available in the app.

### Push example

Push needs the server part to work, so we're not able to include it in the js13kPWA example hosted on GitHub Pages, as it offers hosting of static files only. It is all explained in the Service Worker Cookbook — see the Push Payload Demo.

This demo consists of three files:

* index.js, which contains the source code of our app
* server.js, which contains the server part (written in Node.js)
* service-worker.js, which contains the Service Worker-specific code.

Let's explore all of these

#### index.js

The `index.js` file starts by registering the service worker:

```js
navigator.serviceWorker
  .register("service-worker.js")
  .then((registration) => {
    return registration.pushManager
      .getSubscription()
      .then(async (subscription) => {
        // registration part
      });
  })
  .then((subscription) => {
    // subscription part
  });
```

It is a little bit more complicated than the service worker we saw in the js13kPWA demo. In this particular case, after registering, we use the registration object to subscribe, and then use the resulting subscription object to complete the whole process.

In the registration part, the code looks like this:

```js
async (subscription) => {
  if (subscription) {
    return subscription;
  }
};
```

If the user has already subscribed, we then return the subscription object and move to the subscription part. If not, we initialize a new subscription:

```js
const response = await fetch("./vapidPublicKey");
const vapidPublicKey = await response.text();
const convertedVapidKey = urlBase64ToUint8Array(vapidPublicKey);
```

The app fetches the server's public key and converts the response to text; then it needs to be converted to a Uint8Array (to support Chrome). To learn more about VAPID keys you can read the Sending VAPID identified WebPush Notifications via Mozilla's Push Service blog post.

The app can now use the `PushManager` to subscribe the new user. There are two options passed to the `PushManager.subscribe()` method — the first is `userVisibleOnly: true`, which means all the notifications sent to the user will be visible to them, and the second one is the `applicationServerKey`, which contains our successfully acquired and converted VAPID key.

```js
registration.pushManager.subscribe({
  userVisibleOnly: true,
  applicationServerKey: convertedVapidKey,
});
```

Now let's move to the subscription part — the app first sends the subscription details as JSON to the server using Fetch.

```js
fetch("./register", {
  method: "post",
  headers: {
    "Content-type": "application/json",
  },
  body: JSON.stringify({ subscription }),
});
```

Then the `onclick` function on the *Subscribe* button is defined:

```js
document.getElementById("doIt").onclick = () => {
  const payload = document.getElementById("notification-payload").value;
  const delay = document.getElementById("notification-delay").value;
  const ttl = document.getElementById("notification-ttl").value;

  fetch("./sendNotification", {
    method: "post",
    headers: {
      "Content-type": "application/json",
    },
    body: JSON.stringify({
      subscription,
      payload,
      delay,
      ttl,
    }),
  });
};
```

When the button is clicked, `fetch` asks the server to send the notification with the given parameters: `payload` is the text that to be shown in the notification, `delay` defines a delay in seconds until the notification will be shown, and `ttl` is the time-to-live setting that keeps the notification available on the server for a specified amount of time, also defined in seconds.

Now, onto the next JavaScript file.

#### server.js

The server part is written in Node.js and needs to be hosted somewhere suitable, which is a subject for an entirely separate article. We will only provide a high-level overview here.

The web-push module is used to set the VAPID keys, and optionally generate them if they are not available yet.

```js
const webPush = require("web-push");

if (!process.env.VAPID\_PUBLIC\_KEY || !process.env.VAPID\_PRIVATE\_KEY) {
  console.log(
    "You must set the VAPID\_PUBLIC\_KEY and VAPID\_PRIVATE\_KEY " +
      "environment variables. You can use the following ones:",
  );
  console.log(webPush.generateVAPIDKeys());
  return;
}

webPush.setVapidDetails(
  "https://example.com",
  process.env.VAPID\_PUBLIC\_KEY,
  process.env.VAPID\_PRIVATE\_KEY,
);
```

 Next, a module defines and exports all the routes an app needs to handle: getting the VAPID public key, registering, and then sending notifications.
 You can see the variables from the `index.js` file being used: `payload`, `delay` and `ttl`.

```js
module.exports = (app, route) => {
  app.get(`${route}vapidPublicKey`, (req, res) => {
    res.send(process.env.VAPID\_PUBLIC\_KEY);
  });

  app.post(`${route}register`, (req, res) => {
    res.sendStatus(201);
  });

  app.post(`${route}sendNotification`, (req, res) => {
    const subscription = req.body.subscription;
    const payload = req.body.payload;
    const options = {
      TTL: req.body.ttl,
    };

    setTimeout(() => {
      webPush
        .sendNotification(subscription, payload, options)
        .then(() => {
          res.sendStatus(201);
        })
        .catch((error) => {
          console.log(error);
          res.sendStatus(500);
        });
    }, req.body.delay \* 1000);
  });
};
```

#### service-worker.js

The last file we will look at is the service worker:

```js
self.addEventListener("push", (event) => {
  const payload = event.data?.text() ?? "no payload";
  event.waitUntil(
    self.registration.showNotification("ServiceWorker Cookbook", {
      body: payload,
    }),
  );
});
```

All it does is add a listener for the `push` event, create the payload variable consisting of the text taken from the data (or create a string to use if data is empty), and then wait until the notification is shown to the user.

 Feel free to explore the rest of the examples in the Service Worker Cookbook if you want to know how they are handled.
 There's a big collection of working examples showing general use, but also web push, caching strategies, performance, working offline, and more.

* Previous
* Overview: js13kGames
* Next


How to make PWAs installable - Progressive web apps

How to make PWAs installable
============================

* Previous
* Overview: js13kGames
* Next

In the last step of this tutorial, we read about how the example application, js13kPWA, works offline thanks to its service worker, but we can go even further and allow users to install the web app on their device. The installed web app can then be launched by users just as if it were any OS-native app. This article explains how to achieve this using the web app's manifest.

The web app manifest allows the app to be launched directly from the device's home screen, taskbar, or dock, rather than the user having to open the browser and then navigate to the site by using a bookmark or typing the URL. Your web app can sit next to native applications, which makes it easier for users to access. Additionally, you can specify that the app be launched in fullscreen or standalone mode, thus removing the default browser user interface that would otherwise be present, creating an even more seamless and native-like feel.

To learn more, see Making PWAs installable.

Requirements
------------

To make our example application installable, the following things are needed:

* A web application manifest, with the correct members filled in.
* The website to be served from a secure (HTTPS) domain.
* An icon to represent the app on the device.
* A service worker registered, to allow the app to work offline.
### The web app manifest file

The key element is a web application manifest file, which lists all the information about the website in a JSON format.

It usually resides in the root folder of a web app. It contains useful information, such as the app's title, paths to different-sized icons that can be used to represent the app on an OS (such as an icon on the home screen, an entry in the Start menu, or an icon on the desktop), and a background color to use in loading or splash screens. This information is needed for the browser to present the web app properly during the installation process, as well as within the device's app-launching interface, such as the home screen of a mobile device.

The `js13kpwa.webmanifest` file of the js13kPWA web app is included in the `<head>` block of the `index.html` file using the following line of code:

```html
<link rel="manifest" href="js13kpwa.webmanifest" />
```

**Note:** many use `manifest.json` for web app manifests as the contents are organized in a JSON structure. However, the `.webmanifest` file format is explicitly mentioned in the W3C manifest specification, so that's what we'll use here.

The content of the file looks like this:

```json
{
  "name": "js13kGames Progressive Web App",
  "short\_name": "js13kPWA",
  "description": "Progressive Web App that lists games submitted to the A-Frame category in the js13kGames 2017 competition.",
  "icons": [
    {
      "src": "icons/icon-32.png",
      "sizes": "32x32",
      "type": "image/png"
    },
    // ...
    {
      "src": "icons/icon-512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "start\_url": "/pwa-examples/js13kpwa/index.html",
  "display": "fullscreen",
  "theme\_color": "#B12A34",
  "background\_color": "#B12A34"
}
```

Most of the members are self-explanatory. Here is a description of the members shown in the previous code sample:

* `name`: The full name of your web app.
* `short_name`: Short name to be shown on the home screen.
* `description`: A sentence or two explaining what your app does.
* `icons`: Information about the app icons — source URLs, sizes, and types. Be sure to include at least a few, so that one that fits best will be chosen for the user's device. See Define your app icons.
* `start_url`: The index document to launch when starting the app.
* `display`: How the app is displayed; can be `fullscreen`, `standalone`, `minimal-ui`, or `browser`.
* `theme_color`: A primary color for the UI, used by operating system.
* `background_color`: A color used as the app's default background, used during install and on the splash screen.

There are even more members you can use than listed above — be sure to check the Web App Manifest reference for details.

Installing the PWA
------------------

Using the information found in our web app manifest, supporting browsers can display an installation prompt to the user. When the user visits the PWA, they can be prompted to install it on their device. When they accept the prompt, the PWA is installed just like other OS-native apps and the user can launch and use the web app as normal.

To learn more about how users can install PWAs, see Installing and uninstalling web apps.

### Splash screen

On certain devices, a splash screen is also generated from the information in the manifest, which is shown when the PWA is launched and while it's being loaded.

![Screenshot of the app's splash screen on a mobile phone. It is an all-red page with the application logo in the middle and its name below it: "js13kGames Progressive Web App"](/en-US/docs/Web/Progressive_web_apps/Tutorials/js13kGames/Installable_PWAs/js13kpwa-splash.png)

The icon and the theme and background colors are used to create this screen.

Summary
-------

In this article, we learned about how we can make PWAs installable with a properly-configured web app manifest, and how the user can then install the PWA on their devices.

Now let's move to the last step in our PWA tutorial: using push notifications to share announcements with the user, and to help the user re-engage with our app.

* Previous
* Overview: js13kGames
* Next


Progressive loading - Progressive web apps

Progressive loading
===================

* Previous
* Overview: js13kGames

In previous steps of this tutorial we covered APIs that help us make our js13kPWA example a Progressive Web App: Service Workers, Web Manifests, Notifications and Push. In this article we will go further and improve the performance of the app by progressively loading its resources.

First meaningful paint
----------------------

It's important to deliver something meaningful to the user as soon as possible — the longer they wait for the page to load, the bigger the chance they will leave before waiting for everything to finish. We should be able to show them at least the basic view of the page they want to see, with placeholders in the places more content will eventually be loaded.

This could be achieved by progressive loading — also known as Lazy loading. This is all about deferring loading of as many resources as possible (HTML, CSS, JavaScript), and only loading those immediately that are really needed for the very first experience.

Bundling versus splitting
-------------------------

Many visitors won't go through every single page of a website, yet the usual approach is to bundle every feature we have into one big file. A `bundle.js` file can be many megabytes, and a single `style.css` bundle can contain everything from basic CSS structure definitions to all the possible styles of every version of the site: mobile, tablet, desktop, print only, etc.

It is faster to load all that information as one file rather than many small ones, but if the user doesn't need everything at the very beginning, we could load only what's crucial and then manage other resources when needed.

Render-blocking resources
-------------------------

Bundling is a problem, because the browser has to load the HTML, CSS, and JavaScript before it can paint their rendered results onto the screen. During the few seconds between initial website access and completion of loading, the user sees a blank page, which is a bad experience.

To fix that we can, for example, add `defer` to JavaScript files:

```html
<script src="app.js" defer></script>
```

They will be downloaded and executed *after* the document itself has been parsed, so it won't block rendering the HTML structure.

Another technique is to load JavaScript modules using dynamic import only when needed.

For example, if a website has a search button, we can load the JavaScript for the search function after the user clicks on the search button:

```js
document.getElementById("open-search").addEventListener("click", async () => {
  const searchModule = await import("/modules/search.js");
  searchModule.loadAutoComplete();
});
```

Once the user clicks on the button, the async click handler is called. The function waits till the module is loaded, then calls the `loadAutoComplete()` function exported from that module. The `search.js` module is therefore only downloaded, parsed, and executed when the interaction happens.

We can also split CSS files and add media types to them:

```html
<link rel="stylesheet" href="style.css" />
<link rel="stylesheet" href="print.css" media="print" />
```

This will tell the browser to load them only when the condition is met.

In our js13kPWA demo app, the CSS is simple enough to leave it all in a single file with no specific rules as to how to load them. We could go even further and move everything from `style.css` to the `<style>` tag in the `<head>` of `index.html` — this would improve performance even more, but for the readability of the example we will skip that approach too.

Images
------

Besides JavaScript and CSS, websites will likely contain a number of images. When you include `<img>` elements in your HTML, then every referenced image will be fetched and downloaded during initial website access. It's not unusual to have megabytes of image data to download before announcing the site is ready, but this again creates a bad perception of performance. We don't need all of the images in the best possible quality at the very beginning of viewing the site.

This can be optimized. First of all, you should use tools or services similar to TinyPNG, which will reduce the file size of your images without altering the quality too much. If you're past that point, then you can start thinking about optimizing the image loading using JavaScript. We'll explain this below.

### Placeholder image

Instead of having all the screenshots of games referenced in `<img>` element `src` attributes, which will force the browser to download them automatically, we can do it selectively via JavaScript. The js13kPWA app uses a placeholder image instead, which is small and lightweight, while the final paths to target images are stored in `data-src` attributes:

```html
<img src="data/img/placeholder.png" data-src="data/img/SLUG.jpg" alt="NAME" />
```

Those images will be loaded via JavaScript *after* the site finishes building the HTML structure. The placeholder image is scaled the same way the original images are, so it will take up the same space and not cause the layout to repaint as the images load.

### Loading via JavaScript

The `app.js` file processes the `data-src` attributes like so:

```js
let imagesToLoad = document.querySelectorAll("img[data-src]");
const loadImages = (image) => {
  image.setAttribute("src", image.getAttribute("data-src"));
  image.onload = () => {
    image.removeAttribute("data-src");
  };
};
```

The `imagesToLoad` variable contains references to all the images, while the `loadImages` function moves the path from `data-src` to `src`. When each image is actually loaded, we remove its `data-src` attribute as it's not needed anymore. Then we loop through each image and load it:

```js
imagesToLoad.forEach((img) => {
  loadImages(img);
});
```

### Blur in CSS

To make the whole process more visually appealing, the placeholder is blurred in CSS.

![Screenshot of placeholder images in the js13kPWA app.](/en-US/docs/Web/Progressive_web_apps/Tutorials/js13kGames/Loading/js13kpwa-placeholders.png)

We render the images with a blur at the beginning, so a transition to the sharp one can be achieved:

```css
article img[data-src] {
  filter: blur(0.2em);
}

article img {
  filter: blur(0em);
  transition: filter 0.5s;
}
```

This will remove the blur effect within half a second, which looks good enough for the "loading" effect.

Loading on demand
-----------------

The image loading mechanism discussed in the above section works OK — it loads the images after rendering the HTML structure, and applies a nice transition effect in the process. The problem is that it still loads *all* the images at once, even though the user will only see the first two or three upon page load.

This problem can be solved by loading the images only when needed: this is called *lazy loading*. Lazy loading is a technique to load images only when they appear in the viewport. There are several ways to tell the browser to lazy load images.

### The loading attribute on <img>

The easiest way to tell the browser to load lazily doesn't involve JavaScript. You add the `loading` attribute to an `<img>` element with the value `lazy`, and the browser will know to load this image only when needed.

```html
<img
 src="data/img/placeholder.png"
 data-src="data/img/SLUG.jpg"
 alt="NAME"
 loading="lazy" />
```

### Intersection Observer

This is a progressive enhancement to the previously working example — Intersection Observer will load target images only when the user scrolls down, causing them to display in the viewport.

Here's what the relevant code looks like:

```js
if ("IntersectionObserver" in window) {
  const observer = new IntersectionObserver((items, observer) => {
    items.forEach((item) => {
      if (item.isIntersecting) {
        loadImages(item.target);
        observer.unobserve(item.target);
      }
    });
  });
  imagesToLoad.forEach((img) => {
    observer.observe(img);
  });
} else {
  imagesToLoad.forEach((img) => {
    loadImages(img);
  });
}
```

If the `IntersectionObserver` object is supported, the app creates a new instance of it. The function passed as a parameter is handling the case when one or more items are intersecting with the observer (i.e. is appearing inside the viewport). We can iterate over each case and react accordingly — when an image is visible, we load the correct image and stop observing it as we no longer need to observe it.

Let's reiterate our earlier mention of progressive enhancement — the code is written so that the app will work whether Intersection Observer is supported or not. If it's not, we just load the images using the more basic approach covered earlier.

Improvements
------------

Remember that there are many ways to optimize loading times, and this example is exploring only one of the approaches. You could try to make your apps more bulletproof by making them work without JavaScript — either using `<noscript>` to show the image with final `src` already assigned, or by wrapping `<img>` tags with `<a>` elements pointing at the target images, so the user can click and access them when desired.

We won't do that because the app itself is dependent on JavaScript — without it, the list of games wouldn't even be loaded, and the Service Worker code wouldn't be executed.

We could rewrite the loading process to load not only the images, but the complete items consisting of full descriptions and links. It would work like an infinite scroll — loading the items on the list only when the user scrolls the page down. That way the initial HTML structure would be minimal, loading time even smaller, and we would have even greater performance benefits.

Conclusion
----------

Fewer files to load initially, smaller files split into modules, use of placeholders, and loading more content on demand — this will help achieve faster initial load times, which brings benefits to the app creator and offers a smoother experience to the user.

Remember about the progressive enhancement approach — offer a usable product no matter the device or platform, but be sure to enrich the experience to those using modern browsers.

Final thoughts
--------------

That's all for this tutorial series — we went through the source code of the js13kPWA example app and learned about the PWA structure, offline availability with Service Workers, installable PWAs, and finally notifications.

And in this article, we've looked into the concept of progressive loading, including an interesting example that makes use of the Intersection Observer API.

Feel free to experiment with the code, enhance your existing app with PWA features, or build something entirely new on your own. PWAs give a huge advantage over regular web apps.

* Previous
* Overview: js13kGames


Making PWAs work offline with Service workers - Progressive web apps

Making PWAs work offline with Service workers
=============================================

* Previous
* Overview: js13kGames
* Next

Now that we've seen what the structure of js13kPWA looks like and have seen the basic shell up and running, let's look at how the offline capabilities using Service Worker are implemented. In this article, we look at how it is used in our js13kPWA example (see the source code also). We examine how to add offline functionality.

Service workers explained
-------------------------

Service Workers are a virtual proxy between the browser and the network. They make it possible to properly cache the assets of a website and make them available when the user's device is offline.

They run on a separate thread from the main JavaScript code of our page, and don't have any access to the DOM structure. This introduces a different approach from traditional web programming — the API is non-blocking, and can send and receive communication between different contexts. You are able to give a Service Worker something to work on, and receive the result whenever it is ready using a Promise-based approach.

Service workers can do more than offering offline capabilities, including handling notifications or performing heavy calculations. Service workers are quite powerful as they can take control over network requests, modify them, serve custom responses retrieved from the cache, or synthesize responses completely.

To learn more about service workers, see Offline and background operation.

Service workers in the js13kPWA app
-----------------------------------

Let's see how the js13kPWA app uses Service Workers to provide offline capabilities.

### Registering the Service Worker

We'll start by looking at the code that registers a new Service Worker, in the app.js file:

```js
if ("serviceWorker" in navigator) {
  navigator.serviceWorker.register("./pwa-examples/js13kpwa/sw.js");
}
```

If the service worker API is supported in the browser, it is registered against the site using the `ServiceWorkerContainer.register()` method. Its contents reside in the sw.js file, and can be executed after the registration is successful. It's the only piece of Service Worker code that sits inside the app.js file; everything else that is Service Worker-specific is written in the sw.js file itself.

### Lifecycle of a Service Worker

When registration is complete, the sw.js file is automatically downloaded, then installed, and finally activated.

#### Installation

The API allows us to add event listeners for key events we are interested in — the first one is the `install` event:

```js
self.addEventListener("install", (e) => {
  console.log("[Service Worker] Install");
});
```

In the `install` listener, we can initialize the cache and add files to it for offline use. Our js13kPWA app does exactly that.

First, a variable for storing the cache name is created, and the app shell files are listed in one array.

```js
const cacheName = "js13kPWA-v1";
const appShellFiles = [
  "/pwa-examples/js13kpwa/",
  "/pwa-examples/js13kpwa/index.html",
  "/pwa-examples/js13kpwa/app.js",
  "/pwa-examples/js13kpwa/style.css",
  "/pwa-examples/js13kpwa/fonts/graduate.eot",
  "/pwa-examples/js13kpwa/fonts/graduate.ttf",
  "/pwa-examples/js13kpwa/fonts/graduate.woff",
  "/pwa-examples/js13kpwa/favicon.ico",
  "/pwa-examples/js13kpwa/img/js13kgames.png",
  "/pwa-examples/js13kpwa/img/bg.png",
  "/pwa-examples/js13kpwa/icons/icon-32.png",
  "/pwa-examples/js13kpwa/icons/icon-64.png",
  "/pwa-examples/js13kpwa/icons/icon-96.png",
  "/pwa-examples/js13kpwa/icons/icon-128.png",
  "/pwa-examples/js13kpwa/icons/icon-168.png",
  "/pwa-examples/js13kpwa/icons/icon-192.png",
  "/pwa-examples/js13kpwa/icons/icon-256.png",
  "/pwa-examples/js13kpwa/icons/icon-512.png",
];
```

Next, the links to images to be loaded along with the content from the data/games.js file are generated in the second array. After that, both arrays are merged using the `Array.prototype.concat()` function.

```js
const gamesImages = [];
for (let i = 0; i < games.length; i++) {
  gamesImages.push(`data/img/${games[i].slug}.jpg`);
}
const contentToCache = appShellFiles.concat(gamesImages);
```

Then we can manage the `install` event itself:

```js
self.addEventListener("install", (e) => {
  console.log("[Service Worker] Install");
  e.waitUntil(
    (async () => {
      const cache = await caches.open(cacheName);
      console.log("[Service Worker] Caching all: app shell and content");
      await cache.addAll(contentToCache);
    })(),
  );
});
```

There are two things that need an explanation here: what `ExtendableEvent.waitUntil` does, and what the `caches` object is.

The service worker does not install until the code inside `waitUntil` is executed. It returns a promise — this approach is needed because installing may take some time, so we have to wait for it to finish.

`caches` is a special `CacheStorage` object available in the scope of the given Service Worker to enable saving data — saving to web storage won't work, because web storage is synchronous. With Service Workers, we use the Cache API instead.

Here, we open a cache with a given name, then add all the files our app uses to the cache, so they are available next time it loads. Resources are identified by their request URL, which is relative to the worker's location.

You may notice we haven't cached `game.js`. This is the file that contains the data we use when displaying our games. In reality this data would most likely come from an API endpoint or database and caching the data would mean updating it periodically when there was network connectivity. We won't go into that here, but the `Periodic Background Sync API` is good further reading on this topic.

#### Activation

There is also an `activate` event, which is used in the same way as `install`. This event is usually used to delete any files that are no longer necessary and clean up after the app in general. We don't need to do that in our app, so we'll skip it.

### Responding to fetches

We also have a `fetch` event at our disposal, which fires every time an HTTP request is fired off from our app. This is very useful, as it allows us to intercept requests and respond to them with custom responses. Here is a simple usage example:

```js
self.addEventListener("fetch", (e) => {
  console.log(`[Service Worker] Fetched resource ${e.request.url}`);
});
```

The response can be anything we want: the requested file, its cached copy, or a piece of JavaScript code that will do something specific — the possibilities are endless.

In our example app, we serve content from the cache instead of the network as long as the resource is actually in the cache. We do this whether the app is online or offline. If the file is not in the cache, the app adds it there first before then serving it:

```js
self.addEventListener("fetch", (e) => {
  e.respondWith(
    (async () => {
      const r = await caches.match(e.request);
      console.log(`[Service Worker] Fetching resource: ${e.request.url}`);
      if (r) {
        return r;
      }
      const response = await fetch(e.request);
      const cache = await caches.open(cacheName);
      console.log(`[Service Worker] Caching new resource: ${e.request.url}`);
      cache.put(e.request, response.clone());
      return response;
    })(),
  );
});
```

Here, we respond to the fetch event with a function that tries to find the resource in the cache and return the response if it's there. If not, we use another fetch request to fetch it from the network, then store the response in the cache so it will be available there next time it is requested.

The `FetchEvent.respondWith` method takes over control — this is the part that functions as a proxy server between the app and the network. This allows us to respond to every single request with any response we want: prepared by the Service Worker, taken from cache, modified if needed.

That's it! Our app is caching its resources on install and serving them with fetch from the cache, so it works even if the user is offline. It also caches new content whenever it is added.

Updates
-------

There is still one point to cover: how do you upgrade a Service Worker when a new version of the app containing new assets is available? The version number in the cache name is key to this:

```js
const cacheName = "js13kPWA-v1";
```

When this updates to v2, we can then add all of our files (including our new files) to a new cache:

```js
contentToCache.push("/pwa-examples/js13kpwa/icons/icon-32.png");

// ...

self.addEventListener("install", (e) => {
  e.waitUntil(
    (async () => {
      const cache = await caches.open(cacheName);
      await cache.addAll(contentToCache);
    })(),
  );
});
```

A new service worker is installed in the background, and the previous one (v1) works correctly up until there are no pages using it — the new Service Worker is then activated and takes over management of the page from the old one.

Clearing the cache
------------------

Remember the `activate` event we skipped? It can be used to clear out the old cache we don't need anymore:

```js
self.addEventListener("activate", (e) => {
  e.waitUntil(
    caches.keys().then((keyList) => {
      return Promise.all(
        keyList.map((key) => {
          if (key === cacheName) {
            return;
          }
          return caches.delete(key);
        }),
      );
    }),
  );
});
```

This ensures we have only the files we need in the cache, so we don't leave any garbage behind; the available cache space in the browser is limited, so it is a good idea to clean up after ourselves.

Other use cases
---------------

Serving files from cache is not the only feature Service Worker offers. If you have heavy calculations to do, you can offload them from the main thread and do them in the worker, and receive results as soon as they are available. Performance-wise, you can prefetch resources that are not needed right now, but might be in the near future, so the app will be faster when you actually need those resources.

Summary
-------

In this article we took a simple look at how you can make your PWA work offline with service workers. Be sure to check out our further documentation if you want to learn more about the concepts behind the Service Worker API and how to use it in more detail.

Service Workers are also used when dealing with push notifications — this will be explained in a subsequent article.

* Previous
* Overview: js13kGames
* Next


Progressive web app structure - Progressive web apps

Progressive web app structure
=============================

* Previous
* Overview: js13kGames
* Next

In this article, we will analyze the js13kPWA application, why it is built that way, and what benefits it brings.

The js13kPWA website structure is quite simple: it consists of a single HTML file (index.html) with basic CSS styling (style.css), and a few images, scripts, and fonts. The folder structure looks like this:

![Folder structure of js13kPWA.](/en-US/docs/Web/Progressive_web_apps/Tutorials/js13kGames/App_structure/js13kpwa-directory.png)

### The HTML

From the HTML point of view, the app shell is everything outside the content section:

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>js13kGames A-Frame entries</title>
    <meta
 name="description"
 content="A list of A-Frame entries submitted to the js13kGames 2017 competition, used as an example for the MDN articles about Progressive Web Apps." />
    <meta name="author" content="end3r" />
    <meta name="theme-color" content="#B12A34" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta
 property="og:image"
 content="https://js13kgames.com/img/js13kgames-banner.png" />
    <link rel="icon" href="favicon.ico" />
    <link rel="stylesheet" href="style.css" />
    <link rel="manifest" href="js13kpwa.webmanifest" />
    <script src="data/games.js" defer></script>
    <script src="app.js" defer></script>
  </head>
  <body>
    <header>
      <p>
        <a class="logo" href="https://js13kgames.com">
          <img src="img/js13kgames.png" alt="js13kGames" />
        </a>
      </p>
    </header>
    <main>
      <h1>js13kGames A-Frame entries</h1>
      <p class="description">
        List of games submitted to the
        <a href="https://js13kgames.com/aframe">A-Frame category</a> in the
        <a href="https://2017.js13kgames.com">js13kGames 2017</a> competition.
        You can
        <a href="https://github.com/mdn/pwa-examples/blob/main/js13kpwa"
 >fork js13kPWA on GitHub</a
 >
        to check its source code.
      </p>
      <button id="notifications">Request dummy notifications</button>
      <section id="content">// Content inserted in here</section>
    </main>
    <footer>
      <p>
        © js13kGames 2012-2018, created and maintained by
        <a href="https://end3r.com">Andrzej Mazur</a> from
        <a href="https://enclavegames.com">Enclave Games</a>.
      </p>
    </footer>
  </body>
</html>
```

The `<head>` section contains some basic info like title, description and links to CSS, web manifest, games content JS file, and app.js — that's where our JavaScript application is initialized. The `<body>` is split into the `<header>` (containing linked image), `<main>` page (with title, description and place for a content), and `<footer>` (copy and links).

The app's only job is to list all the A-Frame entries from the js13kGames 2017 competition. As you can see it is a very ordinary, one page website — the point is to have something simple so we can focus on the implementation of the actual PWA features.

### The CSS

The CSS is also as plain as possible: it uses `@font-face` to load and use a custom font, and it applies some simple styling of the HTML elements. The overall approach is to have the design look good on both mobile (with a responsive web design approach) and desktop devices.

### The main app JavaScript

The app.js file does a few things we will look into closely in the next articles. First of all it generates the content based on this template:

```js
const template = `<article>
 <img src='data/img/placeholder.png' data-src='data/img/SLUG.jpg' alt='NAME'>
 <h3>#POS. NAME</h3>
 <ul>
 <li><span>Author:</span> <strong>AUTHOR</strong></li>
 <li><span>Twitter:</span> <a href='https://twitter.com/TWITTER'>@TWITTER</a></li>
 <li><span>Website:</span> <a href='http://WEBSITE/'>WEBSITE</a></li>
 <li><span>GitHub:</span> <a href='https://GITHUB'>GITHUB</a></li>
 <li><span>More:</span> <a href='http://js13kgames.com/entries/SLUG'>js13kgames.com/entries/SLUG</a></li>
 </ul>
</article>`;
let content = "";
for (let i = 0; i < games.length; i++) {
  let entry = template
    .replace(/POS/g, i + 1)
    .replace(/SLUG/g, games[i].slug)
    .replace(/NAME/g, games[i].name)
    .replace(/AUTHOR/g, games[i].author)
    .replace(/TWITTER/g, games[i].twitter)
    .replace(/WEBSITE/g, games[i].website)
    .replace(/GITHUB/g, games[i].github);
  entry = entry.replace("<a href='http:///'></a>", "-");
  content += entry;
}
document.getElementById("content").innerHTML = content;
```

Next, it registers a service worker:

```js
if ("serviceWorker" in navigator) {
  navigator.serviceWorker.register("/pwa-examples/js13kpwa/sw.js");
}
```

The next code block requests permission for notifications when a button is clicked:

```js
const button = document.getElementById("notifications");
button.addEventListener("click", () => {
  Notification.requestPermission().then((result) => {
    if (result === "granted") {
      randomNotification();
    }
  });
});
```

The last block creates notifications that display a randomly-selected item from the games list:

```js
function randomNotification() {
  const randomItem = Math.floor(Math.random() \* games.length);
  const notifTitle = games[randomItem].name;
  const notifBody = `Created by ${games[randomItem].author}.`;
  const notifImg = `data/img/${games[randomItem].slug}.jpg`;
  const options = {
    body: notifBody,
    icon: notifImg,
  };
  new Notification(notifTitle, options);
  setTimeout(randomNotification, 30000);
}
```

### The service worker

The last file we will quickly look at is the service worker: sw.js — it first imports data from the games.js file:

```js
self.importScripts("data/games.js");
```

Next, it creates a list of all the files to be cached, both from the app shell and the content:

```js
const cacheName = "js13kPWA-v1";
const appShellFiles = [
  "/pwa-examples/js13kpwa/",
  "/pwa-examples/js13kpwa/index.html",
  "/pwa-examples/js13kpwa/app.js",
  "/pwa-examples/js13kpwa/style.css",
  "/pwa-examples/js13kpwa/fonts/graduate.eot",
  "/pwa-examples/js13kpwa/fonts/graduate.ttf",
  "/pwa-examples/js13kpwa/fonts/graduate.woff",
  "/pwa-examples/js13kpwa/favicon.ico",
  "/pwa-examples/js13kpwa/img/js13kgames.png",
  "/pwa-examples/js13kpwa/img/bg.png",
  "/pwa-examples/js13kpwa/icons/icon-32.png",
  "/pwa-examples/js13kpwa/icons/icon-64.png",
  "/pwa-examples/js13kpwa/icons/icon-96.png",
  "/pwa-examples/js13kpwa/icons/icon-128.png",
  "/pwa-examples/js13kpwa/icons/icon-168.png",
  "/pwa-examples/js13kpwa/icons/icon-192.png",
  "/pwa-examples/js13kpwa/icons/icon-256.png",
  "/pwa-examples/js13kpwa/icons/icon-512.png",
];
const gamesImages = [];
for (let i = 0; i < games.length; i++) {
  gamesImages.push(`data/img/${games[i].slug}.jpg`);
}
const contentToCache = appShellFiles.concat(gamesImages);
```

The next block installs the service worker, which then actually caches all the files contained in the above list:

```js
self.addEventListener("install", (e) => {
  console.log("[Service Worker] Install");
  e.waitUntil(
    (async () => {
      const cache = await caches.open(cacheName);
      console.log("[Service Worker] Caching all: app shell and content");
      await cache.addAll(contentToCache);
    })(),
  );
});
```

Last of all, the service worker fetches content from the cache if it is available there, providing offline functionality:

```js
self.addEventListener("fetch", (e) => {
  e.respondWith(
    (async () => {
      const r = await caches.match(e.request);
      console.log(`[Service Worker] Fetching resource: ${e.request.url}`);
      if (r) {
        return r;
      }
      const response = await fetch(e.request);
      const cache = await caches.open(cacheName);
      console.log(`[Service Worker] Caching new resource: ${e.request.url}`);
      cache.put(e.request, response.clone());
      return response;
    })(),
  );
});
```

### The JavaScript data

The games data is present in the data folder in a form of a JavaScript object (games.js):

```js
const games = [
  {
    slug: "lost-in-cyberspace",
    name: "Lost in Cyberspace",
    author: "Zosia and Bartek",
    twitter: "bartaz",
    website: "",
    github: "github.com/bartaz/lost-in-cyberspace",
  },
  {
    slug: "vernissage",
    name: "Vernissage",
    author: "Platane",
    twitter: "platane\_",
    website: "github.com/Platane",
    github: "github.com/Platane/js13k-2017",
  },
  // ...
  {
    slug: "emma-3d",
    name: "Emma-3D",
    author: "Prateek Roushan",
    twitter: "",
    website: "",
    github: "github.com/coderprateek/Emma-3D",
  },
];
```

Every entry has its own image in the data/img folder. This is our content, loaded into the content section with JavaScript.

Next up
-------

In the next article we will look in more detail at how the app shell and the content are cached for offline use with the help from the service worker.

* Previous
* Overview: js13kGames
* Next


CycleTracker: Manifest and iconography - Progressive web apps

CycleTracker: Manifest and iconography
======================================

* Previous
* Overview: CycleTracker
* Next

A PWA manifest file is a JSON file that provides information about the features of that app to make it look and behave like a native app when installed on the user's device. The manifest contains metadata for your app, including its name, icons, and presentational directives.

While according to the spec, all of the manifest keys, or members, are optional, some browsers, operating systems, and app distributors have require specific members for a web app to be a PWA. By including a name or short name, the start URL, an icon meeting some minimum requirements, and the type of application viewport in which the PWA should be viewed, your app will meet the manifest requirements of a PWA.

A minimalist manifest file for our menstrual cycle tracking app could look like this:

```js
{
  "short\_name": "CT",
  "start\_url" : "/",
  "icons": [
    {
      "src": "icon-512.png",
      "sizes": "512x512"
    }
  ],
  "display": "standalone"
}
```

Before saving the manifest file and linking to it from our HTML file, we can develop a still brief but more informative JSON object to define the identity, presentation, and iconography of the PWA. Yes, the above would work, but let's discuss the members in this example and a few other members that enable manifest files to better define the appearance of our CycleTracker PWA.

App identity
------------

To identify your PWA, the JSON must include a `name` or `short_name` member, or both, to define the PWA name. It can also include a `description`.

`name`

The name of the PWA. This is the name used when the operating system lists applications, as the label next to the application icon, etc.

`short_name`

The name of the PWA displayed to the user if there isn't enough space to display the `name`. It is used as the label for icons on phone screens, including in the "Add to Home Screen" dialog on iOS.

When both the `name` and `short_name` are present, the `name` is used in most instances, with the `short_name` used when there is a limited space to display the application name.

`description`

Explanation of what the application does. It provides an accessible description of the application's purpose and function.

### Task

Write the first few lines of your manifest file. You can use the text below, or more discreet or descriptive values, and a description of your choosing.

### Example solution

```js
{
  "name": "CycleTracker: Period Tracking app",
  "short\_name": "CT",
  "description": "Securely and confidentially track your menstrual cycle. Enter the start and end dates of your periods, saving your private data to your browser on your device, without sharing it with the rest of the world."
}
```

App presentation
----------------

The appearance, or presentation, of a PWA's installed and offline experiences are defined in the manifest. Presentation manifest members include `start_url` and `display`, and members which can be used to customize your app colors, including `theme_color` and `background_color`.

`start_url`

The start page when a user launches the PWA.

`display`

Controls the app's display mode including `fullscreen`, `standalone`, which displays the PWA as a standalone application, `minimal-ui`, which is similar to a standalone view but with UI elements for controlling navigation, and `browser`, which opens the app in a regular browser view.

There is also an `orientation` member that defines the PWA's default orientation as `portrait` or `landscape`. As our app works well in both orientations, we'll omit this member.

### Colors

`theme_color`

The default color of operating system and browser UI elements such as the status bar on some mobile experiences and the application title bar on desktop operating systems.

`background_color`

A placeholder color to be displayed as the background of the app until the CSS is loaded. To create a smooth transition between app launch and load, it is recommended to use the `<color>` declared as the app's `background-color` color.

### Task

Add presentation definitions to the manifest file you began creating in the previous task.

### Example solution

As the example application is a single page, we can use `"/"` as the `start_url`, or omit the member altogether. For that same reason, we can display the app without the browser UI by setting the `display` to `standalone`.

In our CSS, the `background-color: #efe;` is set on the `body` element selector. We use `#eeffee` to ensure a smooth transition from placeholder appearance to app load.

```js
{
  "name": "...",
  "short\_name": "...",
  "description": "...",
  "start\_url": "/",
  "theme\_color": "#eeffee",
  "background\_color": "#eeffee",
  "display": "standalone"
}
```

App iconography
---------------

PWA icons help users identify your app, make it more visually appealing, and improve discoverability. The PWA app icon appears on home screens, app launchers, or app store search results. The size of the rendered icon and the file requirements varies depending on where it is displayed and by whom. The manifest is where you define your images.

Within the manifest JSON object, the `icons` member specifies an array of one or more icon objects for use in different contexts, each with a `src` and `sizes` member, and optional `type` and `purpose` members. Each icon object's `src` list the source of a single image file. The `sizes` member provides a list of space-separated sizes for which that particular image should be used or the keyword `any`; the value is the same as the `<link>` element's `sizes` attribute. The `type` member lists the image's MIME type.

```js
{
  "name": "MyApp",
  "icons": [
    {
      "src": "icons/tiny.webp",
      "sizes": "48x48"
    },
    {
      "src": "icons/small.png",
      "sizes": "72x72 96x96 128x128 256x256",
      "purpose": "maskable"
    },
    {
      "src": "icons/large.png",
      "sizes": "512x512"
    },
    {
      "src": "icons/scalable.svg",
      "sizes": "any"
    }
  ]
}
```

All icons should have the same look and feel to ensure users recognize your PWA, but the larger the icon, the greater the detail it can contain. While all icon files are squares, some operating systems render different shapes, cutting sections off, or "masking" the icon, to meet the UI, or shrinking and centering the icon with a background if the icon is not maskable. The safe zone, the area that will render okay if the icon is masked as a circle, is the inner 80% of the image file. Icons are labeled as safe to be masked by the `purpose` member which, when set to `maskable`, defines the icon as adaptive.

In Safari, and therefor for iOS and iPadOS, if you include the non-standard `apple-touch-icon` in the `<head>` of the HTML document via `<link>`, they will take precedence over manifest-declared icons.

### Task

Add the icons to the manifest file you have been constructing.

Playing with the words "cycle" and "period" of CycleTracker and the green theme color we've chosen, our icon images could all be light green squares with a green circle. Our smallest size `circle.ico`, and icon file that is just a circle representing the period punctuation mark and app theme color, with our in-between images, `circle.svg`, `tire.svg`, and `wheel.svg`, adding more detail moving from a plain circle to a tire as it gets larger, with our largest icons being a detailed wheel with spokes and shadows. That said, designing icons is beyond the scope of this tutorial.
```

<div>
  <img alt="a green circle" src="circle.svg" role="img" />
  <img alt="a simple wheel" src="tire.svg" role="img" />
  <img alt="a detailed wheel" src="wheel.svg" role="img" />
</div>
```

```
div {
  display: flex;
  gap: 5px;
}
img {
  width: 33%;
}
```

### Example solution

```js
{
  "name": "...",
  "short\_name": "...",
  "description": "...",
  "start\_url": "...",
  "theme\_color": "...",
  "background\_color": "...",
  "display": "...",
  "icons": [
        {
      "src": "circle.ico",
      "sizes": "48x48"
    },
    {
      "src": "icons/circle.svg",
      "sizes": "72x72 96x96",
      "purpose": "maskable"
    },
    {
      "src": "icons/tire.svg",
      "sizes": "128x128 256x256"
    },
    {
      "src": "icons/wheel.svg",
      "sizes": "512x512"
    }
  ]
}
```

Adding the manifest to the app
------------------------------

You now have a fully usable manifest file. Time to save it and link to it from our HTML file.

The manifest file extension can be the specification suggestion `.webappmanifest`. However, being a JSON file, it is most commonly saved with the browser-supported `.json` extension.

PWAs require a manifest file to be linked from the app's HTML document. We have a fully functional app, but it's not yet a PWA because it doesn't link to our external manifest JSON file yet. To include the external JSON resource, we use the `<link>` element, with the `rel="manifest"` attribute, and set the `href` attribute to the location of the resource.

```html
<link rel="manifest" href="cycletracker.json" />
```

The `<link>` element is most commonly used to link to stylesheets and, with PWAs, the required manifest file, but is also used to establish site icons (both "favicon" style icons and icons for the home screen and apps on mobile devices) among other things.

```html
<link rel="icon" href="icons/circle.svg" />
```

When using the `.webmanifest` extension, set `type="application/manifest+json"` if your server doesn't support that MIME type.

### Task

Save the manifest file that you have created in the steps above, then link to it from the `index.html` file.

Optionally, link to a shortcut icon from your HTML as well.

### Example solution

The `<head>` of `index.html` may now look similar to:

```html
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width" />
  <title>Cycle Tracker</title>
  <link rel="stylesheet" href="style.css" />
  <link rel="manifest" href="cycletracker.json" />
  <link rel="icon" href="icons/circle.svg" />
</head>
```

View the `cycletracker.json` file and view the project source code on GitHub.

With a manifest file, Safari will recognize your site as a web app. For the web app to be a full PWA, we will still need to add a service worker.

Debugging manifest files
------------------------

Some browser developer tools provide insight into the app manifest. In Edge, Firefox, and Chrome developer tools, the manifest members and their values are visible under the "Application" panel.

![In the developer tools, the left panel includes links to the manifest. The right side reads App Manifest, with the file name as a link to the JSON file.](/en-US/docs/Web/Progressive_web_apps/Tutorials/CycleTracker/Manifest_file/debugger_devtools.jpg)

The Manifest App pane provides the name of the manifest file as a link, and identity, presentation, and icons sections.

![The identity and presentation manifest members along with values, if present.](/en-US/docs/Web/Progressive_web_apps/Tutorials/CycleTracker/Manifest_file/manifest_identity_and_presentation.jpg)

Supported manifest members are displayed, along with all included values. In this screenshot, while we did not include the `orientation` or `id` members, they are listed. The App panel can be used to see the manifest members and even learn: in this example, we learn that to specify an App Id that matches the current identity, set the `id` field to "/".

![Installability shows that because we don't have a service worker, our app is not an installable PWA. yet.](/en-US/docs/Web/Progressive_web_apps/Tutorials/CycleTracker/Manifest_file/manifest_installability.jpg)

Chrome and Edge also provide errors and warnings, protocol handlers, and information to improve the manifest and icons.

Our web app doesn't have any protocol handlers; a topic not covered in this tutorial. Had we included some, they would be found under "Protocol Handlers". As that section is empty, the developer tools link to more information on the topic.

![The four icons included in the Manifest file, with the background removed as "show only the minimum safe area for maskable icons is checked.](/en-US/docs/Web/Progressive_web_apps/Tutorials/CycleTracker/Manifest_file/manifest_icons.jpg)

The manifest panel also includes insight into the safe area for maskable icons and a link to a PWA image generator. This tool creates over 100 square PNG images for Android, Apple OSs, and Windows, as well as a JSON object listing all the images and their sizes. The images produced may not serve your needs, but the list of image sizes produced for each OS demonstrates the diversity of where and how PWAs can be served.

The developer tools are useful in identifying which manifest members are supported. Note the Firefox developer tools have entries for `dir`, `lang`, `orientation`, `scope`, and `id`, even though our manifest file did not include these members. Firefox also includes the value of the `purpose` member for each icon, displaying `any` if no the purpose is not explicitly set.

![The Manifest panel in Firefox developer tools, showing values for the not included dir, scope, and id members, and the lang and orientation members without associated values.](/en-US/docs/Web/Progressive_web_apps/Tutorials/CycleTracker/Manifest_file/manifest_firefox.jpg)

Up next
-------

To get the PWA benefits from other browsers and all operating systems that support PWAs, we need to add a service worker, which we'll do without using a framework.

* Previous
* Overview: CycleTracker
* Next