Bubblewrap: How To Publish Your Progressive Web App (PWA) In The Google Play Store

Traditionally, when developers wanted to distribute their web-based apps through app stores like Google Play, there was only one option: Apache Cordova. In the meantime, Progressive Web Apps (PWA) have arrived. The life of a PWA starts in the browser, and users can choose to install the application to the home screen for faster access. Still, there are lots of reasons why developers might want to distribute their applications via app stores. With the help of Trusted Web Activities (TWA) and Bubblewrap, developers can distribute their existing PWA through the Play Store for Android and Chrome OS, combining the power of both worlds.

In this article:

Christian Liebel is consultant at Thinktecture, focuses on web standards and Progressive Web Applications, and is Thinktecture's representative at the W3C.

What Are Progressive Web Apps, Anyway?

Progressive Web Apps (PWA) are web-based applications that fulfill specific criteria, such as being responsive, app-like, and offline-capable. Leveraging a single code base, PWAs run on any modern browser (Chrome, Edge, Firefox, Safari), any desktop operating system (Linux, macOS, Windows, Chrome OS), and any mobile OS (Android, iOS, iPadOS). Developers can deploy PWAs by merely uploading their source files (HTML, CSS, JavaScript, and assets) to a server—no approval process, no app store guidelines. The PWA can be updated by uploading new files to the server.

Progressive Web Apps are deployed like any other website—a Service Worker makes them available offline.

Users open a Progressive Web App by entering its URL in the browser. The browser then downloads the source files of the application. PWAs use a Service Worker, a central proxy, to store a copy of the source files in a local cache. When offline, the Service Worker delivers the source files from the cache, and the application continues to work. Typically, PWAs follow the Single-Page Application (SPA) pattern. This kind of application downloads all source files once and then works in-memory, i.e. view changes do not lead to roundtrips to the server. This approach makes this type of application very performant and mimics the way native apps work.

If users want faster access to the Progressive Web App, they can add it to the home screen or program list of their platform. This is when the Web App Manifest comes in. A manifest is a JSON-based file, typically named manifest.webmanifest, or manifest.json. The manifest defines the appearance of the installed Progressive Web App, its name, icon, display mode, and so on.

There are many reasons why developers would want to publish their applications in app stores:

  • they have existing apps in the store with a large user base
  • they expect their users to search the store to install applications
  • their competitors have a store app as well

Trusted Web Activities to the Rescue

Thanks to Trusted Web Activities (TWA), developers can reuse the same PWA deployment and create a Play Store package without writing a single line of native code. In the case of TWAs, developers upload an almost empty APK to the app store. The APK only contains a link to the website that should be shown.

Backed by a library called android-browser-helper, a TWA opens a compatible browser (Chrome 72+, Edge 45.05+, Brave, Vivaldi, Fenix/Firefox Nightly). It creates a Custom Tab in which the PWA is hosted. This way, the TWA shares its state (cookies, storage) with the browser. If no compatible browser is installed, the application will not launch, but start to the default browser with the PWA’s URL instead.

Trusted Web Activities link to an existing PWA. Digital Asset Links make sure it is authorized to do so.

