Native mobile Apps give us a lot of magnificent opportunities compared to the pure web site or PWA: with native apps users can allow to share data with messengers and other apps, get access to a contacts list, use a device accelerometer, and permanently share their location. All these and many more features are very limited or missed on the web or PWA because of strict security considerations.

Moreover, native apps could be published to Android's Google Play Store or Apple's App Sore where users can share their reviews and feedback which in the case of qualitative apps is a great benefit to marketing efforts.

After years in web development, I still knew about Mobile application development nothing. But  I’ve faced with huge amount of cases when clients wanted to implement some features that are available for native apps but could not be implemented to PWA or web browsers.

Some time ago we started to implement these cases with a Flutter app. We created a “Wrapper application” which allowed us to get native App features using WebView native-to-javascript bridge. This approach gave good results, but there was one small but necessary thing - using Flutter required some minimal knowledge of Dart language and we always spent extra time to write every permission and sensor handling from scratch.

But then we figured out the Open-Source and commercial-friendly framework with a ton of pre-made native modules already exists in this world. And this is CapacitorJS. Yes, also there is an old-school Cordova, but according to our benchmarks, it is slow and consumes more RAM.

In this post I will give you simple guide how to create CapacitorJS app on top of existing Nuxt web app.

Our final aim is - to create an AAB/APK file from our website. Let’s go on!

Step 1. Create your web app (or you can generate AAB/APK files for existing site, but just for example I will create a new project. 

npx nuxi@latest init <project-namne>
cd <project-name>
npm install 

Step 2. Generate entry index.html file. For this step we should disable SSR mode in nuxt.config to get SPA version of our project. To simplify process of generating file change nuxt generate script in package.json:

{
  "scripts": {
    // ...
"generate":"NUXT_TO_NATIVE_APP='true' nuxt generate"
}
}

And add condition to nuxt.config.ts:

export default defineNuxtConfig({
  devtools: { enabled: true },
  ssr: process.env.NUXT_TO_NATIVE_APP == 'true' ? false : true,
})

Then execute 

npm run generate

Check out .dist directory , it should contain index.html file which is ready for deployment. We will return to it later.

Step 3.Install Capacitor

# Install the Capacitor CLI locally
npm install -D @capacitor/cli

# Initialize Capacitor in your Nuxt project
npx cap init

# Install the required packages
npm install @capacitor/core @capacitor/ios @capacitor/android
# Add the native platforms
npx cap add ios npx cap add android

By this point, you should be able to observe new ios and android folders in your Nuxt 3 project.

Add default directory which included our index.html file (result of the executing "generate" command) at capacitor.config.json:

{
  "appId": "com.example.app",
  "appName": "TestApp",
  "webDir": "dist"
}

Now we are ready launch our native App. Update nuxt files ( if we have some changing ) :

npm run generate

And synchronize native app code with web app code :

npx cap sync

Step 4. Prepare for build.

Install openjdk:

sudo apt install -y openjdk-18-jdk
sudo apt install -y openjdk-18-jre

Add path to env:

export JAVA_HOME=/path/to/openjdk-18

First of all we have to generate keystore. Navigate to android directory of our web project and execute command:

keytool -genkey -v -keystore <name>.keystore -alias <name> -keyalg RSA -keysize 2048 -validity 10000

In my case it was something like this :

keytool -genkey -v -keystore test.keystore -alias test -keyalg RSA -keysize 2048 -validity 10000

You will get <name>.keystore file in  android directory.

During generating of key you should enter key password, please remember it , we'll use it in build configurations.
Now we will add buildOptions to capacitor.config.json:

{
  ...
  "webDir": "dist",
  "android": {
    "allowMixedContent": true,
    "buildOptions": {
      "keystorePath": "<name>.keystore",
      "keystoreAlias": "<name>",
      "keystorePassword": "<password>",
      "releaseType": "APK",
      "signingType": "apksigner"
    }
  }
}
sudo apt update && sudo apt install android-sdk

And add sdk-manager. Since Ubuntu 22 (Jammy), you can simply install it via :

sudo apt install sdkmanager

Add variables to env:

export ANDROID_HOME=/path/to/sdk
export PATH=$PATH:ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$ANDROID_HOME/platform-tools

Pass to sdk depends on your OS , in my case it is "/usr/lib/android-sdk".

Now we have to accept all licenses.

Add permissions to write into directory of licenses:

sudo chmod -R a+w /usr/lib/android-sdk/licenses/

And add licenses:

sdkmanager --licenses

Add permissions to write into directories for packages:

sudo chmod -R a+w /usr/lib/android-sdk/

Install the packages:

sdkmanager "platforms;android-33"

sdkmanager "build-tools;30.0.3"

Run build:

npx cap build android --keystorepath test.keystore --keystorepass testpassword --keystorealias test --keystorealiaspass testpassword --androidreleasetype APK

That's all . After this step you get APK file.