Platforms · Platform
PWAs on Chrome and Android (WebAPK, TWA)
Published Updated
In one line: installing a PWA on Android can do more than add it to the Home Screen — on devices with Google Mobile Services (GMS), Chrome automatically generates and installs a special APK of your app, sometimes called a WebAPK, which makes the app show up in the app launcher and in Android’s app settings and lets it register a set of intent filters. Where GMS is not present, Chrome adds a browser-badged home-screen shortcut instead. If you want a Google Play listing, you wrap the same web app in a Trusted Web Activity (TWA).
Where it is supported
Section titled “Where it is supported”Support for promoting a PWA for installation varies by browser and platform, and on Android the kind of install you get varies too:
- On Android, the only browsers that install PWAs as WebAPKs are Chrome on devices with Google Mobile Services (GMS) and Samsung Internet on Samsung devices. This gives them a real entry in the app launcher and switcher, and in the system settings.
- Firefox, Edge, Opera, and other browsers — including Chrome on devices where GMS is not present — instead add a browser-badged home-screen shortcut that opens the site in the browser.
- PWAs can be installed on all versions of Android that run Chrome for Android, specifically Jelly Bean and above.
- Trusted Web Activity is available in Chrome on Android, version 72 and above.
The two Android-specific packaging mechanisms have their own datasets. WebAPK minting:
- 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.
And Trusted Web Activity:
- 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.
Separately from manifest-driven promotion, Chrome for desktop and Android, Safari for desktop, and Edge for desktop also support users installing any website as an app, whether or not it has a manifest file, and without regard to the installability criteria for the manifest file. The benefit of shipping a manifest is that the browser will actively promote the site for installation when it is visited, and that you can customize the installation behaviour.
What makes the app installable
Section titled “What makes the app installable”For a web app to be promoted for installation it must include a web app manifest, and Chromium-based browsers (including Google Chrome, Samsung Internet, and Microsoft Edge) require that the manifest include:
nameorshort_nameicons, which must contain a 192px and a 512px iconstart_urldisplayand/ordisplay_overrideprefer_related_applications, which must befalseor not present
It must also be served over https, or from a local development environment using
localhost or 127.0.0.1 — with or without a port number. A service worker is not a
requirement for installability, although many PWAs use one to provide an offline
experience.
Triggering the install prompt yourself
Section titled “Triggering the install prompt yourself”A PWA can provide its own in-page UI to open the install prompt instead of relying on the
browser’s default UI, which lets you give the user context and a reason to install. This
relies on the beforeinstallprompt event, fired on Window as soon as the browser has
determined that the PWA is installable. There is no guaranteed time the event fires, but
it usually happens on page load.
Support for the event itself is narrower than installation support in general:
- 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.
The button starts hidden, because not all browsers support installation, and the handler reveals it:
<button id="install" hidden>Install</button>let installPrompt = null;const installButton = document.querySelector('#install');
window.addEventListener('beforeinstallprompt', (event) => { event.preventDefault(); // cancels the event, which prevents the browser // displaying its own install UI on some platforms installPrompt = event; // keep the event for later installButton.removeAttribute('hidden');});
installButton.addEventListener('click', async () => { if (!installPrompt) { return; } const consumedPrompt = installPrompt; installButton.disabled = true; // no second click while the prompt is open try { const result = await consumedPrompt.prompt(); console.log(`Install prompt was: ${result.outcome}`); // "accepted" or "dismissed" } finally { // Retire the event and the button on *every* exit path, including a // rejected `prompt()`: the instance has been consumed either way, so a // second click on it would be invalid. installPrompt = null; installButton.disabled = false; installButton.setAttribute('hidden', ''); }});prompt() must be called in the event handler for a user action such as a button click,
and may only be called once on a given BeforeInstallPromptEvent instance — which is why
the handler above retires installPrompt and hides the button from a finally block,
rather than only on the success path. This technique is not supported on iOS.
Detecting how the app is running, with a fallback
Section titled “Detecting how the app is running, with a fallback”MDN’s pattern keeps the install control hidden by default — because not all browsers will
support installation — and reveals it from the beforeinstallprompt handler shown above.
display-mode is a different signal alongside it: it tests whether the app is being
displayed in a normal browser tab or in some alternative way, such as a standalone app. It
is not a way to trigger or query installation, so use it as context for your UI:
function applyDisplayMode(installButton) { if (!('matchMedia' in window)) { // Fallback: the display mode cannot be read here, so leave the in-page // button hidden and rely on the browser's own install entry point. installButton.hidden = true; return; }
// `standalone`: the app is not being displayed in a conventional browser tab. if (window.matchMedia('(display-mode: standalone)').matches) { installButton.hidden = true; }}Two things to keep in mind about this test. display-mode identifies the mode that was
actually applied, which may not be the value requested in the manifest, since a browser may
not support the requested mode. And it describes presentation, not installation state: in
standalone the application looks and feels like a standalone application, which can
include its own window and its own icon in the application launcher, but a match is not a
report from an installation API.
WebAPK — the package Chrome generates
Section titled “WebAPK — the package Chrome generates”To generate the WebAPK, Chrome looks at the web app manifest and other metadata. When an update to the manifest is detected, Chrome needs to generate a new APK — so change the manifest only when necessary, and do not use it to store user-specific identifiers or other customized data, because frequent changes increase the overall install time.
- Intent filters. An installed PWA registers a set of intent filters for all URLs
within the scope of the app. When a user clicks a link inside your scope, the app opens
instead of a browser tab. Set
scopeto restrict this to part of your site — useful when your app and other non-app content share a domain. - Not a WebView. The site opens in the version of Chrome the user installed it from.
- No Play listing. The generated APKs cannot be uploaded to the Play Store; that is what a TWA is for.
- Splash screen icons. Provide at least a 192px and a 512px icon; WebAPKs generated in Chrome 71 or later show a larger icon on the splash screen.
Two behaviours regularly surprise people:
- Permissions are not granted at install time. They work like any other web app and must be requested at runtime, ideally only when you actually need them. Android normally grants installed apps immediate permission to show notifications, but apps installed via WebAPKs are not granted this at install time — you must request it at runtime.
- Storage is shared with the browser. Chrome stores the app’s data in the current profile rather than segregating it, so cookies, client-side storage, and the service worker are shared between the browser and the installed app. If the user clears their Chrome profile or deletes site data, that applies to the WebAPK too.
Trusted Web Activity — shipping to Google Play
Section titled “Trusted Web Activity — shipping to Google Play”A TWA opens your web content from an Android app you publish, using a protocol based on Custom Tabs. What distinguishes it from other ways of embedding web content:
- The content is trusted. The app and the site it opens are expected to come from the same developer, verified using Digital Asset Links.
- It is the real browser. Content is rendered by the user’s browser, exactly as the user would see it in their browser, except run fullscreen. Your web content should be accessible and useful in the browser first.
- No direct access to web state. The host app cannot reach the web content or state
such as cookies and
localStorage; coordinate by passing data to and from the page in URLs, for example through query parameters and intent URIs. - Graceful degradation. If the user’s version of Chrome does not support Trusted Web Activities, Chrome falls back to a simple toolbar using a Custom Tab.
Bubblewrap is a Node.js library and CLI that generates and builds Trusted Web Activity projects. PWABuilder simplifies packaging and publishing a PWA for various app stores, including the Google Play Store, Microsoft Store, Meta Quest Store, and iOS App Store.
Decision framework
Section titled “Decision framework”| Decision question | Recommended action | Rationale |
|---|---|---|
| Do I need the Play Store for users to install? | No — Chrome generates and installs a WebAPK directly. | Only a Play listing needs a TWA; the generated APKs cannot be uploaded to Play. |
When do I call prompt()? |
From the handler for a user action, using the stashed event. | prompt() must run in a user-action handler and works only once per event instance. |
| How do I get a Play listing? | Wrap the PWA in a TWA (Bubblewrap, or PWABuilder for several stores). | A TWA renders your site in the user’s browser, fullscreen. |
| Why doesn’t my app get its own launcher entry? | Check the browser: only Chrome with GMS and Samsung Internet mint WebAPKs. | Other Android browsers add a browser-badged home-screen shortcut instead. |
| Why are notifications silently unavailable after install? | Request the permission at runtime. | WebAPK installs are not granted notification permission at install time. |
Practical checklist
Section titled “Practical checklist”- Ship a manifest with
name/short_name, 192px and 512pxicons,start_url, anddisplay, and keepprefer_related_applicationsfalse or absent. - Serve over
https(orlocalhost/127.0.0.1in development). - Keep your install button hidden by default and reveal it from the
beforeinstallprompthandler; expect it never to fire on iOS. - Call
prompt()from a user-action handler, once per event instance. - Read the applied display mode with
(display-mode: standalone), remembering it reports the mode actually in use rather than the manifest’s requested value. - Set
scopedeliberately — it decides which links the WebAPK’s intent filters capture. - Request notification and other permissions at runtime; installation grants nothing.
- Do not assume installed-app storage is separate — clearing Chrome’s data clears it.
- Change the manifest only when necessary, to avoid repeated WebAPK regeneration.
- For a Play listing, verify Digital Asset Links so the app and site are trusted.
Where to go next
Section titled “Where to go next”- Installability criteria — the full requirement list this page summarizes.
- WebAPK — the generated Android package in detail.
- Trusted Web Activity — packaging and publishing to Play.
- beforeinstallprompt and custom install prompts — the event flow and prompt UX in depth.
- Samsung Internet — the other Android browser that installs PWAs as WebAPKs.
Specifications
| Specification | Status |
|---|---|
| None. | |