PWA Install Prompt

Progressive web apps (PWA) are becoming increasingly popular and with the recent announcement that Chrome 73 now supports Desktop PWAs on all desktop platforms let’s see how we can make our users aware of this feature. Today I’m going to show you how to prompt the user to install your PWA or give them the option to dismiss.

First let’s create some basic HTML markup that we will use for the banner:

<div class="prompt">
  <p>Do you want to add this site to your home screen?</p>
  <button type="button" class="prompt__install">Yes Please</button>
  <button type="button" class="prompt__close">No Thanks</button>
</div>

We don’t want the banner to show by default, so let’s hide it:

.prompt {
  display: none;
}

To show the banner we are going to use the BeforeInstallPromptEvent which automatically gets fired on the window when a PWA has been detected. As per the MDN description:

The BeforeInstallPromptEvent is fired at the Window.onbeforeinstallprompt handler before a user is prompted to “install” a web site to a home screen on mobile.

First let’s setup some variables and functions that we will use later on:

const prompt = document.querySelector('.prompt');
const installButton = prompt.querySelector('.prompt__install');
const closeButton = prompt.querySelector('.prompt__close');
let installEvent;

// checks if the localStorage item has been set
function getVisited() {
  return localStorage.getItem('install-prompt');
}

// sets the localStorage item
function setVisited() {
  localStorage.setItem('install-prompt', true);
}

Here we are setting some variables which point to our banner and the buttons. To ensure the banner does not show up again after the user has interacted with it, we are using localStorage to store a value which we will use later on.

Now we will add in the BeforeInstallPromptEvent:

// this event will only fire if the user does not have the pwa installed
window.addEventListener('beforeinstallprompt', (event) => {
  event.preventDefault();

  // if no localStorage is set, first time visitor
  if (!getVisited()) {
    // show the prompt banner
    prompt.style.display = 'block';

    // store the event for later use
    installEvent = event;
  }
});

This event will automatically get fired on the window when the browser detects a PWA is present. There are also extra conditions depending on the browser being used, for example some browsers only fire this event after the user has interacted with the website for more than 30 seconds.

Here we check if the user is a first time visitor by checking if the localStorage item has been set, as we don’t want to show the banner if the user has already closed it out.

If the user is a first time visitor we show the banner and store the event for later use.

Next we can add an event that will fire when the “Yes Please” button within the banner is clicked:

installButton.addEventListener('click', () => {
  // hide the prompt banner
  prompt.style.display = 'none';

  // trigger the prompt to show to the user
  installEvent.prompt();

  // check what choice the user made
  installEvent.userChoice.then((choice) => {
    // if the user declined, we don't want to show the button again
    // set localStorage to true
    if (choice.outcome !== 'accepted') {
      setVisited();
    }

    installEvent = null;
  });
});

Here we hide the banner and trigger the browser prompt to show which gives the user 2 choices. They can either install or cancel. Below is an example of how this looks on Chrome Desktop.

If the user selects install, no action is required, we just set the installEvent back to null. Once installed the BeforeInstallPromptEvent won’t fire and thus the prompt will never show again until it is uninstalled.

If the user selects cancel, we run the setVisited() function which sets our localStorage item to true so that the prompt banner will not show up again – as this would be annoying for the user.

Finally we can add an event that will fire when the “No Thanks” button within the banner is clicked:

closeButton.addEventListener('click', () => {
  // set localStorage to true
  setVisited();

  // hide the prompt banner
  prompt.style.display = 'none';  

  installEvent = null;
});

When the button is clicked this will run the setVisited() function which sets our localStorage item to true, hides the banner and finally sets the installEvent back to null.

Putting it all together the full script should now look like:

const prompt = document.querySelector('.prompt');
const installButton = prompt.querySelector('.prompt__install');
const closeButton = prompt.querySelector('.prompt__close');
let installEvent;

function getVisited() {
  return localStorage.getItem('install-prompt');
}

function setVisited() {
  localStorage.setItem('install-prompt', true);
}

// this event will only fire if the user does not have the pwa installed
window.addEventListener('beforeinstallprompt', (event) => {
  event.preventDefault();

  // if no localStorage is set, first time visitor
  if (!getVisited()) {
    // show the prompt banner
    prompt.style.display = 'block';

    // store the event for later use
    installEvent = event;
  }
});

installButton.addEventListener('click', () => {
  // hide the prompt banner
  prompt.style.display = 'none';

  // trigger the prompt to show to the user
  installEvent.prompt();

  // check what choice the user made
  installEvent.userChoice.then((choice) => {
    // if the user declined, we don't want to show the button again
    // set localStorage to true
    if (choice.outcome !== 'accepted') {
      setVisited();
    }

    installEvent = null;
  });
});

closeButton.addEventListener('click', () => {
  // set localStorage to true
  setVisited();

  // hide the prompt banner
  prompt.style.display = 'none';  

  installEvent = null;
});

That’s a wrap! I find this a quick solution that isn’t obtrusive to the user giving them full control to install or dismiss the prompt.

You can check this out as a working example on my movies progressive web app.