PWA Tools and Packaging
Updated: Feb 4, 2025
LIMITED PWA DISTRIBUTION
Meta Horizon Store currently supports only immersive WebXR Progressive Web App (PWA) distributions. Support for PWA 2D and Hybrid (PWA 2D + immersive) will be available later. This page explains how to use packaging tools to convert a web experience into an APK for Store submission.
Before packaging your PWA, it’s recommended to first create a place for your app using the Developer Dashboard. Follow the steps in
Create apps to create your app page. By doing this, you will also generate an app ID. You need to create an app page and have an app ID before you can create a PWA APK with In-App purchase support.
Bubblewrap is an open-source tool to make wrapping your Progressive Web App into an Android App Bundle as easy as running a couple of CLI commands. It generates an Android project that launches your PWA as a
Trusted Web Activity. Meta uses a custom forked version of Bubblewrap CLI to support the immersive launch experiences and Meta Horizon Store API connections, including in-app-purchase APIs.
Download the Meta Forked Bubblewrap CLI from the
github repo.
Extract the bubblewrap
directory, navigate to the directory, and install the dependency libraries. NOTE: If you are using a Windows device, make sure your NodeJS version is 21.0.0:
cd bubblewrap
npm install
npm run build
Create an alias for the bubblewrap command:
MacOS
alias bubblewrap="/YOUR_DIRECTORY/bubblewrap/packages/cli/bin/bubblewrap.js"
Windows
Set-Alias -Name bubblewrap -Value C:\YOUR_DIRECTORY\bubblewrap\packages\cli\bin\bubblewrap.js
Create and Install a PWA App Package (APK)
Create your Web Manifest file Create a web manifest file for your domain
https://domain.com/manifest.webmanifest if you haven’t already. This manifest file will be used as a baseline to generate configurations for your PWA. Fill in the values that you want for your PWA. You can find more information in the
getting started page.
{
"short_name": "PWA Name",
"name": "Full name of your PWA",
"icons": [
{
"src": "/images/icons-192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "/images/icons-512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": "https://domain.com/startpage/",
"scope": "https://domain.com/",
}
Bubblewrap your application into an APK Once you have the
Web App Manifest file, you can create an App Package (APK) for the Meta Horizon Store using the Bubblewrap CLI.
To use the CLI, create a project directory, and navigate to the directory:
mkdir my-pwa && cd my-pwa
Run the bubblewrap init
command with your website URL:
bubblewrap init --manifest=https://domain.com/manifest.webmanifest --metaquest
This generates a default Android Webapp project from the Web App Manifest. An in-console wizard starts, allowing you to override the default configuration.
ANDROID SDK AND JDK INSTALLATION
When Bubblewrap is run for the first time, it will ask for downloading the Java Development Kit (JDK) and the Android command-line tools. To ensure the correct versions are downloaded for each dependency, we strongly recommend allowing Bubblewrap to download the correct versions instead of using one already available on your computer. You will be asked to select the launch mode of your PWA in Horizon OS. Currently, Meta Horizon OS supports only the immersive
launch mode for WebXR PWAs.
While creating the PWA package using our CLI tool, you must specify a name for the package. The Application ID is important for you to upload to the Store because subsequent updates will require an identical Application ID for each submission.
Make sure the Meta Horizon Store Billing feature is enabled if you want to access the In-App-Purchase functionalities inside your PWA.
Enter your app ID number from Meta Developer Dashboard. You have to provide a correct ID here to make In-App purchase work properly in your PWA. You can find your app ID number in the URL bar of your browser when viewing your app on the App Manager page of the
Meta Developer Dashboard. For example:
developer.oculus.com/manage/applications/
0000000000000000/.
Meta Horizon Store requires application packages to be digitally signed with a certificate when uploaded. All developers must create their own unique digital signature and sign their applications before submitting them to Meta for approval. For more information, check the
Android Application Signing page. Bubblewrap will ask for the path for the key when creating the application.
Note: If you are creating an APK for an existing application listed Meta Horizon Store, you will need to add the path to the same key used by that application.
If you do not have an existing signing key and are creating a new listing on the Meta Horizon Store, you can use the default value provided by Bubblewrap to have it create a new release signing key for you. Make sure to save the keystore file you use to sign your application. All subsequent updates to your application must be signed with the same certificate file.
Signing key information (5/5)
Please, enter information about the key store containing the keys that will be used
to sign the application. If a key store does not exist on the provided path,
Bubblewrap will prompt for the creation of a new keystore.
- Key store location: The location of the key store in the file
system.
- Key name: The alias used on the key.
Read more about Android signing keys at:
https://developer.android.com/studio/publish/app-signing
? Key store location: /YOUR_PATH/android.keystore
? Key name: android
After you’ve initialized your Android Webapp project, you can build your app apk within the same directory you ran Bubblewrap’s initialization command.
Note: You will need the passwords for your signing key for this step.
Make sure the app-release-signed.apk file is generated in your directory.
Create Digital Asset Link for your PWA
What is digital asset link? The Digital Asset Links protocol and API enable an app or website to make public, verifiable statements about other apps or websites. For example, a website can declare that it is associated with a specific Android app, or it can declare that it wants to share user credentials with another website. In the PWA use case this file is used to prove your web app and your PWA are both owned by you.
Digital Asset Link requirement for WebXR PWAs In Meta Horizon OS, WebXR PWAs will not launch if the Digital Asset Link verification doesn’t pass.
Get Your Digital Asset Link file You can get the SHA-256 fingerprint of the signing key you used when building your PWA APK via the following Java keytool command:
keytool -list -v \
-keystore <keystore-file-path> \
-alias <key-alias> \
-keypass <key-password> \
-storepass <store-password> | grep SHA256
$ Signature algorithm name: SHA256withRSA
$ SHA256: BD:92:64:B0:1A:B9:08:08:FC:FE:7F:94:B2...
Create Your Digital Asset Links File using the following command within the same directory created during Bubblewrapping Your PWA. Replace <fingerprint>
with the fingerprint (for example BD:92:64:B0:1A:B9:08:08:FC:FE:7F:94:B2...
) copied from the previous step.
bubblewrap fingerprint add <fingerprint>
This command will add the fingerprint to the application’s fingerprint list and generate an assetlinks.json file. Upload this file to the .well-known
directory on the same website domain as your PWA. (https://domain.com/.well-known/assetlinks.json)
Note: The keytool
command may not be available if you haven’t installed any JDK in your OS already. If you met with this issue, you will be able to find the executionable binary under the JDK directory created by bubblewrap. For example, in Mac OS the keytool script can be found under
${USER_HOME}/.bubblewrap/jdk/jdk-11.0.9.1+1/Contents/Home/bin
Multiple PWAs from a single domain The assetlinks.json file is a json array of package names and fingerprints. It is possible to add the ownership of multiple PWA apps within a single domain. To do that, just concatenate the asset link statements into the array similar to the following
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "pwa.application.id.1",
"sha256_cert_fingerprints": [
"BD:92:64:B0:1A:B9:08:08:FC:FE:7F:94:B2..."
]
}
},
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "pwa.application.id.2",
"sha256_cert_fingerprints": [
"B3:FE:E9:E1:5C:1A:E8:68:5C:1A:16:99:E8..."
]
}
}]
Sideload your PWA to Test
adb install [path to the app package]
Once installed, you can launch your PWA from the App Library on Quest. In the App Library, change the app filter to Unknown Sources, and you should see your app in the list. Click on the app to launch it.
Frequently Asked Questions
My test WebXR PWA app can not launch after installed to the headset. What should I do? The most common issues that causes WebXR PWA not launching is the Digital Asset Link verification not passing. A TWA verification was unsuccessful!
log would be printed to the Device Log in this case. Please double check that both the package name and the signing key fingerprint match what’s listed in the https://domain.com/.well-known/assetlinks.json file.
I’m getting the cli ERROR spawn EINVAL
error. What shoud I do? Try to double check your NodeJS version that is executing the bubblewrap runtime. On Windows devices, please make sure you are using NodeJS 21.0.0 to execute the bubblewrap.js
file.
I’m getting the Android Gradle plugin requires Java 17 to run. You are currently using Java 11.
error. What should I do? This is most likely due to the conflict JDK version of the bubblewrap tool that’s already installed on your device. You can edit the "jdkPath"
field of your config.json
file to point to a Java 17 JDK (the default path of this file is ~/.bubblewrap/config.json
on Mac and C:\Users\zjm\.bubblewrap\config.json
on Windows). Alternatively, you can delete the the "jdkPath"
field in the config.json
file and execute the bubblewrap command to automatically install the compatible JDK file.