Chapter 85: Progressive Web Apps (PWAs)

PWA Installation & The beforeinstallprompt Event

Intercepting browser install heuristics, managing deferred prompts, designing custom in-app install flows, and tracking post-install analytics.

LEARNING OBJECTIVES
  • Understand the browser heuristics and criteria required to trigger the PWA installability flow.
  • Intercept the beforeinstallprompt event, cancel the default browser mini-infobar, and save the event object in memory.
  • Trigger the native OS installation dialog programmatically via deferredPrompt.prompt() and capture the user's decision using deferredPrompt.userChoice.
  • Track successful application installations using the appinstalled lifecycle event and CSS display-mode detection.
🎬 INTERACTIVE VISUAL PIPELINE Core Architecture Simulation
🌐
1. Input
Directives & Tags
⚙️
2. Parse
Tokenizer & AST
🌳
3. Layout
Box Model & Flow
🎨
4. Render
GPU Paint & Composite
PHASE 1: INPUT & DIRECTIVES
Browser receives declarative markup stream, parsing tag tokens and initializing component state.

📖 The Mental Model & Story (Intuitive Foundation)

Imagine browsing an online store. The moment you land on the homepage, a sales representative blocks your view with a clipboard, shouting: "Sign this lifetime membership contract right now!" You would immediately close the tab in frustration. However, if the representative waits until you have customized three items, added them to your cart, and expressed satisfaction before gently saying, "Would you like to install our app on your home screen for instant tracking and 1-tap checkout?", you are far more likely to accept.

Browsers historically showed an intrusive, automated banner (the "mini-infobar") the instant a site met basic PWA criteria.

Modern PWA architecture empowers frontend engineers to intercept that aggressive default banner (e.preventDefault()), stash the installation capability in memory, and present a beautifully branded, contextual in-app install button at the exact psychological moment of peak user delight (e.g., after completing a purchase, finishing a lesson, or creating a new document).


Technical Deep Dive & Specifications

Chromium PWA Installability Criteria

To trigger the beforeinstallprompt event, the application must satisfy strict browser heuristics:

