In this article, we’ll convert a web app into Android and iOS apps using Ionic Capacitor. We’ll build a dummy web app and then move that web app into a native mobile app, without any additional effort. We’ll define a workflow that renders certain features only on the native app or only on the web app. This will let us add device-centric features, enhancing the end user’s experience.
Table of Contents
- The Native route
- Ionic Capacitor
- Understanding the Web app to Native app workflow
- Extending Web apps to Native apps
Technical Requirements
To follow along with the instructions in this article, you will need to have the following (depending on the platforms you want to deploy too):
- For the Android App: Android Studio on Windows, Linux, or Mac
- For the iOS App: XCode and a Mac
To develop apps for iOS, officially, you need to do so on a Mac with macOS. However, if you aren’t currently developing for iOS, you can still follow along as many of the steps are similar and you can make the step towards iOS later with the required hardware.
Source files for this article are available at the following repository: https://github.com/PacktPublishing/PacktPlus/tree/main/Ionic/Extend-a-web-app-into-Android-and-iOS-apps-using-Ionic-Capacitor
The Native route
There are two approaches available to developers when we build new web apps that need to cater to a mobile audience as well. We either use the responsive web route, where the user accesses our web app through a mobile web browser, or we pick the native mobile app route, where we build native mobile apps for each mobile operating system.
The native apps approach have certain advantages over responsive web app approach:
- Better performance
- Better security
- Offline capabilities
- Access to device features
- Seamless experience when on a mobile/tablet
Let’s look at how we can leverage these advantages while building a web app, without any additional effort.
Ionic Capacitor
Before we begin building an app, let’s understand what Ionic Capacitor is. Capacitor’s describes itself as,
“A brand new approach to building native apps with the Web — now even faster, more modular, and more enjoyable to build with than ever.”
Capacitor introduces us to a new world of apps, Native Web apps. These apps behave like native apps on a mobile device and like progressive web apps when on the web.
You can read more about the latest version of Capacitor 3.0 here: Announcing Capacitor 3.0
Understanding the Web app to Native app workflow
In this section, we’ll look at a workflow that helps us reuse a web app code base to build a native mobile app using Ionic Capacitor.

Figure 1 – Web app to native mobile app workflow
Considering the above workflow, we will start with a web app that we have spent our time and energy over. The web app we have built is a responsive web app that can be rendered on all devices in a responsive way.
Important Note
The web app that we have built follows a Single Page App architecture. This approach of adding native app capabilities to a web app can be achieved on a SPA and not on monolithic apps.
Typically, we build web apps and deploy it to a server, which can be accessed over web browsers.
For mobile, we’ll add Ionic Capacitor to the same code base. This will take care of wrapping the web app in a native shell and using that shell, we can build and deploy the native app to the stores.
Now that we’ve established the workflow, We can follow the steps required to building and deploying our native mobile apps.
Extending Web apps to Native apps
Now that we have a high-level understanding of what we are going to achieve, let’s list the steps to achieve it.
For the sake of this tutorial, we aren’t going to build a web app from scratch. We’ll leverage a Next.js Blog starter template instead. We’ll follow this broad overview to build and deploy the native mobile apps:
- Install Node.js
- Clone web app: https://github.com/timlrx/tailwind-nextjs-starter-blog
- Setup export process
- Add Capacitor
- Add Android and iOS platforms
- Deploy and test Android and iOS Apps
- Workflow to add native only features
As part of our workflow, we are going to integrate a Capacitor plugin with the web app that will work only in the native environment. More on this when we reach line item 7.
So, let’s get started with our 7 steps
Step 1: Install Node.js
You can install the latest version of Node.js by downloading it from here: Node.js Download.
For this tutorial, I’ve used version v14.17.0, as you can see in the following output :
❯ node -v
v14.17.0
❯ npm -v
6.14.13
Step 2: Clone web app
Next, we’ll clone a simple blog web app, built on Next.js. To work with this approach, the web app need not be the sample app as the one used in this tutorial. We can use any headless app that can be converted to a static site and can be hosted on its own.
To clone the web app, run the following command:
$ npx degit https://github.com/timlrx/tailwind-nextjs-starter-blog.git
The above command will clone the web app to the tailwind-nextjs-starter-blog
folder. Next, change directory into this folder and run the following command:
$ npm install
This will install the required dependencies to run the starter blog template.
To view the app in action, run
$ npm start
And when we navigate to http://localhost:3000/
we should see something like Figure 2.

Figure 2 – the deployed web app
This app comes out of box with a blog template and has light and dark mode. You can read more it here.
Now that our production ready web app is up and running, let’s move on to the next step.
Step 3: Setup export process
In this step, we’ll convert and export our Next.js SPA a static site. This way, the static website that gets exported from this code base will be moved into the native app.
Important Note
This export process is specific to Next.js, if you are using Angular or React or any other SPA libraries or frameworks, you’ll have to use instructions for how to build a static site from the code base specific to those frameworks.
For us to successfully export the static site, let’s follow these steps:
- Update the json scripts section and add the following line:
...// snipp
"export": "next export",
...// snipp
- Update config.js > withBundleAnalyzer function with the following line:
...// snipp
images: {
loader: 'imgix',
path: 'https://example.com/myaccount/',
},
...// snipp
Note
To understand more about step 2 above, please refer to the following links:
1. Next.js 10 Image Optimisation Component and next-optimized-images, a brief comparison
2. Image Component and Image Optimization
- Finally run the following commands to export the static site to out folder
$ npm run build
$ npm run export
Once done, the out
directory should look like Figure 3:

