![Pete LePage](https://web.dev/images/authors/petelepage.jpg) Pete LePage [X](https://twitter.com/petele) [GitHub](https://github.com/petele) [Mastodon](https://techhub.social/@petele) [Homepage](https://petelepage.com/)

<br />


Published: February 14, 2020

<br />

Many browsers let you enable and promote the installation of your Progressive
Web App (PWA) directly within its user interface. Installation (formerly
referred to as Add to Home Screen) lets users add your PWA to their mobile or
desktop device. Installing a PWA adds it to a user's launcher, so it can run
like any other installed app.

In addition to the [browser-provided install experience](https://web.dev/articles/promote-install#browser-promotion),
you can provide your own custom install flow directly within your app.
![](https://web.dev/static/articles/customize-install/image/install-app-button-provid-c69a76f7a7ce5.png) The "Install App" button provided in the Spotify PWA.

When considering whether to promote installation, consider how users typically
use your PWA. For example, if there's a set of users who use your PWA multiple
times in a week, these users might benefit from the added convenience of
launching your app from a phone home screen or from the Start menu in a desktop
operating system. Some productivity and entertainment applications also benefit
from the extra screen space created by removing the browser toolbars from the
window in installed `standalone`, `minimal-ui`, or `window-control-overlay` modes.

## Prerequisites

Before getting started, make sure your PWA meets the
[installability requirements](https://web.dev/articles/install-criteria), which typically
include having a [web app manifest](https://web.dev/articles/customize-install/web.dev/add-manifest).

## Promote installation

To show that your Progressive Web App is installable, and to provide a custom
in-app install flow:

1. Listen for the `beforeinstallprompt` event.
2. Save the `beforeinstallprompt` event so it can trigger the install flow later.
3. Alert the user that your PWA is installable, and provide a button or other element to start the in-app installation flow.

> [!NOTE]
> **Note:** The `beforeinstallprompt` event and the `appinstalled` event have been moved from the web app manifest spec to their own [incubator](https://github.com/WICG/beforeinstallprompt). The Chrome team remains committed to supporting them, and has no plans to remove or deprecate support. Google's Web DevRel team continues to recommend using them to provide a customized install experience.

### Listen for the `beforeinstallprompt` event

If your Progressive Web App meets the required [installation criteria](https://web.dev/articles/install-criteria),
the browser fires a `beforeinstallprompt` event. Save a reference to the event,
and update your user interface to indicate that the user can install your PWA.

    // Initialize deferredPrompt for use later to show browser install prompt.
    let deferredPrompt;

    window.addEventListener('beforeinstallprompt', (e) => {
      // Prevent the mini-infobar from appearing on mobile
      e.preventDefault();
      // Stash the event so it can be triggered later.
      deferredPrompt = e;
      // Update UI notify the user they can install the PWA
      showInstallPromotion();
      // Optionally, send analytics event that PWA install promo was shown.
      console.log(`'beforeinstallprompt' event was fired.`);
    });

> [!NOTE]
> **Note:** There are many different [patterns](https://web.dev/articles/promote-install) that you can use to notify the user that your app can be installed and provide an in-app install flow. These can include a button in the header, an item in the navigation menu, or an item in your content feed.

### In-app installation flow

To provide in-app installation, provide a button or other interface element
that a user can click or tap to install your app. When the element is clicked or
tapped, call `prompt()` on the saved `beforeinstallprompt` event (stored in the
`deferredPrompt` variable). It shows the user a modal install dialog, asking
them to confirm that they want to install your PWA.

    buttonInstall.addEventListener('click', async () => {
      // Hide the app provided install promotion
      hideInstallPromotion();
      // Show the install prompt
      deferredPrompt.prompt();
      // Wait for the user to respond to the prompt
      const { outcome } = await deferredPrompt.userChoice;
      // Optionally, send analytics event with outcome of user choice
      console.log(`User response to the install prompt: ${outcome}`);
      // We've used the prompt and can't use it again, throw it away
      deferredPrompt = null;
    });

The `userChoice` property is a promise that resolves with the user's choice.
You can only call `prompt()` on the deferred event once. If the user
dismisses it, you'll need to wait until the `beforeinstallprompt` event
fires again, typically immediately after the `userChoice` property
has resolved.

> [!TIP]
> **Tip:** Make a site [installable](https://web.dev/articles/codelab-make-installable) with the `beforeinstallprompt` event.

## Detect when the PWA was successfully installed

You can use the `userChoice` property to determine whether the user installed
your app from within your user interface. But, if the user installs your PWA
from the address bar or another browser component, `userChoice` won't help.
Instead, you should listen for the `appinstalled` event, which fires whenever
your PWA is installed, no matter what mechanism is used to install it.

    window.addEventListener('appinstalled', () => {
      // Hide the app-provided install promotion
      hideInstallPromotion();
      // Clear the deferredPrompt so it can be garbage collected
      deferredPrompt = null;
      // Optionally, send analytics event to indicate successful install
      console.log('PWA was installed');
    });

## Detect how the PWA was launched

The CSS `display-mode` media query indicates how the PWA was launched, either in
a browser tab, or as an installed PWA. This makes it possible to apply different
styles depending on how the app was launched. For example, you can configure it
to always hide the install button and provide a back button when launched as an
installed PWA.

### Track how the PWA was launched

To track how users launch your PWA, use `matchMedia()` to test the
`display-mode` media query.

    function getPWADisplayMode() {
      if (document.referrer.startsWith('android-app://'))
        return 'twa';
      if (window.matchMedia('(display-mode: browser)').matches)
        return 'browser';
      if (window.matchMedia('(display-mode: standalone)').matches || navigator.standalone)
        return 'standalone';
      if (window.matchMedia('(display-mode: minimal-ui)').matches)
        return 'minimal-ui';
      if (window.matchMedia('(display-mode: fullscreen)').matches)
        return 'fullscreen';
      if (window.matchMedia('(display-mode: window-controls-overlay)').matches)
        return 'window-controls-overlay';

      return 'unknown';
    }

### Track when the display mode changes

To track if the user changes between `browser` and other display modes,
listen for changes to the `display-mode` media query.

    // Replace "standalone" with the display mode used in your manifest
    window.matchMedia('(display-mode: standalone)').addEventListener('change', () => {
      // Log display mode change to analytics
      console.log('DISPLAY_MODE_CHANGED', getPWADisplayMode());
    });

### Update UI based on the current display mode

To apply a different background color for a PWA when launched as an installed
PWA, use conditional CSS:

    @media all and (display-mode: standalone) {
      body {
        background-color: yellow;
      }
    }

## Update your app's icon and name

What if you need to update your app name, or provide new icons?
Check out [How Chrome handles updates to the web app manifest](https://web.dev/articles/manifest-updates)
to see when and how are those changes are reflected in Chrome.