A complete Guide: “Building a CapacitorJS application using pure JavaScript and Webpack”

Luca R.
10 min readAug 26, 2021

Developing a mobile application is not limited to using native code anymore. With the introduction of PWAs (Progressive Web Applications) new cross-plattform runtimes emerged and are now the foundation of many web-based applications present in the app stores.

Making use of CapacitorJS we can deploy a simple website written in JavaScript as an application for Android, IOS or the web itself (PWA) using one single codebase. Therefore developing costs are significantly lower and coding is much more efficient.

In this article we are going to focus on creating an application that is written in pure JavaScript and uses no other frameworks. Because using pure JavaScript in 2021 is a bad idea overall, finding easy tutorials online on how to build your application or use native plugins (which Capacitor supports btw) was almost impossible - But now here we are!

‣ What do we need?

Because we are working with JavaScript Packages provided via npm, make sure you have Node.js installed before proceeding (npm will install alongside):

  • Node.js (v14.16.1) or higher
  • NPM (v7.6.2) or higher
  • Android Studio (any platform) for Android apps and/or
    Xcode (macOS only) for IOS apps

Important: You will only be able to build and publish IOS apps using a macOS device!

‣ How to get started?

First up we need to create our basic Capacitor application. Make sure you are in the right folder and run the following command in the command line:

npm init @capacitor/app

Install the package, follow further instructions requiring your input and you will find the newly created application directory in the current folder. With Capacitor v3 the structure of this directory should look like this:

www
css
⨽ style.css
js
⨽ capacitor-welcome.js
⨽ index.html
⨽ manifest.json
.gitignore
capacitor.config.json
package.json
README.md

After doing so, we are setup and good to go!

‣ Restructuring the project

Because we will use Webpack to bundle our JavaScript files later on, we need to refactor the given folder structure a little bit:

  • Create a new folder src in the main directory
  • Create the new script file index.js in src/
  • Create the Webpack config file webpack.config.js in the main directory
  • Remove the capacitor-welcome.js file from www/js/

After completion our new folder structure should look like that:

src
⨽ index.js
www
css
⨽ style.css
js
⨽ index.html
⨽ manifest.json
.gitignore
capacitor.config.json
package.json
README.md
webpack.config.js

‣ Modify for use of pure JavaScript

The basic structure is done, though we still have to change the contents of some files to start working with pure JavaScript. We will start by changing our index.html file.

www/index.html

1. If we are not planning to release the application as a PWA we can safely remove the two given script imports for Ionic PWA Elements found in the documents header:

<script type="module" src="https://unpkg.com/@ionic/pwa-elements@latest/dist/ionicpwaelements/ionicpwaelements.esm.js"></script><script nomodule src="https://unpkg.com/@ionic/pwa-elements@latest/dist/ionicpwaelements/ionicpwaelements.js"></script>

2. Then we will remove the <capacitor-welcome> HTML element from our body which was originally defined inside the deleted capacitor-welcome.js file.

3. Next we are going to change our script import from capacitor.js to js/main.js . This file will later contain our bundled JavaScript code, hence the capacitor.js file (which includes the Capacitor Core if not imported via ES2017) is no longer needed.

4. Lastly the js/capacitor-welcome.js import gets removed and our index.html should be done!

webpack.config.js

To start bundling our Node.js modules using Webpack we will need to define a very basic config file including important information like the output destination of our bundled script. The following config contents will take the file src/index.js and bundle it for production as www/js/main.js :

const path = require('path');module.exports = {
mode: "production",
entry: "./src",
output: {
path: path.resolve(__dirname, "www/js/"),
filename: "main.js"
}
};

capacitor.config.json

By default the capacitor.config.json should include a property called "bundledWebRuntime": true which we need to switch to false . This will prevent Capacitor from bundling the files and we can use Webpack instead.

We now got the basic setup for a Capacitor application running and can start using it!

‣ Let’s use Capacitor!

To try out our setup we will create an application that displays the device information using Capacitors Device Plugin featured in v3.

Because we use Webpack to bundle all necessary files in the process, we can just install and import any module for use right away. To use Capacitor and its Device Plugin run the following command in the main directory:

npm install @capacitor/device

src/index.js

Now that we installed our first plugin we can start modifying the script. Using ES2017 we will import Capacitor Core and the Device Plugin:

import '@capacitor/core';import {
Device
} from '@capacitor/device';

Next we will go ahead and create a Promise-based function to get the device information from our native device (A more in-depth documentation on how to use each Capacitor Plugin can be found here):

async function getDeviceInfo() {
let info = await Device.getInfo();
return info;
};

Afterwards our goal is to display the device information in our HTML after the DOM has completed loading. Therefore we need to add an EventListener for DOM-loading and call our function:

window.onload = start;function start() {
getDeviceInfo().then(info => {
document.body.innerHTML = JSON.stringify(info, null, 4);
});
}

We now completed our setup and can proceed to bundling and testing!

‣ Webpack for bundling!

After all necessary changes have been made, we can now bundle our application using Webpack to try it out! But first we will need to install the Webpack CLI in our project by using:

npm install -D webpack-cli

After the installation we will then be able to run the bundler by calling:

npx webpack

Go ahead and open www/index.html in the browser of your choice. You should now be presented with your current device information. If that is the case, you can start developing your frontend!

Important: Every time you change the contents of your src/index.js , you will need to run npx webpack again before refreshing your index.html .

‣ Further development

Now that we tested our Capacitor application and can be sure everything is working as expected, it is time to start the real development:

  1. If you have already created a website in JavaScript you can go ahead and port your code over to our newly created project. Just make sure to not delete any important parts of the above shown.
  2. If you want to start creating the app from scratch just code a website within the given files and you should be good to go!

