53

I understand that with a properly made Progressive Web App mobile browsers will display a banner prompting users to 'Install' the app on their home screen.

I have been looking around for a way to trigger that prompt from within the app, but have not been able to find anything.

Is there a line of JavaScript that can be used to call the install prompt banner at any time?? Something that I could add to a install button tucked away in a help screen for instance?

It might be difficult for some users to find the "Add to Home Screen" option if they missed the install banner prompt. I'd like to give them a button they can click to be prompted again.

2020 EDIT: Yes, this is possible in Chrome - see answer below

See this great article: How to provide your own in-app install experience and my working demo of the article's process applied in a React app.

Or for a slightly different approach, see how snapdrop.net did it.

Adam D
  • 1,962
  • 2
  • 21
  • 37
  • What type of device are you using to test? – Mathias Rechtzigel May 14 '18 at 14:22
  • 1
    Here's some useful documentation that you may want to review: https://developers.google.com/web/fundamentals/app-install-banners/ – Mathias Rechtzigel May 14 '18 at 14:23
  • @MathiasRechtzigel Thank you, that's very useful. As this documentation and Anand's answer below explained, unfortunately it seem that it's not possible to do what I imagined, which was create a link to prompt an install at any time. The prompt only comes up once, whether naturally or caught and then released from a button press etc. – Adam D May 16 '18 at 22:14
  • any solution for only safari on iphone? – huykon225 May 20 '19 at 03:37
  • No, it is not possible. – Weihang Jian Oct 17 '20 at 12:11
  • 1
    As [this great article from web.dev](https://web.dev/customize-install/) explains, this is totally possible now. – Adam D Nov 07 '20 at 11:28

8 Answers8

40

Chrome(or any PWA supporting browser) triggers the beforeinstallprompt event for Web app install banner, which you can catch and re-trigger in more appropriate time when you think user wont miss it/convinced about adding your site to home page. Starting Chrome version 68, catching beforeinstallprompt and handling the install prompt programmatically is mandatory and no banners will be shown automatically.

In case the user have missed the prompt/declined to add to home screen, the event can't be manually triggered by our code. This is intentionally left that way to avoid web pages annoying the users to repeatedly prompt the user for adding to home screen. Thinking of users perspective, this makes complete sense.

Yes, there are cases when user miss the option accidentally and he may not know of the browser menu option to "Add to home screen" and it would be nice for us to trigger the same again. But unfortunately, that's not an option. At lest for now and I personally don't see that changing much considering how developers can abuse if its left to the developers to prompt.

Alternate option: If the user have missed the install prompt or even chosen not to install it to home screen, give some time and when you think he is starting to like your site(based on conversions) you can show him a full page or half page Div popup kind of install instructions to add your site to home screen from browsers menu. It can have some images or Gif animation showing user how to add to home screen from the menu. With that, it should be self explanatory to most users, if not all.

Here is some code example for the same, which is iOS specific(look under #PROTIP 3).

As a bonus, you can show some promotions like discounts or added features when user add to home screen, which will convince user to do so. PWA has a way to find if the site is accessed form the home screen or browser.

For Development/testing: If you need this banner to come multiple times for dev/testing purpose, you can set the below flow in your Chrome for the same,

chrome://flags/#bypass-app-banner-engagement-checks
Anand
  • 9,672
  • 4
  • 55
  • 75
  • 2
    May I know how would I know if user has already installed PWA on mobile or not? – Pardeep Jain Jan 30 '19 at 06:47
  • 1
    @PardeepJain If you set the `start_url` to something like `/?utm_source=pwa`, you should be able to check programmatically whether the user opened the app as a PWA (though not if it's installed but opened from a regular browser.) – Fabien Snauwaert Aug 02 '19 at 17:33
  • 1
    Browsers should give users the option to permanently disable a prompt. So if an app is abusing the prompt, users can disable it, and if a user missed the prompt, the app can still trigger it again. I think that's a nice balance. – xyres Aug 18 '19 at 10:34
  • 1
    A way to check whether the user is using the standalone app: `if (window.matchMedia('(display-mode: standalone)').matches) { … }`. [See docs](https://developers.google.com/web/fundamentals/app-install-banners#detect-mode) – Fabien Snauwaert Jan 23 '20 at 13:12
  • All seems to be in order while checked with Lighthouse but the prompt isn't triggered on Android/Chrome at https://cambio.express - any guess why? Thank you! – Yatko Jan 31 '21 at 23:21
  • @Yatko I could able to add to home screen manually and it's working as expected. But didn't get the prompt though. May be chrome didn't feel the site is significant enough user would want to install. There was some such criteria which is not very explicit on how chrome assess it. Will check lighthouse tomorrow to see if I find anything. – Anand Feb 02 '21 at 01:25
  • @Anand makes great sense, offering to install every page that has a PWA functionality would trigger a horde of sites spamming us with "Install {app}" prompts. Most likely you are right, pages with low significance simply won't trigger the prompt. We were thinking about two other [requirements](https://web.dev/install-criteria/): Fallback page and decent internet speed (we don't have the first and only have 3G in Cuba) ... if anyone can trigger the [PWA install prompt](https://cambio.express), please let us know. Thank you! – Yatko Feb 02 '21 at 21:20
16

Thanks for all the great answers. Recently it appears that things have become more standardized, and there is a solid way of doing things in chrome at least that allows users to click and install the app even if they missed the prompt etc.

Pete LePage wrote a very clear article on web.dev called How to provide your own in-app install experience which details the process. The code snippets and process is taken directly from the article.

  1. Listen for the beforeinstallprompt event.
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();
});
  1. Save the beforeinstallprompt event, so it can be used to trigger the install flow later.
buttonInstall.addEventListener('click', (e) => {
  // Hide the app provided install promotion
  hideMyInstallPromotion();
  // Show the install prompt
  deferredPrompt.prompt();
  // Wait for the user to respond to the prompt
  deferredPrompt.userChoice.then((choiceResult) => {
    if (choiceResult.outcome === 'accepted') {
      console.log('User accepted the install prompt');
    } else {
      console.log('User dismissed the install prompt');
    }
  });
});

  1. Alert the user that your PWA is installable, and provide a button or other element to start the in-app installation flow.

For more details and other capabilities, see the full article.

I made a working demo of a this process implemented in React. (source code)

Also, for a slightly different approach, you can see how the maker(s) of Snapdrop implemented an install button in the source code.

Adam D
  • 1,962
  • 2
  • 21
  • 37
  • Hi, please share an agnostic pwa install button example in pure js + css + htm. I still +1ed this answer ;) – Hugolpz Dec 02 '20 at 15:54
  • Following the above approach, I get the following error on clicking the button again after dismissing the prompt `Uncaught (in promise) DOMException: Failed to execute 'prompt' on 'BeforeInstallPromptEvent': The prompt() method must be called with a user gesture at HTMLButtonElement.` – murtuzaali_surti Apr 24 '23 at 11:12
  • the issue mentioned by me in this [comment](https://stackoverflow.com/questions/50332119/is-it-possible-to-make-an-in-app-button-that-triggers-the-pwa-add-to-home-scree#comment134193465_64727286) was finally fixed by a combination of both yours and the following reference https://web.dev/codelab-make-installable/ – murtuzaali_surti Apr 27 '23 at 07:23
6

Well, there's nothing stopping you from storing the deferredPrompt value in a variable and use it later. Then you can popup a custom box, and if the user closes it down, you still haven't ran deferredPrompt.prompt() and can use it later. I store mine in Redux. If the user closes the install prompt, I use the deferredPrompt in the hamburger menu to install it at any time.

Listen to install and store event as prompt

 const beforeInstallListener = e => {
        // Prevent Chrome 76 and later from showing the install prompt
        e.preventDefault();
        // Stash the event so it can be triggered later.
        dispatch(setAndroidDeferredPrompt(e));
        dispatch(showAndroidPromptDownload(true));
    };
    window.addEventListener("beforeinstallprompt", beforeInstallListener);

Create a function which uses this deferredPrompt to prompt the user

    const installToAndroid = async () => {
    dispatch(showAndroidPromptDownload(false));
    deferredPrompt.prompt(); // Wait for the user to respond to the prompt
    const choiceResult = await deferredPrompt.userChoice;

    if (choiceResult.outcome === "accepted") {
        console.log("User accepted the PWA prompt");
    } else {
        closeAndroidPromptWithoutDownload();
        console.log("User dismissed the PWA prompt");
    }
    dispatch(setAndroidDeferredPrompt(null));
};

Remember, if the user clicks the banner to install, deferredPrompt.prompt() is invoked and it won't work anymore. Therefore I keep a check to see if deferredPrompt exists, and remember to set it to null if the user has invoked .prompt().

{deferredPrompt && <button onClick={() => installPWA(deferredPrompt)}>Installer til hjemskjerm</button>}

This was done with React Hooks, Redux and LocalStorage (where I store a pwa.reducer.js state)

jeyloh
  • 81
  • 1
  • 4
3

In mobile Chrome on Android, "Add to Home Screen" can be accessed from the browser's menu. (Similar options may exist for mobile Safari/Firefox on Android/iOS as well.) The web app manifest file is read and the app is added as it would be with the original prompt feature.

While JavaScript cannot be used to manually invoke the prompt, a workaround would be to provide on-screen instructions showing users how to manually open the menu and add for their specific user-agent.

enter image description here

enter image description here

anthumchris
  • 8,245
  • 2
  • 28
  • 53
2

There are events that browsers are now firing that help with the issue of handling installation of PWA's. It's not a web standard YET, but it's a good approach.

  • 'beforeinstallprompt' is fired right before the prompt is shown and
  • 'appinstalled' is fired right after the installation is complete.

So you can intercept 'beforeinstallprompt' event, prevent it from happening so that the banner doesn't show up, stash it for when you want the banner to show up, and finally create a button that a user clicks to then fire the earlier deferred event at will.

Below is a link about how to achieve this:

https://web.dev/installable/discover-installable/codelab-make-installable

Emmanuel Mahuni
  • 1,766
  • 16
  • 16
2

In Dev mode,

Try this in devtools console to trigger the event:

event = new Event('beforeinstallprompt')
window.dispatchEvent(event)
Anika
  • 160
  • 2
  • 10
1

If you accept external services, I bumped into https://progressier.com. It's a service assisting you to create pwa for your webapp, by :

  1. filling a form
  2. uploading the 512px icon

Since I know how to create PWA I was just testing, then I remembered the following:

  • normal PWA will let the browser's handle the interaction, which is notoriously barely noticeable
  • an "Install me" button is therefor needed but requires to be confident in your cross-browser javascript
  • an installs counter would be welcome

Progressier.com provide solutions to this. The generate the code spinet for PWA, PWA install button, and PWA installations counter.

Limitations: the free service is limited to 50k pageviews.

Source

Hugolpz
  • 17,296
  • 26
  • 100
  • 187
0

This as of April 2023.

Put your button wherever you want, but keep it invisible for now. Then we will display it if necessary.

<button type=button id=installbutton style='display:none'>Install</button>

Regardless of our logic to display the button, we will always hide it if the app is opened in standalone mode. We can do this using css, and so this will essentially override our hide/show logic. This works in all browsers. That's the easy part.

@media all and (display-mode: standalone) {
  #installbutton {display:none}
}

So with the css in place, the following only applies when the app is opened in a browser, unfortunately, in a browser there's no way to know for sure if the app is installed. Saving the install state in localStorage or a cookie is pointless because those can be deleted and then your app knows nothing. (Plus see point #4 below)

The best we can do is check the 'beforeinstallprompt' event. If we can add a listener to this event then that means the app is not yet installed, and the device is not iOS. Those are the only two things we know for sure, but it could also be another browser that doesn't support it, like Firefox on Android. (In my code I'm not addressing Firefox on Android)

For iOS, instead of prompting for the install, we will popup a message with instructions on how to install manually.

      var prompt;
      var beforeinstallpromptsupport = false;
      
      window.addEventListener('beforeinstallprompt', function(e){
        // We now know the app is not yet installed and this is not ios
        // Note: If the app is installed through Edge on Android, this event will still fire in Edge.  
        // If however the app is installed through Chrome on Android, this event will not fire in Edge.  
        // So it appears that edge only consider the app installed if it is installed through Chrome.  Sigh.
        beforeinstallpromptsupport = true;
    
        // Prevent the mini-infobar from appearing on mobile
        e.preventDefault();
    
        // Show button
        $('#installbutton').show();
    
        // Bind native install prompt to click of button
        // Then hide button so it can not be clicked again (or if you prefer, disable it)
        $('#installbutton').on('click', function(){
           e.prompt();
           $('#installbutton').hide();
        })
      });
      
      function alternatePrompt(){
    
        // This function only matters if we can not bind the native prompt to the 'beforeinstallprompt' event.
        // So this code only runs if we are on iOS or if the app is already installed
        if(!beforeinstallpromptsupport){
    
          // Detects if device is on iOS 
          const ios = () => {
            const userAgent = window.navigator.userAgent.toLowerCase();
            return /iphone|ipad|ipod/.test(userAgent);
          }
    
          if (ios()){
            // Show button
            $('#installbutton').show();
    
            // Change what we do on click of our button
            // ie. Instead of a native install prompt, we popup instructions for a manual install
            $('#installbutton').off('click').on('click', function(){
              promptPopup('iosinstructions');
            })
    
          }else{
            // This is not iOS and the beforeinstallprompt event is not supported
            // Therefore, app is likely already installed, so do not display button
          }
        }
      }

  // Check for event support after half a second (Without this delay our code will not have had the chance to set 'beforeinstallpromptsupport')
  window.setTimeout(alternatePrompt,500);

Note 1: This does not handle Firefox on Android, nor I'm sure other less popular browsers.

Note 2: This was not tested on Mac.

Note 3: iOS will always show the button because there is no way to tell if it is installed or not.

Note 4: The reason I don't keep track of installed state in localStorage is because if the 'beforeinstallprompt' event exists, that event only executes if the app is not yet installed, so that is a better method of determination. And when the event does not exist (iOS) there's no good time to set the localStorage as there's no way to know when the app was installed (unless you want to assume the app was installed when the user clicked the button).

Note 5: My code "promptPopup('iosinstructions')" is based on the mobiscroll framework. You can generate a popup however you like.

Vincent
  • 1,741
  • 23
  • 35