Distribute a PWA via app stores
Published
Outcome: your existing site, listed in an app store, with the store package generated from it rather than rewritten. On Android, one route is a Trusted Web Activity — per Chrome for Developers, a way to open your own web-app content, such as your PWA, from your Android app using a protocol based on Custom Tabs, where the content is rendered by the user’s browser exactly as they would see it in the browser, except run fullscreen. On Windows, per Microsoft, publishing a PWA to the Microsoft Store requires no code changes.
Browser & ecosystem support
Section titled “Browser & ecosystem support”Per Chrome for Developers, Trusted Web Activity is available in Chrome on Android version 72 and above. Per Chrome for Developers, a Trusted Web Activity tries to adhere to the user’s default choice of browser: if that browser supports Trusted Web Activities it is launched, and failing that, any installed browser that supports them is chosen. Per Microsoft, the Microsoft Store route targets Windows and is driven through Microsoft Partner Center rather than through the browser.
Google Play, via a Trusted Web Activity
Section titled “Google Play, via a Trusted Web Activity”Per Chrome for Developers, content in a Trusted Web Activity is trusted: the app and the site it opens are expected to come from the same developer, and this is verified using Digital Asset Links. The capability is restricted to websites you own, and you prove ownership by setting up those asset links.
Per Chrome for Developers, Bubblewrap is a set of libraries and a command line tool for Node.js that helps developers generate, build and run PWAs inside Android applications using Trusted Web Activity. Per its README, it requires Node.js 14.15.0 or above.
-
Install the CLI and initialize from your manifest. Per Chrome for Developers, Bubblewrap reads the web manifest, asks you to confirm the values used in the Android project, and generates the project from them.
Terminal window npm i -g @bubblewrap/clibubblewrap init --manifest=https://my-twa.com/manifest.jsonbubblewrap build -
Install the build to test it. Per Chrome for Developers, the build step outputs
app-release-signed.apk, which can be installed on a development device for testing or uploaded to the Play Store for release. Per Chrome for Developers,bubblewrap installinstalls it on a connected device, andadb install app-release-signed.apkdoes the same. -
Publish
assetlinks.json. Per Chrome for Developers, Digital Asset Links consist of a file on your website pointing to your app plus metadata in your app pointing to your website; upload the file to.well-known/assetlinks.jsonrelative to your site root so the browser can verify it. -
Create an app icon, then deploy. Per Chrome for Developers, the next step after the quick start is to create an icon for your app — and once that is done, you can consider deploying your app to the Play Store.
Per Chrome for Developers, PWA Builder provides a GUI interface that uses the Bubblewrap library to power the generation of Trusted Web Activity projects.
Microsoft Store, via PWA Builder
Section titled “Microsoft Store, via PWA Builder”Per Microsoft, publishing a PWA to the Microsoft Store requires no code changes: you create an app reservation in Microsoft Partner Center, package your PWA using PWA Builder, and then submit your package to the Store. Per Microsoft, the advantages it lists are discoverability alongside other Windows apps, the trust Windows customers place in Store downloads (which adhere to the Microsoft Store Policies), a consistent install experience, and analytics about your app’s health and usage in the Partner Center dashboard.
Detect the store build from the web, and fall back
Section titled “Detect the store build from the web, and fall back”Once the same product exists both as a site and as a store listing, the web version should
stop advertising an install the user already completed. Per MDN,
navigator.getInstalledRelatedApps() returns a promise that resolves with an array of
objects representing any related platform-specific apps or PWAs the user has installed,
and MDN names exactly this use: removing “install our app” banners when the app is already
installed.
async function shouldShowInstallBanner() { if (!('getInstalledRelatedApps' in navigator)) { // Not supported here — show the banner rather than hide a working call to // action behind a check the browser cannot answer. return true; } const related = await navigator.getInstalledRelatedApps(); return related.length === 0;}Per MDN, this works only if two things are set up: the invoking web app must be listed in
the related_applications member of its manifest, and the platform-specific app or PWA
must define its relationship back — an Android app via the Digital Asset Links system, a
Windows UWP app via URI Handlers.
Practical checklist
Section titled “Practical checklist”- A signing-key mismatch downgrades your app to a Custom Tab. Per Chrome for Developers, Digital Asset Links take into account the key the APK was signed with, and a common cause of verification failing is using the wrong signature — and failing verification means your site launches as a Custom Tab with browser UI at the top of the page instead of as a Trusted Web Activity.
- Google Play may hold a different key than Bubblewrap did. Per Chrome for Developers,
Bubblewrap builds with a key set up during
init, but when you publish in Google Play another key may be created for you depending on how you choose to handle signing. Check the asset links against the key Play actually ships. - Before asset links are configured, the app is not a TWA yet. Per Chrome for Developers, on first run you will notice your website launches as a Custom Tab, not a Trusted Web Activity, because Digital Asset Links validation has not been set up.
- The host app cannot read your web state. Per Chrome for Developers, the host app has
no direct access to web content in a Trusted Web Activity or to web state such as cookies
and
localStorage. Plan any native-to-web handoff as data passed in URLs. - The web experience is the product. Per Chrome for Developers, the content is rendered by the user’s browser in exactly the same way as in the browser, so anything broken on the site is broken in the store build.
getInstalledRelatedApps()is not dependable everywhere. Per MDN it is experimental and not Baseline, because it does not work in some of the most widely used browsers, and it must be invoked in a top-level secure context — not inside an<iframe>.
Where to go next
Section titled “Where to go next”- Trusted Web Activity reference — the mechanism in reference form.
related_applications— the manifest member the detection above depends on.getInstalledRelatedApps()— the API’s own entry, including its constraints.- Installability criteria — what the browser itself requires before offering an install.
← Back to the Guides overview.