← Back to Blog
Illustration of an Android phone home screen with a web app install banner showing a home icon and download arrow

Chrome Install Prompt Optimization: Trigger Timing, Custom Install Button and Why It Is Not Showing (2026)

Chrome install prompt optimization means three things: making sure your page passes Chrome's installability checks so beforeinstallprompt fires at all, capturing that event and replacing Chrome's default mini-infobar with your own install button shown at the right moment, and writing copy and placement that give the visitor a reason to accept. Below is how each part works, the settings that decide whether the prompt appears, and a diagnostic order for when it does not.

What you are actually optimizing

Chrome on Android does not show the install prompt because you asked it to. It fires a beforeinstallprompt event when the page meets its installability criteria and the visitor has shown some engagement, and only then can your code call prompt() to display the native dialog. Everything you can influence lives in three layers:

  1. Installability: manifest, HTTPS, service worker and the "not already installed" check. Fail these and nothing else matters.
  2. Trigger timing: when you call prompt() relative to what the visitor has just done.
  3. Presentation: the custom button or banner you show, its copy, where it sits, and what happens after the visitor accepts or dismisses.

If you need the platform background first, how the prompt differs between Android and iOS and what happens on each, our article on the add to home screen prompt covers that. This one is about tuning the Chrome path.

Chrome install prompt optimization starts with installability

Chrome's installability criteria, documented on web.dev and the Chrome for Developers site, are the gate. As of current Chrome releases the checklist is:

  • Served over HTTPS (localhost is exempt for development).
  • A web app manifest linked from the page with at least: name or short_name, start_url, display set to standalone, fullscreen or minimal-ui (or a display_override that includes one), and icons including a 192 by 192 and a 512 by 512 PNG.
  • prefer_related_applications absent or false. If it is true, Chrome steers users to the native app listed in related_applications instead.
  • The app is not already installed on the device.
  • A user engagement heuristic: the visitor has interacted with the origin for at least a short period and has at least tapped or clicked on the page. Chrome does not publish the exact numbers, and they change, but a page that fires nothing on first load with zero interaction is behaving as designed.

Two requirements that older guides still list have been relaxed. Chrome removed the "works offline" check several releases ago, and more recently relaxed the service worker requirement for installability. In practice you should still register a service worker: older Chrome versions on Android still check for it, other Chromium browsers may differ, and a service worker is what makes the installed app launch quickly and survive a bad connection.

A minimal manifest that passes:

{
  "name": "Example App",
  "short_name": "Example",
  "start_url": "/?source=pwa",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#0a84ff",
  "icons": [
    { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" }
  ]
}

Add a separate maskable icon entry if you want the launcher icon to fill Android's adaptive shape instead of sitting inside a white circle; that is a visible quality signal on the home screen, and it changes how much the installed icon looks like a real app.

Capturing beforeinstallprompt and building a custom install button

Chrome's default behavior on Android is a small bottom bar (the mini-infobar) that appears once the criteria are met. It is easy to miss and easy to dismiss, and once dismissed Chrome waits a long time before showing it again. The whole point of a custom install button is to take control of that moment.

let deferredPrompt = null;
const installButton = document.getElementById('install-btn');

window.addEventListener('beforeinstallprompt', (event) => {
  // Stop Chrome from showing the mini-infobar immediately.
  event.preventDefault();
  deferredPrompt = event;
  // Now reveal your own UI; it stays hidden until the event proves installability.
  installButton.hidden = false;
});

installButton.addEventListener('click', async () => {
  if (!deferredPrompt) return;
  deferredPrompt.prompt();
  const { outcome } = await deferredPrompt.userChoice; // 'accepted' | 'dismissed'
  // The deferred event can only be used once.
  deferredPrompt = null;
  installButton.hidden = true;
  // Send outcome to analytics here.
});

window.addEventListener('appinstalled', () => {
  // Fires after a successful install from any entry point, including Chrome's own menu.
  deferredPrompt = null;
  installButton.hidden = true;
});

Rules that follow from the event's design:

  • prompt() must be called from a user gesture (a tap on your button). Calling it on a timer or on page load is ignored.
  • Each beforeinstallprompt event can be used for exactly one prompt() call. If the visitor dismisses, you cannot immediately call it again; you wait for a new event, which Chrome will not fire for some time.
  • Keep your install button hidden until the event arrives. A button that does nothing when tapped, because the event never fired, is worse than no button.
  • Listen for appinstalled to clean up UI and record the conversion. The userChoice outcome tells you the visitor tapped Install; appinstalled tells you it completed.

Trigger timing: when to show the button

The event tells you installation is possible; it does not mean the visitor is ready. Chrome's own guidance on web.dev is to promote installation after the visitor has had a reason to want the app, and our experience with install-driven campaigns points the same way. Patterns that work:

  • After a completed action: the visitor finished reading, submitted a form, added something, reached the end of a flow. Show the install button on the confirmation state.
  • On return visits: a second session on the same origin is a stronger signal of interest than a first one. Count visits in local storage and promote on the second or third.
  • Scroll depth or dwell time thresholds for content pages: show the button once the visitor has scrolled past the first screen and stayed for a while, not in the first second.
  • Where install is the offer, such as a landing page whose purpose is to get the app onto the home screen, show the button as the primary call to action in the first screen, but only after the event has fired. Hide it until then, or show a disabled state with a short explanation.

Patterns that hurt: calling prompt() from a setTimeout (it fails silently and consumes the gesture), showing an install banner that covers the content before the visitor has seen anything, and showing it again every page view after a dismissal. A dismissal is information; respect it for a period you choose (store a timestamp, and do not re-promote for several days or weeks).

Copy and placement for the install button

The native Chrome dialog shows your app's name and icon and an Install button; you cannot change that text. What you control is the element the visitor taps to get there and the words around it.

  • Label the button with the benefit, not the mechanism: "Add to home screen for one-tap access" or "Install for faster loading and offline access" beats "Install PWA". Most visitors do not know the acronym.
  • Say what it costs: nothing to download from a store, no account required, and it can be removed like any app. The common hesitation is that "install" sounds like a large download.
  • Place the button where the tap is natural: in the first screen for install-first landing pages, at the end of content for article pages, in the header for returning visitors. Avoid floating overlays that fight the browser's own bottom bar.
  • Use your app icon next to the button. It sets the expectation of what will appear on the home screen and matches the native dialog that follows.
  • One install prompt per screen. Combining it with a notification permission request or a newsletter popup in the same moment lowers acceptance of all of them.

For install-first pages, the landing page itself is the prompt's environment; our guide to the PWA landing page covers the page structure that surrounds the button.

Why the Chrome install prompt is not showing: a diagnostic order

When beforeinstallprompt never fires, work through these in order. Chrome DevTools has the answers for most of them under Application, then Manifest, which lists installability errors by name.

  1. Open DevTools, Application, Manifest. Read the installability section. Missing icon sizes, a wrong display value, a start_url that redirects off-origin or returns a non-200 status, and manifest parse errors all show up here.
  2. Confirm HTTPS on the exact URL the visitor lands on, including after any redirect from the ad click.
  3. Check whether the app is already installed. Chrome will not fire the event for an installed app. Test in a fresh profile or uninstall first. On Android, Chrome's chrome://webapks page lists installed web apps.
  4. Check for a recent dismissal. If a tester dismissed the prompt earlier on the same device, Chrome suppresses it for a cooldown period; web.dev describes this as about three months for the mini-infobar. Use a fresh profile for testing.
  5. Check the browser. beforeinstallprompt is a Chromium feature. It does not exist in Safari, it does not fire in Firefox, and it does not fire inside the embedded in-app browsers of Facebook, Instagram, TikTok and most other apps, because those are WebViews, not Chrome. For ad traffic this is usually the real cause: the visitor is in the app's browser, the event never fires, and the install button never appears. The fix is on the landing page, not in the manifest: detect the in-app browser and move the visitor to Chrome before promoting installation.
  6. Check prefer_related_applications. If it is true and a related native app is listed, Chrome promotes that app instead.
  7. Check the engagement heuristic. Interact with the page (tap something, stay a few seconds) before expecting the event.
  8. Check your own code. preventDefault() without ever calling prompt() means the prompt is captured and never used; a listener attached after the event already fired misses it. Attach listeners as early as possible in page load.
  9. Check the service worker in Application, Service Workers. Even where it is no longer required, a failing registration on an older Chrome build blocks the prompt.

If all nine pass and the event still does not fire on a clean Chrome profile on Android, capture the Chrome version and test in Chrome's own installability tooling before suspecting the page.

Measuring whether optimization worked

Treat the prompt as a funnel with four steps and record each one:

  • beforeinstallprompt fired (installability passed for this visitor).
  • Install button shown (your timing rule triggered).
  • prompt() called (the visitor tapped your button).
  • userChoice accepted and appinstalled fired.

The ratio between the first and the last tells you whether the page, the timing or the copy is the weak point, and comparing by traffic source shows how much of the gap is the in-app browser problem. How to send these events to GA4 and attribute them to campaigns is covered in our article on measuring add to home screen conversions.

ROIBest's PWA landing pages are built around this sequence for ad traffic: handoff from the in-app browser to Chrome, the custom install button bound to beforeinstallprompt, and install events recorded as separate funnel steps so you can see where visitors drop off.

FAQ

Why is beforeinstallprompt not firing on my site?

Most often one of four reasons: the manifest fails an installability check (see DevTools, Application, Manifest), the app is already installed, the prompt was dismissed recently and Chrome is in its cooldown period, or the page is open in a non-Chrome browser such as an in-app WebView where the event does not exist.

Can I show the Chrome install prompt automatically on page load?

No. prompt() only works from a user gesture such as a tap, and only after beforeinstallprompt has fired. You can show your own install button automatically once the event arrives, but the native dialog needs the visitor's tap.

How often can I show the install prompt?

Once per beforeinstallprompt event. After a dismissal, Chrome waits before firing the event again, and you should also store the dismissal and avoid re-promoting for days or weeks rather than on every page view.

Does the install prompt work in the Facebook or TikTok in-app browser?

No. Those apps open pages in an embedded WebView that does not implement beforeinstallprompt. For ad traffic, detect the in-app browser and guide the visitor to open the page in Chrome before showing the install button.

ROIBest

Build your next growth engine with ROIBest

Talk to our team about a stable, high-converting Android PWA solution.