The term trusted in TWA indicates that a trust relationship is required between the TWA and the website delivering the source files. Websites have to authorize the resulting APK bundle by providing its public signing key. That means developers need to upload the Digital Asset Links (a file called assetlinks.json) to the .well-known folder of the origin they want to deliver their source files from (e.g., https://example.com/.well-known/assetlinks.json). If the APK is not signed with the matching private key, the TWA will not launch, but the default browser will open with the given URL instead.

In the last step, the developer uploads the APK bundle into the Google Play Store where it has to meet the store’s guidelines and be approved, same as any native app. When opened for the first time, TWAs show a banner (e.g., Running in Chrome) at the bottom of the screen. Apart from that, there’s no visible difference between native applications and a TWA.

Please note that PWAs, even when shipped as a TWA, can only call web platform APIs. Fortunately, many APIs with native power have landed on the web over the past years (such as push notifications, hardware-accelerated 2D/3D visualizations, gamepad/pen/touch input, or clipboard access). Thanks to efforts like Project Fugu, the Web App Gap will continue to close in the future. However, in contrast to applications packaged with Apache Cordova (or its contender Ionic Capacitor), TWAs cannot call arbitrary native APIs.

Frictionless TWA Generation with Bubblewrap

Bubblewrap is an open-source command-line tool that simplifies TWA generation. It requires Node.js 10 or above, the Java Development Kit (JDK) 8, and the Android SDK installed on the system. Developers can install the CLI by calling npm i -g @bubblewrap/cli on the command line. To create a new Bubblewrap project in the current directory, developers call bubblewrap init --manifest https://example.com/manifest.webmanifest, where --manifest points to the Web App Manifest of the PWA. Bubblewrap then downloads the manifest and launches an interactive assistant. When running for the first time, the CLI asks for the locations of the JDK and Android SDK. More information about the SDK locations on the different platforms can be found in the Bubblewrap CLI documentation.

Bubblewrap's interactive assistant guides the developer through the TWA generation process

The interactive assistant asks for different properties the resulting Android application should have, such as the application name, icons, or colors. Where applicable, the assistant suggests using the values that were defined in the PWA’s Web App Manifest. Also, the developer either needs to provide an Android Keystore (in case they already have an existing Play Store app), or create a new one. The result is a native Android app project that can also be opened in Android Studio. The project configuration is stored in the file twa-manifest.json.

To compile an APK from the project, developers run the bubblewrap build command. As part of this process, Bubblewrap also validates the Progressive Web App by running it against the audit tool Lighthouse, which checks the correct usage of PWA features and other best practices. Also, Bubblewrap creates the assetlinks.json file that must be uploaded to the PWA’s origin, as noted above.

As the Progressive Web App updates automatically and separately, the Trusted Web Activity theoretically would not have to be touched again. In case developers need to update their TWA for some reason, they can run the bubblewrap update command, which automatically increments the APK’s version code.

Case Study: Top-Notch Web Apps for Android 6

We recently migrated our first customer to TWA and traditionally we would have used a Cordova app. The customer wanted to replace their existing native Android application in the Play Store by their new web application. Apart from modern Android devices, the customer needs to support devices down to Android 6.0 Marshmallow. The integrated WebView of Android 6.0, which Cordova would use by default, doesn’t support all the CSS features the client’s web application requires. Crosswalk, which bundles a Chromium browser into the application package, would significantly increase the bundle size and is no longer maintained, posing a security risk to users.

Fortunately, recent versions of Chrome can be installed on this Android version, and the customer already had a working PWA. So, we generated a Trusted Web Activity from the PWA with the help of Bubblewrap. Since the CLI allows modification of the app version and offers to provide an existing Keystore, it makes it very easy to replace existing native applications in the store. There’s no visible difference between a native application and the TWA. As a result, Trusted Web Activities are also a great tool to provide modern web apps via the Play Store for legacy devices.


Our recommendation for developers is to aim for a Progressive Web App whenever possible. Using the very same codebase and easy way of deployment, developers cannot only address browsers, desktop, and mobile systems, but also app stores like Google Play, the Microsoft Store, or the Samsung Galaxy Store (currently US-only). For the Apple App Store, developers need to ship their application as a part of the application bundle, which requires wrappers like Apache Cordova or Capacitor.

In case you want your PWA to be present in the Google Play Store, Trusted Web Activities and Bubblewrap are the way to go. The process is frictionless, both for Play Store newcomers and for developers who want to replace their existing native application with a PWA. Again, the cross-platform story holds up: One codebase, maximum reach.

Thanks to André Bandarra and Ursula Brummack for reviewing this post.


Current articles, screencasts and interviews by our experts

Don’t miss any content on Angular, .NET Core, Blazor, Azure, and Kubernetes and sign up for our free monthly dev newsletter.

EN Newsletter Anmeldung (#7)
Related Articles
One of the more pragmatic ways to get going on the current AI hype, and to get some value out of it, is by leveraging semantic search. This is, in itself, a relatively simple concept: You have a bunch of documents and want to find the correct one based on a given query. The semantic part now allows you to find the correct document based on the meaning of its contents, in contrast to simply finding words or parts of words in it like we usually do with lexical search. In our last projects, we gathered some experience with search bots, and with this article, I'd love to share our insights with you.
If you previously wanted to integrate view transitions into your Angular application, this was only possible in a very cumbersome way that needed a lot of detailed knowledge about Angular internals. Now, Angular 17 introduced a feature to integrate the View Transition API with the router. In this two-part series, we will look at how to leverage the feature for route transitions and how we could use it for single-page animations.
.NET 8 brings Native AOT to ASP.NET Core, but many frameworks and libraries rely on unbound reflection internally and thus cannot support this scenario yet. This is true for ORMs, too: EF Core and Dapper will only bring full support for Native AOT in later releases. In this post, we will implement a database access layer with Sessions using the Humble Object pattern to get a similar developer experience. We will use Npgsql as a plain ADO.NET provider targeting PostgreSQL.