A WebView library for Compose Multiplatform that enables you to display web content and HTML across Android, iOS, Desktop (JVM), and Web (WASM) platforms with a unified API.
Key Features:
- π Load remote URLs (custom headers: Android only)
- π Display HTML content directly
- π JavaScript β Compose messaging (via
onScriptResult) - π Compose β JavaScript calls (via
WebViewController.evaluateJavaScript) - π Built-in security protections against dangerous URL schemes
- βΏ Accessibility support with screen reader compatibility
- π§ͺ Testing support with test tags and semantics
- π― Single API across all platforms (some capabilities are platform-dependent)
| Capability | Android | iOS | Desktop (JVM) | Web (WASM) |
|---|---|---|---|---|
Load remote url |
β | β | β | β (iframe; subject to CSP/X-Frame-Options) |
Load htmlContent |
β | β | β | β |
Custom request headers (headers) |
β | β | β | β |
JS β Compose (onScriptResult) with htmlContent |
β | β | β | β |
JS β Compose (onScriptResult) with remote url |
β (bridge injected) | β (bridge injected) | β (bridge injected) | |
Compose β JS (WebViewController.evaluateJavaScript) |
β | β | β (executes; no return values yet) | β
(same-origin / htmlContent only) |
evaluateJavaScript return values |
β | β | β (returns Unsupported) |
β
(same-origin / htmlContent only) |
Notes:
- On Web/WASM, browsers prevent injecting scripts into cross-origin iframes. For reliable messaging on WASM, prefer
htmlContentor same-origin pages you control. - On native targets, bridge injection can still be affected by page security policies (e.g., strict CSP). Treat messaging as best-effort for arbitrary third-party pages.
- On Desktop/JVM, JavaScript execution is best-effort, but
evaluateJavaScriptcurrently does not surface return values (it returnsWebViewJsResult.Unsupported(...)).
Add the dependency to your Compose Multiplatform project:
Option A β Version catalog (recommended)
- In
gradle/libs.versions.toml:
[versions]
cmpWebview = "0.0.1"
[libraries]
cmp-webview = { module = "io.github.aryapreetam:cmp-webview", version.ref = "cmpWebview" }- In your module's
build.gradle.kts:
kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation(libs.cmp.webview)
}
}
}
}Option B β Direct dependency
kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation("io.github.aryapreetam:cmp-webview:0.0.1")
}
}
}
}@Composable
fun MyScreen() {
WebView(
url = "https://example.com",
modifier = Modifier.fillMaxSize(),
onLoadStarted = { println("Loading started") },
onLoadFinished = { println("Loading finished") },
onLoadError = { error -> println("Error: $error") }
)
}@Composable
fun HtmlScreen() {
val html = """
<html>
<body>
<h1>Hello from HTML!</h1>
<p>This is rendered locally.</p>
</body>
</html>
""".trimIndent()
WebView(
htmlContent = html,
modifier = Modifier.fillMaxSize()
)
}JavaScript code in your web content can communicate with your Compose code:
In your HTML/JavaScript:
// Listen for bridge ready event
window.addEventListener('ComposeWebViewBridgeReady', function () {
// Send message to Compose
ComposeWebViewBridge.postMessage('Hello from JavaScript!');
});
// Example: Send button click data
document.getElementById('myButton').addEventListener('click', function () {
ComposeWebViewBridge.postMessage(JSON.stringify({
action: 'buttonClick',
data: 'some value'
}));
});In your Compose code:
@Composable
fun BridgeExample() {
WebView(
url = "https://example.com",
onScriptResult = { message ->
println("Received from JavaScript: $message")
// Parse JSON if needed
val data = Json.decodeFromString<MyData>(message)
// Handle the message
}
)
}If you need to call JavaScript from Compose, pass a WebViewController.
@Composable
fun ComposeToJsExample() {
val controller = rememberWebViewController()
val scope = rememberCoroutineScope()
Column {
Button(onClick = {
scope.launch {
controller.evaluateJavaScript(
"document.body.style.background = 'tomato';" +
"window.ComposeWebViewBridge?.postMessage('ack');"
)
}
}) {
Text("Run JS")
}
WebView(
htmlContent = "<html><body>...</body></html>",
controller = controller,
onScriptResult = { msg -> println("JSβCompose: $msg") },
)
}
}Notes:
- On Desktop/JVM, the script runs best-effort, but return values are not available yet.
- On Web/WASM, calling JS only works reliably for
htmlContentor same-origin content.
Detailed documentation is available in the docs directory:
- Usage & Security Guide: Custom headers, base URL, error handling, URL validation, bridge message security, and Content Security Policy (CSP).
- Testing Guide: Interop testing limitations, UI testing with test tags, semantics for loading states, and testing bridge communication.
- Accessibility Guide: Automatic semantics, screen reader announcements, and best practices.
- Troubleshooting Guide: Solutions for bridge messaging, load errors, blank displays, IDE unresponsiveness, memory leaks, and Desktop session limitations.
- Performance Considerations: Memory management, caching behavior, and optimization best practices.
- Uses native Android WebView
- JavaScript is enabled by default
- Supports all standard web features
- Uses WKWebView
- Supports modern web standards
- Bridge communication via WKScriptMessageHandler
- Uses Wry (native OS web engine via ComposeNativeWebview)
- No Chromium bundling β uses the platform's native web engine
- Bridge communication via JS polyfill (
window.javaBridgeβwindow.kmpJsBridge)
- Uses HTML iframe
- Cross-origin limitations: Bridge only works for same-origin content
- For cross-origin URLs, the iframe cannot inject bridge scripts due to browser security
Download and try the sample app on your platform without building from source:
| Platform | Download Link |
|---|---|
| π macOS (Intel) | |
| π macOS (Apple Silicon) | |
| πͺ Windows | |
| π§ Linux | |
| π€ Android | |
| π Web (Wasm) | |
| ο£Ώ iOS Simulator |
For instructions on how to install and run the pre-built executables or build and run the sample application from source code, please refer to the How to Run the Sample App guide.
MIT License Β© 2025 aryapreetam and contributors. See LICENSE for details.
- ComposeNativeWebview - Native Webview for Compose Multiplatform (Desktop/JVM).
- compose-webview-multiplatform - WebView for JetBrains Compose Multiplatform
See CONTRIBUTING.md for detailed contribution guidelines.