Skip to content

Render A2UI in Compose

One Maven artifact, at.exponential:eui-compose (minSdk 26). Its POM brings the primitives, Compose, coroutines and JNA, and the AAR carries the Rust core for arm64-v8a, armeabi-v7a and x86_64. R8 needs no extra rules.

dependencies {
implementation("at.exponential:eui-compose:0.12.0")
}

It resolves from mavenCentral(). The whole dependency block of the guide’s app:

guides/compose/app/build.gradle.kts
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("org.jetbrains.kotlin.plugin.compose")
}
android {
namespace = "at.exponential.guide"
compileSdk = 36
defaultConfig {
applicationId = "at.exponential.guide"
minSdk = 26
targetSdk = 36
versionCode = 1
versionName = "0.1.0"
}
buildFeatures {
compose = true
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}
kotlin {
compilerOptions {
jvmTarget = JvmTarget.JVM_17
}
}
dependencies {
// The SDK: the painter + host runtime (its POM brings the primitives,
// Compose, coroutines and JNA with the native core).
implementation("at.exponential:eui-compose:0.12.0")
implementation("androidx.activity:activity-compose:1.12.4")
implementation(platform("androidx.compose:compose-bom:2024.12.01"))
implementation("androidx.compose.foundation:foundation")
implementation("androidx.compose.material3:material3")
}

One ExponentialHost per activity, connected in onCreate and disposed in onDestroy; HostSurface paints a surface by id. The emulator reaches the host machine’s server at 10.0.2.2.

guides/compose/app/src/main/kotlin/at/exponential/guide/MainActivity.kt
package at.exponential.guide
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import at.exponential.ui.ExponentialUi
import at.exponential.ui.host.ExponentialHost
import at.exponential.ui.host.HostOptions
import at.exponential.ui.host.HostSurface
import at.exponential.ui.host.JsonlStreamTransport
import at.exponential.ui.theme.Mode
import at.exponential.ui.theme.ThemeHandle
class MainActivity : ComponentActivity() {
// One host per app: A2UI messages in over a transport, actions back out.
private val host = ExponentialHost(
HostOptions(
transport = JsonlStreamTransport(url = "http://10.0.2.2:4300/a2ui.jsonl", postUrl = "http://10.0.2.2:4300/action"),
theme = ThemeHandle.builtin("exponential"),
mode = Mode.Dark,
),
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ExponentialUi.appContext = applicationContext // links open through it
host.connect()
setContent {
// The surface the agent creates, as tall as its content: you own the scroller.
Column(Modifier.safeDrawingPadding().verticalScroll(rememberScrollState()).padding(16.dp)) {
HostSurface(host, "main", fallback = { Text("Waiting for the agent…") })
}
}
}
override fun onDestroy() {
host.dispose()
super.onDestroy()
}
}

The lower-level SurfaceModel + ExponentialSurface take a HostPlugin (every member has a default; ClosureHost builds one from lambdas): icons painted under LocalContentColor, onAction, onInput (Change debounced with a revision, Commit on blur or IME Done), openUrl, fontFamily, markdown. Register theme fonts with ExponentialUi.registerFont(family, FontFamily). Details in the Compose library’s README.

bun packages/exponential-ui/guides/check.ts compose builds guides/compose in an empty directory against locally published Maven artifacts.