We rewrote our native app using Kotlin/JS Kunafa and Capacitor. And it was GREAT.

We have been developing native apps for more than 7 years (mostly for clients) and our own service Balsam App also has been developed as a native app. Kotlin is our language of choice for development. We have been using it since 2016 for the Android and the backend. So when we wanted to develop web apps and dashboards for our applications we naturally went for Kotlin/JS. We found that our productivity using Kotlin/JS for web apps is much greater than using Kotlin for Android (not even considering that the iOS app must be built separately).

I will be focusing more on comparing the Kotlin/Js wep app development to Android development, as Kotlin is an official language for Android and I think it is a direct comparison.

Why Kotlin/JS and Kunafa

Compared to native Android development, building web apps using Kunafa is a much enjoyable experience.

The full power of Kotlin is still here

For us, Kotlin by far is the best thing that happen to Android, and using it for the web means that we can have the full power of static typing, pragmaticality, and conciseness. Moreover, Coroutines in the JS word is as wonderful as it is in the JVM. For example, all our network calls in both Android and the web uses coroutines as such

             before = { updateAppointmentStatusUiState.value = BasicUiState.Loading },
             onConnectionError = { updateAppointmentStatusUiState.value = BasicUiState.Error }
        ) {
            response = ServerCaller.updateAppointmentStatus(appointment.id, newStatus.name)
            updateAppointmentStatusUiState.value = BasicUiState.Loaded


In fact, beside the UI stuff, most business logic files such as ViewModels are indistinguishable from their counter parts in Android.

The richness of the JavaScript world

Love it or hate it (for us, definitely hate it), JavaScript is very popular and there are libraries for mostly everything. Integrating JS libraries with Kotlin is a breeze and if you have the library type definition, you can convert it to Kotlin with Dukat. If you don’t, check DefinitelyTyped first, you might find it there.

UI DSL vs XML files

Writing UI in Kunafa is done by using its DSL. It is much easier to organise and refactor UI components in Kunafa. This allow us to have a UI code that’s very descriptive and easier to maintain. Here’s a snippet from one of our files:

verticalLayout {
    style {
        width = weightOf(2)
        height = matchParent
    horizontalLayout {
        style {
            width = matchParent
    verticalLayout {
        style {
            width = matchParent
            height = weightOf(2)
            maxHeight = 500.px
            transition = "max-height 0.4s linear"
            transitionDelay = "0s"

In comparison, using XML has always been a pain point for us to manage and handle. They are very verbose, and not so easy to refactor and extract to a different file.

Passing values between components

Passing data between Android activities is weird and annoying! We have to use intent extras and data must either be primitive or serialized. We used to use Gson to serialize data objects and pass them through activities. In the JS world (and in Kunafa), we have full control over the creation of all UI components. As such we can pass data in any format and using normal data objects.

Code architecture

We use MVVM architecture, and we adopted it in the Kotlin/Js world as well. Kunafa has Observable class but you can use any Observable class (or write your own). The architecture in all of our apps are very similar and it works great. The logic is separate from UI definitions and updated using Observables only.

Capacitor is fun

Capacitor does not interfere with your web development. For our case, we developed the app as a normal web app without any knowledge or experience with Capacitor. When it was almost finished (and pretty much functioning), only then we started integrating Capacitor to work with it and it was seamless.

When it comes to native projects files, we like the philosophy of Capacitor. Capacitor allows you to have full control of the Android and iOS files and you configure it exactly like you want. Nothing is hidden, nothing is behind the scene.

The API of Capacitor is also very clear. It took us less than a week to finish integrating it with the web app (that includes configuring the project, handling the back button on Android, push messages and everything else).

If you need some native functionality that Capacitor does not provide (e.g. Android in-app updated), it very easy to write native code without the overhead of writing a complete Capacitor Plugin. After all, you have full control of the native project and can see everything that happen.

The downside

You need more effort to make the app feel native

Since we cannot use native components, we had to make the UI elements look like their native counter parts. Though frankly if you are using Ionic widgets you might not need to that yourself.

Here’s how our hybrid app look.


Fun fact, notice how the status bar is black? We did not notice it until the app is released, but once we did, we wanted to change it to match the light UI of the app. And there it was, Capacitor API to change the status bar appearance.

Performance. Or is it?

I really don’t think there is a performance downside. Rendering some elements on the screen and getting some data from the internet is not that intensive and current devices can handle this with breeze. There are studies and benchmarks that shows that this indeed is the case.

Conclusion. Our experience

We launched the hybrid app a few days ago, and we haven’t got any negative feedback yet. From a development point of view, our productivity multiplied. Developing web apps with Kotlin is a better experience that building Android app, and you only need to write the code once for Android and iOS. Although you need to take extra care to make you app look and behave like a native app. Here’s the link to our app Balsam. Moving forward, I think many of the apps will be converted to Kotlin/JS as well as our future apps.

Leave a Reply

Your email address will not be published. Required fields are marked *