Figure 3 – the Out directory for the static site
This is the static version of our website that can be run on any environment that supports JavaScript.
Step 4: Adding Capacitor
The next step on our list is to add Capacitor to our existing web app code. Before we do that, please make sure you have completed the Environment setup.
To add capacitor to our existing code base, run the following commands:
$ npm install --save-dev @capacitor/core @capacitor/cli
$ npx cap init
The second command will create the capacitor.config.json
file at the root of the project once we answer the questions presented to us.
The contents of the capacitor.config.json
file should look like the following:
{
"appId": "com.example.app",
"appName": "tailwind-nextjs-starter-blog",
"webDir": "out",
"bundledWebRuntime": false
}
Do note that I have updated the webDir
property to point to the folder where the static site is generated.
Step 5: Adding Android and iOS platforms
Now that the capacitor configuration has been completed, we will add the Android and iOS platform related code setup to our web app code base.
Run the following commands for Android:
$ npm install @capacitor/android
$ npx cap add android
Run the following commands for iOS:
$ npm install @capacitor/ios
$ npx cap add ios
If everything works properly, we should see 2 new folders, android
and ios
that will have native code for the static website.
You can read more about Android and iOS Capacitor support here: Capacitor Android Documentation and Capacitor iOS Documentation.
Step 6: Deploy and test Android and iOS Apps
Now that we have the Android and iOS apps setup, we will run them to view the output.
iOS App
To deploy an app to iOS, you will need to use XCode on a Mac. If you have the setup, follow these instructions to deploy the app, starting with the following command:
$ npx cap open ios
And then you can use the XCode IDE to run the app.
If you would like to run the app via command line without opening the code in XCode, run the following code on a Mac.
$ npx cap run ios
And we should see the output shown in Figure 4.

Figure 4 – the deployed iOS app
Android App
To deploy the Android app, open the code base in Android Studio by running the following command:
$ npx cap open android
And then you can use the Android Studio IDE to run the app.
If you would like to run the app via command line without opening the code in Android Studio, run the following command:
$ npx cap run android
And we should see the output shown in Figure 5.

Figure 5 – the deployed Android app
Step 7: Workflow to add native only feature
Now that we’ve learned to convert a web app to a native app with all the features of the web one, let’s look at customizing the app to add native only features on top of the web app and make this code run only on the native app.
To explore this option, we are going to implement the Share plugin (https://capacitorjs.com/docs/apis/share) which lets users share posts directly to native apps. We don’t want to use this plugin or behaviour in the web app but only inside the native app.
To achieve this, we need to add the share plugin to our web app and set up the plugin to run the code only when the app is deployed as a like a native app.
- To install the Share plugin, run the following commands:
$ npm install @capacitor/share
$ npx cap sync
- Next, open tailwind-nextjs-starter-blog/pages/index.js and add the following import statement to the plugin at top:
import { Share } from '@capacitor/share'
- Next, we need to identify if the current app is executed as a web app or a native app. For that we are going to use the following snippet
const IS_CORDOVA_APP = typeof window === 'undefined' ? undefined : !!window.cordova
IS_CORDOVA_APP
will be true when we run on devices, using Ionic Capacitor and false when we run on the web
- Next, I am going to create a component to display a “Share” link based on the type of device
const MobileNativeShare = ({ frontMatter }) => {
const { slug, title, summary } = frontMatter
const showNativeShare = async () => {
if (IS_CORDOVA_APP) {
await Share.share({
title,
text: summary,
dialogTitle: slug,
})
}
}
return IS_CORDOVA_APP ? <button onClick={showNativeShare}>Share</button> : ''
}
- And finally after the “Read more” link, we will add the native share link
<MobileNativeShare frontMatter={frontMatter}></MobileNativeShare>
- Now we’ll build, export and sync the code base. Once done, we will run the app on Android and iOS to validate. Run the following comments to do this:
$ npm run build
$ npm run export
$ npx cap sync
$ npx cap open android
$ npx cap open ios
And now in the native app, we should see the Share link:

Figure 6 – The native app with a Share option
- If we tap on it, we should see the share sheet for each device, as shown below

Figure 7 – Share sheets for Android and iOS
With that, our native mobile app is done!
Summary
In this article, we learned to convert an existing single page app style web app to a native app and add native app only features, to enhance the experience, without losing the cross-platform reusability using Capacitor. This cross-platform development approach is highly suitable for projects where we want to publish our app as a native app and at the same time deploy a web version with the same code base.
___________________
About the Author
Arvind Ravulavaru is a platform architect at Ubiconn IoT Solutions, with over 9 years of experience in software development and 2 years in hardware & product development. For the last 5 years, he has been working extensively on JavaScript, both on the server side and the client side. Over the past couple of years his focus has been on IoT, building a platform for rapidly developing IoT solutions, named The IoT Suitcase. Prior to that, Arvind worked on big data, cloud computing, and orchestration.
Leave A Comment