So it seems like desktop applications are dead, or the were… I don’t know. I’d rather a full native app instead of a web-based app. EVERYWHERE! And that includes desktop apps. Electron.js based apps are fast to develop, easy to maintain, and cross platform, but they s*ck$. They are hungry for resources, CPU and memory, as they are based on the always-hungry (Chrome) browser.
So, a few solutions come to scene in the last few years. The most famous I believe it’s Flutter, it covers mobile and desktop, but also web. But it’s not the only one! JetBrains released Compose for Desktop, which targets macOS, Linux and Windows. It’s a port of Android’s Compose for Desktop, what makes this a great alternative for multiplatform apps. If we add Kotlin Multiplatform to this equation for creating Business Logic that can also run on iOS, Kotlin seems like a great starting point for fully native desktop and mobile apps.
Web-based multiplatfom solutions are hungry for resources. Native apps are more resource-efficient, load faster, work better in offline mode.
Similar to the previous article, I will explore the development process of a Linux app using Kotlin Compose, and also will run that app on macOS. Take into consideration that Kotlin Compose is yet an experimental feature.
System Requirements
The only requirement for developing apps using Compose for Desktop, is Kotlin 1.4.20 or newer. A Java Runtime Environment is of course required for this JVM language, and even Kotlin can run on JRE 7 (for example) it’s recommended to use an as new as possible Java Virtual Machine, for example JVM 11 or 13.
At the time of this post there is a bug in JDK 14 that doesn’t allow to generate a Debian package in Linux. If you are using Linux, please switch to either JDK 11 (LTS) or JDK 15.
IntelliJ IDE, made by JetBrains, supports creating Kotlin Compose apps out-of-the-box. Two flavours are available:
- Desktop
- Multiplatform
Desktop targets macOS, Linux and Windows using Compose, while Multiplatform adds Android support.


In this tutorial, a desktop-only app will be created.
Getting Started
When the IDE finished starting up the project, all dependencies are downloaded and the index is updates, a single Main.kt file will be present in your source folder. The entry point of the app is a main function that returns a Window.
A simple Compose app may look like
@ExperimentalAnimationApi
fun main() = Window(title = "Hello, Compose!") {
MaterialTheme {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier.padding(32.dp).fillMaxWidth().fillMaxHeight()
) {
Card {
var expanded by remember { mutableStateOf(false) }
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier.clickable { expanded = !expanded }) {
Image(
bitmap = imageResource("drawable/kotlin.png"),
alignment = Alignment.Center,
modifier = Modifier.padding(32.dp).preferredWidth(128.dp).preferredHeight(128.dp)
)
AnimatedVisibility(expanded) {
Column {
Text(
text = "Hello, Compose!",
style = MaterialTheme.typography.h2
)
}
}
}
}
}
}
}

