Installing Progressive Web Apps¶
Installing a Progressive Web App registers a website with the operating system as an application. The site gets its own launcher icon, its own window without browser UI, an entry in the OS app list, and on some platforms access to features that plain tabs don't get. The browser still runs the app. That distinction explains most of the surprises developers hit: storage is shared with the browser or isolated from it depending on the platform, "uninstall" may or may not delete data, and your code often can't tell that it was installed at all. This section covers what installation creates on each platform, how users and your UI trigger it, and how you detect and measure it.
Key takeaways
- An installed PWA is an OS-level launcher entry plus a browser-hosted app window. Chromium creates real OS artifacts: Start menu entries on Windows, app bundles on macOS,
.desktopfiles on Linux, and signed WebAPKs on Android. Safari creates Home Screen web apps on iOS and Dock web apps on macOS. - Storage behavior differs by platform. Chromium apps on desktop and Android share storage with the browser profile. Each iOS Home Screen web app has its own isolated storage. Safari on macOS copies only cookies into a new Dock app.
- Only Chromium-based browsers let your page trigger the install dialog, through
beforeinstallprompt. Safari and Firefox are user-initiated only, so your UI must fall back to instructions there. - Since iOS 26, every site added to the Home Screen opens as a web app by default, and desktop Chrome lets users install almost any page from its menu (Install page as app). Installation no longer depends on your manifest, but promotion and the quality of the result still do.
- Install detection comes from several partial signals:
appinstalled, thedisplay-modemedia feature,navigator.standaloneon iOS,getInstalledRelatedApps(), and a marker instart_url. None of them works everywhere. navigator.install()and the<install>element, which let a page install itself or another origin's app, are still experimental in September 2026.
What installation creates, technically¶
"Install" means different things on different platforms. What they have in common is that the browser writes something into the operating system's app registry that launches your start_url in an app window. The table shows what that is on each platform.
| Platform | Installing browser | What the OS gets | Where users find and remove it |
|---|---|---|---|
| Windows | Chrome, Edge (Chromium) | Start menu shortcut in a per-browser Apps folder, optional taskbar and desktop shortcuts, an entry in Settings > Apps > Installed apps | Start menu, Installed apps, chrome://apps, edge://apps, the app window's menu |
| Windows | Firefox 143+ | A site pinned to the taskbar that opens in a simplified Firefox window | Taskbar |
| macOS | Chrome, Edge | An app shim bundle; Chrome puts it in ~/Applications/Chrome Apps.localized/ | Launchpad, Spotlight, Finder, chrome://apps |
| macOS | Safari 17+ | A web app added to the Dock, opened in its own window even without a manifest | Dock, Launchpad, Spotlight |
| Linux | Chrome, Edge | A freedesktop .desktop file named <browser>-<app id>-<profile>.desktop in the user's applications directory | Desktop environment's app launcher |
| ChromeOS | Chrome | A launcher and shelf app managed by the system | Launcher, Settings > Apps |
| Android | Chrome with Google Play services, Samsung Internet on Samsung devices | A WebAPK: a real Android package signed by a minting server | App drawer, Settings > Apps |
| Android | Firefox, Edge, others | A pinned home screen shortcut badged with the browser logo | Home screen |
| iOS, iPadOS | Safari and, since 16.4, browsers with Apple's web-browser entitlement | A Home Screen web app | Home Screen, App Library |
The details of each flow are in Installation by Platform. The rest of this section covers the parts every installed app has in common.
The launcher entry and the app's identity¶
Every install creates a launcher entry named after your manifest's name or short_name and using an icon from icons (or apple-touch-icon on iOS). The browser also records an identity for the app, and on Chromium that identity is the manifest id, or start_url when there's no id. The identity decides whether a later manifest counts as an update to the same app or as a different app. It also shows up in APIs that need to refer to an installed app, such as getInstalledRelatedApps() on desktop and the experimental navigator.install(). Set id explicitly before your first user installs. App Identity & Updates covers how the ID is computed and what changes trigger an update.
The standalone window¶
Launching the entry opens start_url in a window whose chrome depends on display and display_override: no browser UI in standalone, a small back and reload toolbar in minimal-ui, or custom title bar content with Window Controls Overlay. Navigations inside the manifest scope stay in the window. Out-of-scope navigations show an origin bar on desktop, or open in a Custom Tab on Android. The display-mode media feature reports which mode applies, which is how CSS and JavaScript adapt to the app context. Display Modes documents how each engine resolves the mode.
Storage: shared with the browser or isolated¶
Where your data lives after installation surprises many teams:
- Chromium on desktop. The app runs in the browser profile it was installed from. Cookies, IndexedDB, Cache Storage and service worker registrations are the same ones the site uses in a tab of that profile. A user signed in in the browser is signed in in the app.
- Chrome on Android (WebAPK). The WebAPK is a thin launcher package. Pages run in Chrome and use Chrome's storage for the origin, so tab and app share state.
- Safari on iOS and iPadOS. Each Home Screen web app has its own storage container, isolated from Safari and from other copies of the same site added to the Home Screen. Since iOS 17.2, Safari's cookies are copied into the new web app when it is created, so users generally stay signed in through cookie-based sessions;
localStorage, IndexedDB and caches are not copied. Adding the same site twice creates two independent apps. - Safari on macOS. When the user adds a site to the Dock, Safari copies the site's cookies into the new web app so the user stays signed in. According to WebKit's Safari 17 announcement, "Safari does not copy over any other kind of local storage."
This has practical consequences for install flows. On iOS, anything you store before installation, such as a draft, a cart or an onboarding state, isn't available in the installed app. On Chromium, your service worker and caches are already in place when the app launches for the first time. Storage Quotas & Persistence and iOS & iPadOS cover the details.
WebAPKs on Android¶
When Chrome on Android installs a promotable PWA on a device with Google Play services, it requests a WebAPK from Google's minting service. The WebAPK is a real, signed Android package generated from your manifest. It appears in the app drawer and in Settings > Apps, registers intent filters so links inside your scope open in the app, and can expose app shortcuts and a share target. Chrome checks the manifest for changes and requests an updated WebAPK when relevant members change. When minting isn't available, Chrome creates a plain home screen shortcut instead, which gets none of these integrations. You can inspect installed WebAPKs at about://webapks. The differences between WebAPKs, shortcuts and Trusted Web Activities are covered in Android.
Minting takes time. According to web.dev's Learn PWA detection chapter, on Android with WebAPK the appinstalled event fires when the user accepts the dialog, "not after the WebAPK is minted and installed". The launcher icon may appear a few seconds later.
Uninstalling and what happens to data¶
Users uninstall PWAs the way they uninstall other apps on the platform: Settings > Apps on Windows and Android, dragging to the Trash or using the launcher on macOS, or long-pressing the icon on iOS. Chromium browsers also offer uninstall from the app window's menu and from chrome://apps or edge://apps. On Windows, Chromium registers each app with the OS uninstall list, so it also appears in Installed apps. Chromium's source only implements that registration on Windows.
Uninstalling doesn't necessarily delete site data:
- Chrome's desktop uninstall dialog has an opt-in checkbox, "Also delete browsing data (origin) which will sign you out of domain." Unchecked, the site's storage survives in the browser profile.
- Removing a WebAPK on Android removes the launcher package. The origin's data lives in Chrome and stays there unless the user clears it.
- Deleting an iOS Home Screen web app deletes its isolated storage container along with it.
No uninstall event reaches your code. If you need to know about uninstalls, infer them server side: push subscriptions that start returning 404 or 410 from the push service, or installed-app sessions that stop arriving. Detecting Installed Apps shows how to record installed sessions.
Install flows by platform¶
The table summarizes how installation starts on each platform and which developer-facing events you get. The full step-by-step flows, menu labels and platform quirks are in Installation by Platform.
| Platform and browser | User-initiated path | Browser promotion | beforeinstallprompt | appinstalled |
|---|---|---|---|---|
| Chrome desktop | Address bar install icon; More > Cast, save, and share > Install page as app | Install icon in the address bar | ✅ | ✅ |
| Edge desktop | App available icon; Settings and more > Apps > Install this site as an app | App available icon | ✅ | ✅ |
| Chrome for Android | More > Add to home screen > Install (labels vary by version) | Install message and bottom sheet, rate-limited by Chrome's own heuristics | ✅ | ✅ (on accept) |
| Samsung Internet | Install icon in the URL bar; menu Add page to | URL bar icon | ✅ (5.0+) | ✅ (7.0+) |
| Firefox for Android | Menu Add app to Home screen or Install (varies by version) | None | ❌ | ❌ |
| Firefox on Windows (143+) | Pin the site to the taskbar | None | ❌ | ❌ |
| Safari on iOS and iPadOS | Share > Add to Home Screen, with Open as Web App on by default since iOS 26 | None | ❌ | ❌ |
| Safari on macOS (17+) | File > Add to Dock or Share > Add to Dock | None | ❌ | ❌ |
Support data as of September 2026. Check MDN's beforeinstallprompt compatibility data and caniuse for live data.
Two platform changes from 2024 to 2025 reshaped this table. Chrome added Install page as app on desktop in 2024, which installs almost any page whether or not it passes the installability criteria, and Safari 26 made Open as Web App the default for every Home Screen addition. As a result, the criteria now decide whether a browser promotes your app and how good the installed result is, not whether users can install it.
Promoting installation¶
Because browsers promote installation sparingly and Safari never does, most successful PWAs ask for the install themselves. The common patterns:
- A persistent, low-key entry point: an Install app item in the header, account menu or settings page. It's always there for users who look for it.
- A contextual promotion: a card in a feed, or a prompt shown after a meaningful action such as completing an order, saving a second document or enabling notifications. It appears when the value of installing is obvious.
- An install landing page that explains what the app does offline or with notifications, linked from emails or onboarding.
- Platform-specific instructions for Safari and Firefox, which don't fire
beforeinstallprompt: a short illustrated guide to Share > Add to Home Screen or File > Add to Dock.
On Chromium, every pattern ends in the same call: save the beforeinstallprompt event, then call its prompt() method from a click handler. Install Prompts & Custom UI documents the event, complete code for each pattern, the iOS instructions UI, the experimental Web Install API, and UX rules that keep promotion from becoming spam. The manifest can make the browser's own dialog more persuasive. Rich Install UI covers the screenshots and description members behind Chromium's store-like dialog.
flowchart TD
A["Page loads"] --> B{"beforeinstallprompt fired?"}
B -- "yes (Chromium)" --> C["preventDefault(), save event, show install button"]
C --> D["User clicks button"]
D --> E["event.prompt()"]
E --> F{"outcome"}
F -- accepted --> G["appinstalled: hide promotion, record install"]
F -- dismissed --> H["Hide button; wait for next beforeinstallprompt"]
B -- "no" --> I{"Already running installed?"}
I -- yes --> J["No promotion"]
I -- no --> K{"iOS / macOS Safari / Firefox?"}
K -- yes --> L["Show platform instructions on demand"]
K -- no --> M["No install UI"] Detecting and measuring installs¶
You can't query "is this app installed?" everywhere, so install analytics combines several signals, each covering part of the picture:
| Signal | What it tells you | Where it works |
|---|---|---|
beforeinstallprompt fired | The page is promotable and not installed in this profile | Chromium |
prompt() result (outcome) | Whether the user accepted or dismissed your prompt | Chromium |
appinstalled | An install succeeded, from your button or from browser UI | Chromium |
display-mode media feature | The current document runs in an app window | All engines, with differences |
navigator.standalone === true | The document runs as a Home Screen web app (iOS, iPadOS) or a Dock web app (macOS) | Safari on iOS and iPadOS; Safari 17+ on macOS. Check it before display-mode on iOS, and combine it with navigator.maxTouchPoints > 0 to tell iOS from macOS |
start_url marker, such as ?source=pwa | The session started from the app icon | All platforms |
document.referrer starting with android-app:// | The first page load came from an Android app, such as a TWA | Chrome on Android |
navigator.getInstalledRelatedApps() | Your PWA or native app is installed, queried from a tab | Chromium on Android (84+) and desktop (140+) |
These signals give you an install funnel: promotable → promotion shown → prompt accepted → installed → launched as app → retained. Detecting Installed Apps has complete detection code, and Install Prompts & Custom UI shows how to instrument each step. For sending these events to an analytics backend, including sessions that start offline, see Analytics for PWAs.
Measure launches and retention, not just installs. An install that's never opened adds little value, and on iOS you can only observe the launch, not the install itself.
Beyond browser installation: stores and wrappers¶
Browser installation isn't the only way to put a PWA on a device. You can package the same web app for app stores:
- Google Play through a Trusted Web Activity, an Android app that renders your origin in Chrome without browser UI, verified with Digital Asset Links.
- Microsoft Store, which lists PWAs directly as packaged apps.
- Apple's App Store, which requires a native wrapper and is subject to App Review's rules on minimum functionality.
Store distribution changes discovery, updates and payments, and it adds signals you can detect, such as related_applications entries of platform play or windows. Publishing to App Stores compares the stores and the tooling, including PWABuilder.
The experimental Web Install API¶
beforeinstallprompt can only install the page the user is on, and only when Chromium decides it's promotable. Microsoft and Google are incubating two entry points that remove both limits: navigator.install(), a promise-based method, and a declarative <install> element. Both can install the current app or an app from another origin identified by its manifest URL, and both require the target manifest to have an id (or the caller to supply the computed ID). They ran as origin trials in Chrome and Edge (navigator.install() from Chrome 143 to 150, <install> from 148 to 153), and Chrome Platform Status lists intents to ship both on desktop in Chrome 156, scheduled for stable release on October 20, 2026. Android isn't part of that intent. WebKit opposes site-initiated installation, and Mozilla hasn't taken a position. Install Prompts & Custom UI covers the current API shape and how to feature-detect it safely.
Pages in this section¶
-
Install Prompts & Custom UI
beforeinstallpromptin depth, complete install button and promotion patterns, iOS instructions,appinstalled, the Web Install API and install funnel analytics. -
Detecting Installed Apps
display-mode,navigator.standalone,getInstalledRelatedApps(), launch attribution withstart_url, TWA referrers and server-side install tracking. -
Installation by Platform
Step-by-step install flows and behavior on Windows, macOS, Linux, ChromeOS, Android, iOS and iPadOS for every major browser.
-
Publishing to App Stores
Shipping a PWA through Google Play, the Microsoft Store and Apple's App Store: packaging, policies and tooling.
-
Trusted Web Activity
Wrapping a PWA in an Android app with Digital Asset Links, Bubblewrap, Play Billing and launch customization.
Further reading¶
On this site
- Installability Criteria: what each browser checks before promoting installation.
- Display Modes: how the app window is configured and detected.
- App Identity & Updates: the manifest
idand how installed apps update. - Rich Install UI: screenshots and descriptions in Chromium's install dialog.
- Android and iOS & iPadOS: platform internals.
- Analytics for PWAs: measuring installs, launches and retention.
External references
- Web Application Manifest (W3C)
- Manifest Incubations: installation prompts and
BeforeInstallPromptEvent(WICG) - Making PWAs installable (MDN)
- Learn PWA: Installation and Detection (web.dev)
- WebKit features in Safari 17.0 and Safari 26.0 (WebKit blog)
- Web Install API explainer (Microsoft Edge)