After completing the basic application you can come back and continue with the next steps, but I still got some more advice based on personal mistakes and common questions:

Avoid using HTML Events

One personal mistake of mine was to rely on onclick-Events inside my HTML instead of using EventListeners. After coding my website and porting it to Capacitor and Webpack I found out that all my previously working events were not working anymore. They were reffering to functions that no longer existed, because Webpack bundles your script into a private object, hence no longer accessible from outside.

Defining events inside HTML elements is not a good practice overall but being forced, I quickly changed all my events to EventListeners using JQuery:

$("#test-id").on("click", (event) => {
// execute your functions
})

Using multiple JavaScript files

Another often asked question is how to use multiple JavaScript files. To answer this question we have to consider every possibility:

  1. Use of a static JavaScript file without a need to bundle:
    Place your static file inside www/js/ and make sure it references no functions from bundled files. Then simply import it in your index.html before any other script file that uses its functions.
  2. Use of a JavaScript file importing modules needing to be bundled:
    Refer to Webpack’s documentation on how to create multiple entry points in your configuration file. After that make sure to import all of your bundled entry points in your index.html as seperate scripts.
  3. Use of a JavaScript file as an exported module to bundle alongside:
    You can export functions from a JavaScript file as a module by using ES2017 exports. The last step would then be to import the module inside one of your already bundled files like src/index.js in our case.
  4. Use of any other Node.js module you can find on npm:
    As already shown Node modules can be seamlessly integrated into our work environment as all Capacitor plugins already are modules by themselves. Import npm modules as you would for any plugin and you are done!

Implement Cordova Plugins

As Apache Cordova gets older and is being surpassed by newer runtimes, there are still great native plugins out there which can be used with Capacitor. Some might be broken, but most of them still work flawlessly! Just install them using npm and import as described on their according page - but make sure to check their device availability beforehand.

‣ Prepare to build your application!

Now that the application is in a working state and we are either ready to test a native device or publish it to the app stores, we will make use of the Capacitor CLI to sync our files and build native code.

Start by selecting the platforms we would like to build our application for. We therefore need to install the corresponding modules before adding the platform to our project using the command line:

npm install @capacitor/android
npx cap add android
npm install @capacitor/ios
npx cap add ios

After adding the designated platforms to the project we will synchronize the website files by calling:

npx cap sync

This should then copy all needed files including plugins to their respective platform folders.

Important: Make sure to first run the bundler with npx webpack and then use npx cap sync every time you change the contents of your script files!

‣ Let’s Build!

Because we are mainly focussing on Android and IOS applications with Capacitor in this article, this part will be split into two segments trying to explain the procedure for each platform as best as I can:

Building for Android

To get started with Android make sure you have Android Studio installed and run at least once! Inside of Android Studio head over to Tools ‣ SDK Manager and install an SDK with an API Level 21 or greater.

Next install the necessary tools located in the SDK Tools tab:

  • Android SDK Build-Tools
  • Android SDK Command-line Tools
  • Android Emulator
  • Android SDK Platform Tools

After finishing the installation we can open our project in Android Studio by running following command in the main directory:

npx cap open android

Now that our project is open, we are almost done with the most basic steps of building and running our application. We simply need to choose a device to run our application on and click the green play button next to our device selector in the top-right corner. The emulator should now startup and run our app on a native device.

Instead of using an emulator we can also plug in our own Android devices to run the app directly on there!

For more detailed information on how to configure your Android application for deployment check out the official documentation here.

Building for IOS

As you will need a device with macOS, bulding and running the application for IOS can be a bit more complicated. I would strongly recommend using the terminal for opening and configuring your Xcode files. Make sure to have Xcode installed and run at least once!

Start of by installing CocoaPods for managing Capacitor packages on your macOS device by executing:

sudo gem install cocoapod

Inside your main project directory you should find an ios/ folder after adding the IOS platform to your project. Navigate to ios/App/ and copy the contents of ios/App/Podfile for later. You will then have to delete this file.

Now open the <ProjectName>.xcodeproj file inside the same folder. You should be presented with some errors inside of Xcode. Navigate the folder structure on your left, locate the Pods folder and delete following files:

Pods-<ProjectName>-debug.xconfig
Pods-<ProjectName>-release.xconfig

After doing so make sure to close Xcode and head back to your ios/ directory in the terminal. To initialize new Podfiles execute:

pod init && pod install

We will then have to overwrite the contents of the newly generated ios/App/Podfile with our before coppied contents. Save the file and run the following in ios/ :

pod install

These steps made sure to reference every dependency correctly for emulating and building. Now that fixing our project is done make sure to open <ProjectName>.xcodeworkspace instead of <ProjectName>.xcodeproj in Xcode - Capacitor should do so by default running:

npx cap open ios

Finally our IOS project is setup the way we need it to be and we should be free of any errors stopping us from running our app on a native device. Head to the device selector on the top-left and choose a device you would like to emulate your application on. Now press the play button to its left and the emulator running you app on a native device should startup.

For more detailed information on how to configure your IOS application for deployment check out the official documentation here.

‣ What now? Deploying!

After running and testing your applications on either platform you can go ahead, finish the configurations needed and publish your app to the stores.

Because we are running a native application using WebViews to display our JavaScript frontend, you are perfectly fine following other great articles on deploying your app for the respective platforms!

You do not need to worry about Capacitor anymore! Just search for “publishing from Android Studio or Xcode” and you are done!

‣ Wish you best of luck!

I hope this article was helpful and I was able to provide insights on a complex topic! Feedback would be appreciated, follow to not miss any new articles and have a nice day - Peace ✌!

--

--

Luca R.

Coding nerd, space addict and IT college student from Germany.