An install button should appear only when the browser decides the Web App is eligible to be installed. That signal is the beforeinstallprompt event, which fires once the app satisfies the installability criteria. Capturing the event gives you control over timing: instead of letting the browser prompt at an arbitrary moment, you hold onto the event and trigger installation when it suits the user.

Capture the event first

Until the event arrives, there is nothing to prompt with and no reason to show an install button. So the first step is to attach a handler that stores the event and reveals the button that was previously hidden.

Roughly four edits are involved:

  1. Click Remix to Edit to make the project editable.
  2. Add a beforeinstallprompt event handler to the window object.
  3. Save the event as a global variable; we'll need it later to show the prompt.
  4. Unhide the install button.

Code:

window.addEventListener('beforeinstallprompt', (event) => {
  // Prevent the mini-infobar from appearing on mobile.
  event.preventDefault();
  console.log('๐Ÿ‘', 'beforeinstallprompt', event);
  // Stash the event so it can be triggered later.
  window.deferredPrompt = event;
  // Remove the 'hidden' class from the install button container.
  divInstall.classList.toggle('hidden', false);
});

Trigger the prompt from a user gesture

Showing the prompt itself means calling prompt() on the saved beforeinstallprompt event. The call has to happen inside a user gesture, which is why it belongs in the install button's click handler rather than in the handler that captured the event.

  1. Add a click event handler for the install button.
  2. Call prompt() on the saved beforeinstallprompt event.
  3. Log the results of the prompt.
  4. Set the saved beforeinstallprompt event to null.
  5. Hide the install button.

Code:

butInstall.addEventListener('click', async () => {
  console.log('๐Ÿ‘', 'butInstall-clicked');
  const promptEvent = window.deferredPrompt;
  if (!promptEvent) {
    // The deferred prompt isn't available.
    return;
  }
  // Show the install prompt.
  promptEvent.prompt();
  // Log the result
  const result = await promptEvent.userChoice;
  console.log('๐Ÿ‘', 'userChoice', result);
  // Reset the deferred prompt variable, since
  // prompt() can only be called once.
  window.deferredPrompt = null;
  // Hide the install button.
  divInstall.classList.toggle('hidden', true);
});

Account for every install path

Your button is not the only route to installation. Users can also install through Chrome's menu, the mini-infobar, or an icon in the omnibox. The appinstalled event on the window object covers all of them, so it's the right place to record an install regardless of how it started.

  1. Add an appinstalled event handler to the window object.
  2. Log the install event to analytics or other mechanism.

Code:

window.addEventListener('appinstalled', (event) => {
  console.log('๐Ÿ‘', 'appinstalled', event);
  // Clear the deferredPrompt so it can be garbage collected
  window.deferredPrompt = null;
});

Where to go next

Your app is now installable. Two related behaviors are worth adding afterwards: