Install Prompt Issues
What This Means
The install prompt (Add to Home Screen) allows users to install your PWA. Issues include:
- Prompt never appearing
- Prompt dismissed and not returning
- Installation failing silently
- Different behavior across browsers
Install Prompt Requirements
Chrome requires ALL of these:
- HTTPS (or localhost)
- Valid web app manifest with required fields
- Registered service worker with fetch handler
- Not already installed
- Meets engagement heuristic (user interaction)
How to Diagnose
1. DevTools Checklist
DevTools > Application > Manifest
- Check "Installability" section for errors
DevTools > Application > Service Workers
- Verify SW is registered and active
2. Console Debugging
// Check if install prompt is available
window.addEventListener('beforeinstallprompt', (e) => {
console.log('Install prompt available!');
console.log('Platforms:', e.platforms);
});
// Check if already installed
window.addEventListener('appinstalled', () => {
console.log('PWA was installed');
});
// Check display mode
if (window.matchMedia('(display-mode: standalone)').matches) {
console.log('Already running as installed PWA');
}
3. Common Blockers
| Issue | Check |
|---|---|
| No HTTPS | Site must be secure |
| Manifest missing | <link rel="manifest"> present |
| Manifest invalid | Check DevTools for errors |
| No service worker | SW registered with fetch handler |
| Already installed | Check if already in standalone mode |
| iOS Safari | Uses different install mechanism |
General Fixes
Custom Install Button
// main.js - Custom install experience
let deferredPrompt;
window.addEventListener('beforeinstallprompt', (e) => {
// Prevent default mini-infobar
e.preventDefault();
// Save event for later
deferredPrompt = e;
// Show custom install button
document.getElementById('install-button').style.display = 'block';
});
document.getElementById('install-button').addEventListener('click', async () => {
if (!deferredPrompt) return;
// Show the prompt
deferredPrompt.prompt();
// Wait for user choice
const { outcome } = await deferredPrompt.userChoice;
console.log(`User ${outcome === 'accepted' ? 'accepted' : 'dismissed'} the install`);
// Clear the deferred prompt
deferredPrompt = null;
// Hide the button
document.getElementById('install-button').style.display = 'none';
});
// Track successful installs
window.addEventListener('appinstalled', () => {
console.log('PWA installed successfully');
// Track in analytics
gtag('event', 'pwa_install', { method: 'browser_prompt' });
});
Install Prompt UI
<!-- Custom install banner -->
<div id="install-banner" style="display: none;">
<div class="install-content">
<img src="/icons/icon-72.png" alt="App icon">
<div>
<strong>Install BlueFrog</strong>
<p>Add to your home screen for quick access</p>
</div>
<button id="install-button">Install</button>
<button id="dismiss-button">×</button>
</div>
</div>
<style>
#install-banner {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: white;
padding: 16px;
box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
z-index: 1000;
}
.install-content {
display: flex;
align-items: center;
gap: 16px;
max-width: 600px;
margin: 0 auto;
}
</style>
iOS Safari Installation
iOS doesn't support beforeinstallprompt. Provide manual instructions:
// Detect iOS Safari
const isIOS = /iphone|ipad|ipod/.test(navigator.userAgent.toLowerCase());
const isInStandaloneMode = window.matchMedia('(display-mode: standalone)').matches;
const isSafari = /safari/.test(navigator.userAgent.toLowerCase()) &&
!/chrome/.test(navigator.userAgent.toLowerCase());
if (isIOS && isSafari && !isInStandaloneMode) {
showIOSInstallInstructions();
}
function showIOSInstallInstructions() {
const banner = document.getElementById('ios-install-banner');
banner.innerHTML = `
<p>Install this app on your iPhone:</p>
<ol>
<li>Tap the Share button <span class="share-icon">⎙</span></li>
<li>Scroll and tap "Add to Home Screen"</li>
</ol>
`;
banner.style.display = 'block';
}
Delayed Prompt Strategy
// Don't show install prompt immediately
let deferredPrompt;
let promptShownThisSession = false;
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
deferredPrompt = e;
});
// Show after user engages (e.g., scrolls, reads content)
let scrollDepth = 0;
window.addEventListener('scroll', () => {
const currentDepth = (window.scrollY / document.body.scrollHeight) * 100;
if (currentDepth > 50 && !promptShownThisSession && deferredPrompt) {
showInstallBanner();
promptShownThisSession = true;
}
});
// Or show after time on page
setTimeout(() => {
if (deferredPrompt && !promptShownThisSession) {
showInstallBanner();
promptShownThisSession = true;
}
}, 60000); // 1 minute
Persist Dismiss Preference
const DISMISS_KEY = 'pwa-install-dismissed';
const DISMISS_DURATION = 7 * 24 * 60 * 60 * 1000; // 7 days
function shouldShowInstallBanner() {
const dismissedAt = localStorage.getItem(DISMISS_KEY);
if (dismissedAt) {
const elapsed = Date.now() - parseInt(dismissedAt, 10);
if (elapsed < DISMISS_DURATION) {
return false;
}
}
return true;
}
function dismissInstallBanner() {
localStorage.setItem(DISMISS_KEY, Date.now().toString());
document.getElementById('install-banner').style.display = 'none';
}
Platform-Specific Guides
| Platform | Guide |
|---|---|
| Next.js | Next.js PWA Install |
| WordPress | WordPress PWA Install |
Verification
- Meet all install criteria
beforeinstallpromptfires (check console)- Custom install button triggers prompt
- Installation completes successfully
- App appears in app drawer/home screen
Common Mistakes
| Mistake | Fix |
|---|---|
| Calling prompt() without user gesture | Must be triggered by click |
| Not preventing default | Call e.preventDefault() |
| Expecting iOS support | Provide manual instructions |
| Prompt reuse | Can only use deferredPrompt once |