+---------------------------------------------------------------------------------------+
|                          PWA INSTALLABILITY CRITERIA CHECKLIST                        |
+---------------------------------------------------------------------------------------+
| 1. Web App Manifest linked via <link rel="manifest">                                 |
| 2. Manifest includes: name / short_name, id, start_url, display (standalone/fullscreen)|
| 3. Manifest contains valid icons: at least 192x192 PNG and 512x512 PNG               |
| 4. Manifest includes a maskable icon (purpose: "maskable" or "any maskable")          |
| 5. Served over a Secure Context (HTTPS or http://localhost)                           |
| 6. Registered Service Worker with an active fetch event listener                      |
| 7. User Engagement Heuristic (User has spent >30 seconds on the page or interacted)   |
+---------------------------------------------------------------------------------------+

The beforeinstallprompt Lifecycle Flow

   BROWSER                           CLIENT DOM (UI)                     USER
      |                                     |                              |
      | 1. Checks PWA heuristics            |                              |
      |====================================>|                              |
      | 2. Fires 'beforeinstallprompt'      |                              |
      |------------------------------------>|                              |
      |                                     | [ Calls e.preventDefault() ] |
      |                                     | [ Stores e as deferredPrompt]|
      |                                     | [ Shows Custom Install UI ]  |
      |                                     |                              |
      |                                     | 3. User clicks "Install App" |
      |                                     |<=============================|
      |                                     |                              |
      | 4. deferredPrompt.prompt()          |                              |
      |<------------------------------------|                              |
      |                                     |                              |
      | 5. Shows Native OS Install Dialog   |                              |
      |===================================================================>|
      |                                     |                              |
      | 6. User clicks [Install] or [Cancel]|                              |
      |<===================================================================|
      |                                     |                              |
      | 7. Resolves deferredPrompt.userChoice                              |
      |    { outcome: 'accepted'|'dismissed'}|                             |
      |------------------------------------>|                              |
      |                                     |                              |
      | 8. Fires 'appinstalled' event       |                              |
      |------------------------------------>| [ Hides Install Button ]     |
      |                                     | [ Sends Telemetry Metric ]   |

Key Properties & Event Methods

API / Property Type Description
event.preventDefault() Method Cancels the default browser mini-infobar prompt.
deferredPrompt.prompt() Method (Async) Triggers the browser's native installation confirmation dialog.
deferredPrompt.userChoice Promise Resolves to an object: { outcome: 'accepted' | 'dismissed', platform: string }.
window.addEventListener('appinstalled') Event Dispatched once the OS finishes placing the app icon on the home screen or app launcher.

💻 Interactive Code Playground

Starter Code: Production Install Prompt Controller

Below is a complete, modular PWA installation manager with contextual UI presentation, outcome handling, and analytics logging.

Line-by-Line Code Breakdown

  • Line 87 (e.preventDefault()): Crucial method call that suppresses the browser's default mini-infobar, allowing you to orchestrate your own custom UI.
  • Line 90 (deferredPrompt = e): Stores the event reference in memory for later user-initiated execution.
  • Line 108 (await deferredPrompt.prompt()): Must be invoked from a user-initiated gesture (such as clicking the Install button); displays the operating system's native installation confirmation modal.
  • Line 111 (const { outcome } = await deferredPrompt.userChoice): Resolves when the user either clicks "Install" (outcome === 'accepted') or "Cancel" (outcome === 'dismissed').
  • Line 126 (window.addEventListener('appinstalled')): Dispatched by the browser immediately after the OS registers the PWA icon.

Expected Browser Render Output


SYS: ACTIVE
HULL: 98%
CORE: STABLE
NET: ONLINE
HTML STARSHIP CODE TERMINAL playground.html
LIVE RENDER & DIAGNOSTICS CORE TEMP: 45°C
INSPECTING DOM: VALID
TAGS: SCANNING...
PWA Installation Flow
Demonstrating deferred prompts and install telemetry.

+-------------------------------------------------------------------+
| [🚀] Orbit Productivity                               [ Later ]   |
|      Install on your desktop or homescreen            [ Install ] |
+-------------------------------------------------------------------+

Install Lifecycle Event Log
[02:45:10] Captured "beforeinstallprompt" event. Default banner prevented.
[02:45:10] Custom in-app install banner displayed to user.
(User clicks Install -> Native OS Dialog appears -> User clicks Confirm):
[02:45:18] Triggering deferredPrompt.prompt()...
[02:45:21] User response outcome: "accepted" on platform: "web"
[02:45:22] 🎉 Application successfully installed to the operating system!

🏋️ Hands-On Exercise

🎯 The Challenge: Build a Header-Integrated PWA Install Trigger

Instructions:

  1. Create a navigation header with an "Install App" button that remains hidden by default (display: none).
  2. Listen for the beforeinstallprompt event, prevent default behavior, and reveal the header button.
  3. When the user clicks the button, call prompt(), evaluate userChoice, and log whether the user accepted or rejected the installation.
  4. Hide the button permanently if the user installs the app or if appinstalled fires.

🏁 Starter Code Sandbox

SYS: ACTIVE
HULL: 98%
CORE: STABLE
NET: ONLINE
STARTER CODE SANDBOX exercise.html
LIVE RENDER & DIAGNOSTICS CORE TEMP: 45°C
INSPECTING DOM: VALID
TAGS: SCANNING...

⚠️ Common Pitfalls

  1. Calling prompt() Outside a User Gesture: Attempting to call deferredPrompt.prompt() inside a setTimeout() or immediately inside the beforeinstallprompt listener will throw a DOMException: The prompt() method must be called with a user gesture.
  2. Reusing the Same deferredPrompt Object Twice: The beforeinstallprompt event is single-use. Once prompt() is called, the event object is consumed. If the user dismisses the dialog, you must wait for the browser to dispatch a new beforeinstallprompt event before calling prompt() again.
  3. Assuming beforeinstallprompt Fires on iOS Safari: Apple WebKit on iOS does NOT support the beforeinstallprompt event. For iOS users, you must display instructional UI explaining how to tap the Safari "Share" button followed by "Add to Home Screen".

💡 Pro Tips

  1. Detect Installed State on App Launch: Always check window.matchMedia('(display-mode: standalone)').matches on initialization. If true, the user is already inside the installed application, so you should completely disable all install promotional logic.
  2. Contextual In-App Triggers Over Top Banners: Conversion rates increase significantly when install buttons are placed contextually (e.g., "Install offline music player" next to a download button) rather than using generic global banners across the top of the viewport.

📌 Key Takeaways

  • The browser fires beforeinstallprompt when the site meets all PWA installability criteria.
  • Call e.preventDefault() inside beforeinstallprompt to suppress the browser's default mini-infobar and retain the event in memory.
  • Call deferredPrompt.prompt() inside a user click handler to show the native OS installation dialog.
  • Inspect deferredPrompt.userChoice to capture whether the user accepted or dismissed the prompt.
  • The appinstalled event fires when the operating system successfully registers the PWA icon.
  • --
⭐ LEARN: HTML 🌟 ⚔️ QUIZ BATTLE ARENA // ACTIVE
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 1 / 3

What must frontend code do inside the beforeinstallprompt event handler to prevent the browser's default mini-infobar from appearing?

Question 1 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 2 / 3

Under what constraint MUST deferredPrompt.prompt() be called by client JavaScript?

Question 2 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 3 / 3

Which browser platform does NOT support the beforeinstallprompt event and requires manual user instructions for "Add to Home Screen"?

Question 3 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP