
Embed Compose UI into native windowing hosts, GPU Metal renderer for smooth resizing/scrolling, GraalVM native-image support, multi-window/multi-runtime, Gradle plugin automates native build.
Compose in native macOS window
[!IMPORTANT] This project is experimental. API and features are subject to change.
Gradle
// build.gradle.kts
plugins {
id("io.github.letmutex.compose.nativehost") version "<VERSION>"
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.compose.multiplatform)
}
kotlin {
jvm("desktop")
sourceSets {
val desktopMain by getting {
dependencies {
implementation("io.github.letmutex.compose-native-host:runtime:<VERSION>")
implementation(compose.desktop.currentOs)
}
}
}
}
composeNativeHost {
appName.set("Sample")
bundleIdentifier.set("example.sample")
}
// Compose Desktop configs
compose.desktop {
application {
mainClass = "example.MainKt"
}
}Kotlin
// src/desktopMain/kotlin/example/Main.kt
package example
import letmutex.compose.nativehost.ComposeNativeHost
fun main() = ComposeNativeHost {
App()
}Swift
// src/macosMain/swift/SwiftUiApp.swift
import SwiftUI
import ComposeNativeHost
final class SampleAppDelegate: ComposeAppDelegateBase {
fileprivate lazy var runtime = makeComposeRuntime(
configuration: ComposeRuntimeConfiguration(kotlinMainClass: "example.MainKt")
)
}
@main
struct SampleApp: App {
@NSApplicationDelegateAdaptor(SampleAppDelegate.self) private var appDelegate
var body: some Scene {
WindowGroup {
ComposeView(runtime: appDelegate.runtime)
}
}
}Use a GraalVM JDK that already has native-image available under bin/.
Environment
-PgraalvmHome=/path/to/graalvm, GRAALVM_HOME=/path/to/graalvm, org.gradle.java.home=/path/to/graalvm, or JAVA_HOME=/path/to/graalvm.macosNativeImageRun and the native image bundle tasks use that GraalVM home directly.macosRun still uses a regular JVM launch path.Gradle
composeNativeHost {
appName.set("Sample")
bundleIdentifier.set("example.sample")
nativeImage {
mainClasses("example.MainKt")
}
}Swift startups
final class SampleAppDelegate: ComposeAppDelegateBase {
override init() {
super.init(configuration: ComposeHostConfiguration(startups: [.jvm, .sharedLibrary()]))
}
}Use .jvm so the same host still runs without a bundled shared library, and add .sharedLibrary() so the staged native image bundle can bind the generated Graal runtime when present.
Use macosRun for the staged JVM bundle or macosNativeImageRun for the staged native image bundle.
./gradlew -p samples :appkit:macosRun
# run the native image bundle
./gradlew -p samples :appkit:macosNativeImageRun# build JVM / native image .app
./gradlew -p samples :appkit:macosCreateDistributable
./gradlew -p samples :appkit:macosNativeImageCreateDistributable
# package JVM / native image .dmg
./gradlew -p samples :appkit:macosPackageDmg
./gradlew -p samples :appkit:macosNativeImagePackageDmg
# package JVM / native image release .dmg
./gradlew -p samples :appkit:macosPackageReleaseDmg
./gradlew -p samples :appkit:macosNativeImagePackageReleaseDmgruntime: shared host API plus macOS runtime, native bridge, and native host sources.gradle-plugin: Gradle plugin for macOS launcher generation, bundle staging, and macOS app tasks.samples/compose: pure Compose Desktop sample app and shared Kotlin sample content.samples/appkit: AppKit-owned sample window using the shared hosted Kotlin sample.samples/swiftui: SwiftUI-owned sample window using the shared hosted Kotlin sample.samples/swiftui-min: minimal SwiftUI-owned sample extracted from the setup shown above.samples/mixed: AppKit-owned window with SwiftUI content composition around the hosted Compose surface.Debugging environment variables:
COMPOSE_NATIVE_HOST_FRAME_TIMINGS=1: Enable logging of frame timings (dispatch delay, input drain, scene render, etc.) to stdout for performance analysis.COMPOSE_NATIVE_HOST_VSYNC_DELAY=1: Enable analysis of VSync signal delivery delay from the display link to the Kotlin runtime.You own the App entry point in Swift. Your app's Swift sources (e.g., in src/macosMain/swift) define the AppKit/SwiftUI lifecycle. The plugin compiles these along with shared runtime helpers into the final native binary.
ComposeHostRuntime, ComposeView). These Swift sources are bundled inside the Gradle plugin and extracted during the build to be compiled into your app.A thin Objective C bridge handles low level JNI communication between the Kotlin JVM/Native Image and the Swift host.
Rendering is performed directly on the GPU via Metal. The runtime uses a custom renderer that bypasses AWT/Swing, ensuring smooth synchronization with macOS window resizing and animations.
The Gradle plugin automates the complex native build pipeline. It:
swiftc and clang..dylib) and launcher into the .app bundle.+---------------+ +--------------+ +----------------+ +-----------+
| Swift App | --(1)--> | Bridge Layer | --(2)--> | Kotlin Runtime | --(3)--> | Metal GPU |
| (Entry Point) | | (JNI / C-API)| | (JVM / Native) | | (Texture) |
+---------------+ +--------------+ +----------------+ +-----------+
Copyright 2026 letmutex
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Compose in native macOS window
[!IMPORTANT] This project is experimental. API and features are subject to change.
Gradle
// build.gradle.kts
plugins {
id("io.github.letmutex.compose.nativehost") version "<VERSION>"
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.compose.multiplatform)
}
kotlin {
jvm("desktop")
sourceSets {
val desktopMain by getting {
dependencies {
implementation("io.github.letmutex.compose-native-host:runtime:<VERSION>")
implementation(compose.desktop.currentOs)
}
}
}
}
composeNativeHost {
appName.set("Sample")
bundleIdentifier.set("example.sample")
}
// Compose Desktop configs
compose.desktop {
application {
mainClass = "example.MainKt"
}
}Kotlin
// src/desktopMain/kotlin/example/Main.kt
package example
import letmutex.compose.nativehost.ComposeNativeHost
fun main() = ComposeNativeHost {
App()
}Swift
// src/macosMain/swift/SwiftUiApp.swift
import SwiftUI
import ComposeNativeHost
final class SampleAppDelegate: ComposeAppDelegateBase {
fileprivate lazy var runtime = makeComposeRuntime(
configuration: ComposeRuntimeConfiguration(kotlinMainClass: "example.MainKt")
)
}
@main
struct SampleApp: App {
@NSApplicationDelegateAdaptor(SampleAppDelegate.self) private var appDelegate
var body: some Scene {
WindowGroup {
ComposeView(runtime: appDelegate.runtime)
}
}
}Use a GraalVM JDK that already has native-image available under bin/.
Environment
-PgraalvmHome=/path/to/graalvm, GRAALVM_HOME=/path/to/graalvm, org.gradle.java.home=/path/to/graalvm, or JAVA_HOME=/path/to/graalvm.macosNativeImageRun and the native image bundle tasks use that GraalVM home directly.macosRun still uses a regular JVM launch path.Gradle
composeNativeHost {
appName.set("Sample")
bundleIdentifier.set("example.sample")
nativeImage {
mainClasses("example.MainKt")
}
}Swift startups
final class SampleAppDelegate: ComposeAppDelegateBase {
override init() {
super.init(configuration: ComposeHostConfiguration(startups: [.jvm, .sharedLibrary()]))
}
}Use .jvm so the same host still runs without a bundled shared library, and add .sharedLibrary() so the staged native image bundle can bind the generated Graal runtime when present.
Use macosRun for the staged JVM bundle or macosNativeImageRun for the staged native image bundle.
./gradlew -p samples :appkit:macosRun
# run the native image bundle
./gradlew -p samples :appkit:macosNativeImageRun# build JVM / native image .app
./gradlew -p samples :appkit:macosCreateDistributable
./gradlew -p samples :appkit:macosNativeImageCreateDistributable
# package JVM / native image .dmg
./gradlew -p samples :appkit:macosPackageDmg
./gradlew -p samples :appkit:macosNativeImagePackageDmg
# package JVM / native image release .dmg
./gradlew -p samples :appkit:macosPackageReleaseDmg
./gradlew -p samples :appkit:macosNativeImagePackageReleaseDmgruntime: shared host API plus macOS runtime, native bridge, and native host sources.gradle-plugin: Gradle plugin for macOS launcher generation, bundle staging, and macOS app tasks.samples/compose: pure Compose Desktop sample app and shared Kotlin sample content.samples/appkit: AppKit-owned sample window using the shared hosted Kotlin sample.samples/swiftui: SwiftUI-owned sample window using the shared hosted Kotlin sample.samples/swiftui-min: minimal SwiftUI-owned sample extracted from the setup shown above.samples/mixed: AppKit-owned window with SwiftUI content composition around the hosted Compose surface.Debugging environment variables:
COMPOSE_NATIVE_HOST_FRAME_TIMINGS=1: Enable logging of frame timings (dispatch delay, input drain, scene render, etc.) to stdout for performance analysis.COMPOSE_NATIVE_HOST_VSYNC_DELAY=1: Enable analysis of VSync signal delivery delay from the display link to the Kotlin runtime.You own the App entry point in Swift. Your app's Swift sources (e.g., in src/macosMain/swift) define the AppKit/SwiftUI lifecycle. The plugin compiles these along with shared runtime helpers into the final native binary.
ComposeHostRuntime, ComposeView). These Swift sources are bundled inside the Gradle plugin and extracted during the build to be compiled into your app.A thin Objective C bridge handles low level JNI communication between the Kotlin JVM/Native Image and the Swift host.
Rendering is performed directly on the GPU via Metal. The runtime uses a custom renderer that bypasses AWT/Swing, ensuring smooth synchronization with macOS window resizing and animations.
The Gradle plugin automates the complex native build pipeline. It:
swiftc and clang..dylib) and launcher into the .app bundle.+---------------+ +--------------+ +----------------+ +-----------+
| Swift App | --(1)--> | Bridge Layer | --(2)--> | Kotlin Runtime | --(3)--> | Metal GPU |
| (Entry Point) | | (JNI / C-API)| | (JVM / Native) | | (Texture) |
+---------------+ +--------------+ +----------------+ +-----------+
Copyright 2026 letmutex
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.