Platform in Platforms
PWAs on Chrome and Android (WebAPK, TWA)
Published Updated
On an Android device with Google Mobile Services (GMS), installing a PWA from Chrome produces a WebAPK: a small APK that Chrome requests from a Google server and installs, so the app gets a launcher entry, a row in Android’s app settings, and intent filters for its scope. Without GMS, Chrome adds a browser-badged home screen shortcut instead, and a Google Play listing needs the same web app wrapped in a Trusted Web Activity (TWA).
How it works
Section titled “How it works”The form of install depends on the browser, not on the site. On Android, Chrome on GMS devices and Samsung Internet on Samsung devices are the two browsers that install PWAs as WebAPKs; Firefox, Edge, Opera, and Chrome on devices without GMS add a shortcut that opens the site in the browser (Making PWAs installable, MDN). Chrome for Android installs on every Android release it runs on, back to Jelly Bean.
- Legend
- Yes
- Partial
- Flag
- No
- Unknown
| Browser / Platform | Support | Versions | Confidence | Source | Notes |
|---|---|---|---|---|---|
| Chrome (Android) | Yes | 57 | medium | source | 12 |
- Generates a WebAPK via Google's minting server at install time.
- No cross-browser compatibility dataset (BCD/caniuse) covers WebAPK; version verified from Chrome Platform Status.
Trusted Web Activity is a separate mechanism with its own dataset:
- Legend
- Yes
- Partial
- Flag
- No
- Unknown
| Browser / Platform | Support | Versions | Confidence | Source | Notes |
|---|---|---|---|---|---|
| Chrome (Android) | Yes | 72 | medium | source | 123 |
- Per Chrome's documentation, Trusted Web Activity is available in Chrome on Android from version 72.
- No cross-browser compatibility dataset (BCD/caniuse) covers TWA; version verified from Chrome's own documentation.
- The same documentation notes other browsers may implement the same protocol, but does not itself document TWA support in any other browser.
What Chrome needs before it offers an install
Section titled “What Chrome needs before it offers an install”Chromium browsers, including Chrome, Samsung Internet, and Edge, promote a site for
installation only when its manifest has name or short_name, icons with a 192px and a
512px entry, start_url, display or display_override, and
prefer_related_applications either false or absent. The page must be served over
https, or from localhost or 127.0.0.1 during development; file:// does not count,
even though it is a secure context. A service worker is not required for installability.
Chrome for Android, Chrome for desktop, Edge for desktop, and Safari for desktop also let
the user install a site that has no manifest at all; the manifest’s job is to make the
browser promote the install and to shape the result.
The user-facing path in Chrome for Android (English UI) is the three-dot More menu,
then Install and create shortcut, then Install
(Chrome Help).
A page can also open the same dialog itself from a beforeinstallprompt handler; the
code is under “Examples”.
What the WebAPK gives you
Section titled “What the WebAPK gives you”Chrome builds the WebAPK from the manifest and other metadata (WebAPKs on Android, web.dev). Once installed:
- The app opens in the Chrome the user installed it from, not in a WebView.
- It registers intent filters for every URL inside
scope. With"scope": "/app/", tapping a link to/app/read/bookopens the app;/help/opens a browser tab. - Permissions are not granted at install. Android grants native apps notification permission on install, but a WebAPK must request it at runtime, through Chrome’s own prompts and settings.
- Storage is the browser profile’s storage. Cookies, IndexedDB, Cache Storage, and the service worker registration are shared with Chrome; clearing Chrome’s site data clears the installed app too.
- WebAPKs generated by Chrome 71 or later show a larger icon on the splash screen when the manifest ships a 512px icon.
- The generated APK is not accepted by Google Play. A Play listing is what a TWA is for.
When the manifest changes
Section titled “When the manifest changes”Chrome for Android schedules a manifest fetch on launch if the last check is more than 24
hours old, and may stretch the interval to 30 days after a failed fetch
(How Chrome handles updates to the web app manifest,
web.dev). A change to name, short_name, icons, background_color, display,
orientation, scope, shortcuts, start_url, theme_color, or web_share_target
queues a new WebAPK, which Chrome requests once every app window is closed and the device
is plugged in and on Wi-Fi. Renaming or moving the manifest file can stop updates
altogether, and a manifest that carries per-user values regenerates the APK for no
benefit.
Trusted Web Activity for Google Play
Section titled “Trusted Web Activity for Google Play”A TWA opens your origin from an Android app you publish, through a protocol built on
Custom Tabs, available in Chrome 72 and later
(Trusted Web Activities,
developer.chrome.com). The app and the site must be linked with Digital Asset Links; when
they are, the content renders fullscreen in the user’s browser, with no browser UI. The
host app has no direct access to cookies or localStorage, so state passes through URLs
(query parameters and intent URIs). On a Chrome older than 72 the same app falls back to a
Custom Tab with a plain toolbar. Bubblewrap is the Node.js CLI that generates the Android
project; the packaging steps are on the
Trusted Web Activity (TWA): PWAs in the Play Store entry.
Examples
Section titled “Examples”Both examples run unchanged in a browser tab, in a WebAPK, and in a desktop app window; the feature checks decide what the user sees.
Showing your own install button
Section titled “Showing your own install button”beforeinstallprompt fires on window once Chrome has decided the page is installable,
usually during page load, with no guaranteed timing. Keep the button hidden until the
event arrives, because the event is Chromium-only:
- Legend
- Yes
- Partial
- Flag
- No
- Unknown
| Browser / Platform | Support | Versions | Confidence | Source | Notes |
|---|---|---|---|---|---|
| Chrome (Android) | Yes | 68 | medium | source | — |
| Chrome (Desktop) | Yes | 73 | medium | source | — |
| Edge (Desktop) | Yes | 79 | medium | source | — |
| Safari (iOS) | No | — | medium | source | 1 |
| Safari (macOS) | No | — | medium | source | 2 |
| Firefox (Desktop) | No | — | medium | source | 3 |
| Samsung Internet | Yes | 9.0 | medium | source | — |
- No beforeinstallprompt; install is manual via the Share sheet → Add to Home Screen.
- Install is via the File → Add to Dock menu, not the web event.
- No manifest-based desktop install path.
<button id="install" hidden>Install</button>let installPrompt = null;const installButton = document.querySelector('#install');
window.addEventListener('beforeinstallprompt', (event) => { event.preventDefault(); // suppress Chrome's own install UI installPrompt = event; // keep the event for the click handler installButton.removeAttribute('hidden');});
installButton.addEventListener('click', async () => { if (!installPrompt) return; const consumedPrompt = installPrompt; installButton.disabled = true; // no second click while the dialog is open try { const result = await consumedPrompt.prompt(); console.log(`Install prompt was: ${result.outcome}`); // "accepted" or "dismissed" } finally { installPrompt = null; // the instance is consumed on every exit path installButton.disabled = false; installButton.setAttribute('hidden', ''); }});prompt() has to run inside a user-activation handler and may be called once per
BeforeInstallPromptEvent instance
(BeforeInstallPromptEvent: prompt() method,
MDN), which is why the handler retires the event in finally rather than only after a
successful prompt. On iOS the event does not exist, so the button stays hidden there and
the user installs from the Share menu.
Reading the applied display mode
Section titled “Reading the applied display mode”display-mode reports the mode the browser applied, which can differ from the manifest’s
display when the browser does not support the requested value
(display-mode,
MDN). It describes presentation, not install state, so use it to adjust the UI, not as an
installation API:
function applyDisplayMode(installButton) { if (!('matchMedia' in window)) { // matchMedia is unavailable: leave the button hidden and rely on the browser's menu. installButton.hidden = true; return; } if (window.matchMedia('(display-mode: standalone)').matches) { installButton.hidden = true; // already running as an app window }}A standalone match means the page is not in a normal tab; it does not prove a WebAPK
exists, because the same media query matches a desktop app window and a Home Screen web
app on iOS.
Observed behaviour
Section titled “Observed behaviour”The WebAPK update cycle is visible on-device, which is the quickest way to tell a stale manifest from a cache problem.
Uninstalling goes through Android, not Chrome: Settings > Apps > See all apps,
tap the web app, then Uninstall (English UI, Chrome Help). When a developer changes
the app’s name, Chrome shows the new name to the user with OK to accept or
Uninstall app to remove it; a rename that imitates another app is treated by Chrome
Help as a sign the developer may be malicious.
See also
Section titled “See also”- WebAPKs on Android (web.dev)
- How Chrome handles updates to the web app manifest (web.dev)
- Trusted Web Activities (developer.chrome.com)
- Installability criteria: what makes a PWA installable
- WebAPK: how Chrome installs PWAs on Android
- Trusted Web Activity (TWA): PWAs in the Play Store
- beforeinstallprompt and custom install prompts
- PWAs on Samsung Internet
Specifications
| Specification | Status |
|---|---|
| None. | |