Distribution
Packaging an application can be a little tricky, you can run the app everywhere using a gradle o maven task, but you can only create a package, a distributable installation file, in the same architecture of the target package. In other words, there is no “cross packaging”. You cannot create a Debian compatible -deb- file from macOS. So, in order to cover all the available flavours, you will need access to Windows, macOS and Debian.
Which, if you have a macOS machine, it’s pretty easy to archive using VirtualBox or any other Virtual Machine.
The package task generates a distributable file for the current platform. There are other related tasks, but I couldn’t successfully run any of the on a platform different that the current on.
$ ./gradlew package
> Task :packageDmg
WARNING: Using incubator modules: jdk.incubator.jpackage
The distribution is written to build/compose/binaries/main/dmg/DesktopApp-1.0.0.dmg
BUILD SUCCESSFUL in 11s
5 actionable tasks: 3 executed, 2 up-to-date
- msi (Microsoft Installer) for Windows
- deb (Debian package) for Debian/Ubuntu compatible
- dmg (Disk Image File) for macOS
Artefacts
The very simple app that you see in this post needs 80MB of disk space, and generates a DMG file of nearly 42MB. Where only 1MB belongs to this application (assets and compiled code). The rest is just Kotlin libraries (coroutines, stdlib, material design, etc).
/Applications/DesktopApp.app/Contents/app
kimi@Kimis-Mac-mini /A/D/C/app> ls -la
total 37024
drwxr-xr-x 38 kimi admin 1216 Mar 23 14:34 ./
drwxr-xr-x 8 kimi admin 256 Mar 23 14:34 ../
-rw-r--r-- 1 kimi admin 104427 Mar 23 14:34 DesktopApp-1.0.0.jar
-rw-r--r-- 1 kimi admin 1522 Mar 23 14:34 DesktopApp.cfg
drwxr-xr-x 3 kimi admin 96 Mar 23 14:34 META-INF/
-rw-r--r-- 1 kimi admin 1522 Mar 23 14:34 MainKt$main$1$1$1$1$1$1.class
-rw-r--r-- 1 kimi admin 9533 Mar 23 14:34 MainKt$main$1$1$1$1$2$1.class
-rw-r--r-- 1 kimi admin 15698 Mar 23 14:34 MainKt$main$1$1$1$1.class
-rw-r--r-- 1 kimi admin 9692 Mar 23 14:34 MainKt$main$1$1.class
-rw-r--r-- 1 kimi admin 2157 Mar 23 14:34 MainKt$main$1.class
-rw-r--r-- 1 kimi admin 1292 Mar 23 14:34 MainKt.class
-rw-r--r-- 1 kimi admin 208635 Mar 23 14:34 animation-core-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 201364 Mar 23 14:34 animation-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 17536 Mar 23 14:34 annotations-13.0.jar
-rw-r--r-- 1 kimi admin 15414 Mar 23 14:34 desktop-jvm-0.2.0-build132.jar
drwxr-xr-x 4 kimi admin 128 Mar 23 14:34 drawable/
-rw-r--r-- 1 kimi admin 822462 Mar 23 14:34 foundation-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 357370 Mar 23 14:34 foundation-layout-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 1488745 Mar 23 14:34 kotlin-stdlib-1.4.20.jar
-rw-r--r-- 1 kimi admin 191485 Mar 23 14:34 kotlin-stdlib-common-1.4.20.jar
-rw-r--r-- 1 kimi admin 22355 Mar 23 14:34 kotlin-stdlib-jdk7-1.4.20.jar
-rw-r--r-- 1 kimi admin 16233 Mar 23 14:34 kotlin-stdlib-jdk8-1.4.20.jar
-rw-r--r-- 1 kimi admin 196243 Mar 23 14:34 kotlinx-collections-immutable-jvm-0.3.jar
-rw-r--r-- 1 kimi admin 1672388 Mar 23 14:34 kotlinx-coroutines-core-jvm-1.4.1.jar
-rw-r--r-- 1 kimi admin 10884 Mar 23 14:34 kotlinx-coroutines-swing-1.4.1.jar
-rw-r--r-- 1 kimi admin 1261868 Mar 23 14:34 material-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 674505 Mar 23 14:34 material-icons-core-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 584609 Mar 23 14:34 runtime-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 19435 Mar 23 14:34 runtime-dispatch-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 56960 Mar 23 14:34 runtime-saved-instance-state-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 248772 Mar 23 14:34 skiko-jvm-0.1.18.jar
-rw-r--r-- 1 kimi admin 8553451 Mar 23 14:34 skiko-jvm-runtime-macos-x64-0.1.18.jar
-rw-r--r-- 1 kimi admin 1378337 Mar 23 14:34 ui-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 38650 Mar 23 14:34 ui-geometry-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 304483 Mar 23 14:34 ui-graphics-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 311880 Mar 23 14:34 ui-text-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 81156 Mar 23 14:34 ui-unit-desktop-0.2.0-build132.jar
-rw-r--r-- 1 kimi admin 11744 Mar 23 14:34 ui-util-desktop-0.2.0-build132.jar
It may seems to much, but, remember that this application embeds a Java Runtime and a lot of libraries that make your live easier developing in Kotlin.
In comparison to Electron.js + React.js I believe that the artefact size is similar, but there is a huge improvement in running a Kotlin / JVM app.