Installability
Installability means the browser or store can present the web app like an app: icon, launch surface, app window, and platform-specific display mode.
Minimum Browser Install Signals
| Signal | Why it matters |
|---|---|
| HTTPS or localhost | Most PWA APIs require a secure context |
| Manifest linked from HTML | Gives the browser app identity and presentation metadata |
| Name and short name | Used in install UI and launch surfaces |
| Icons | Required for home screen, app launcher, task switcher, and splash surfaces |
start_url and scope | Define launch entry and app boundary |
| Installable display mode | standalone, fullscreen, or minimal-ui gives app-like display |
Chrome prompt behavior has stricter rules than manual menu installation. Older advice often says a service worker fetch handler is required for install. Current Chrome documentation says menu installation no longer universally requires that, while service workers remain essential for offline, reliability, push, and background behavior.
beforeinstallprompt
Use beforeinstallprompt as an optional enhancement, not the only install path.
type BeforeInstallPromptEvent = Event & {
prompt: () => Promise<void>;
userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }>;
};
let deferredPrompt: BeforeInstallPromptEvent | undefined;
window.addEventListener('beforeinstallprompt', (event) => {
event.preventDefault();
deferredPrompt = event as BeforeInstallPromptEvent;
document.querySelector('#install')?.removeAttribute('hidden');
});
document.querySelector('#install')?.addEventListener('click', async () => {
if (!deferredPrompt) return;
await deferredPrompt.prompt();
const choice = await deferredPrompt.userChoice;
console.log(choice.outcome);
deferredPrompt = undefined;
});
Gotcha: Not every browser supports
beforeinstallprompt. Always provide human instructions such as browser menu install or iOS Add to Home Screen when those targets matter.
Test Checklist
- Manifest loads without DevTools errors.
- Icons appear in install UI.
- Installed window opens at the expected route.
- App stays inside intended scope.
- Offline reload works after first load if offline is promised.
- Uninstall/reinstall does not create a second identity because
idchanged.