Skip to content

aryapreetam/cmp-webview

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

32 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

CMP WebView - Compose Multiplatform WebView Library

Compose Multiplatform Version Kotlin Version

Release workflow status Maven Central Version MIT License

Android iOS Desktop Web

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)

βœ… Platform capability matrix

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) ⚠️ best-effort (no cross-origin injection)
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 htmlContent or 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 evaluateJavaScript currently does not surface return values (it returns WebViewJsResult.Unsupported(...)).

Wasm Demo: Try Live Demo

API Docs: Library API Docs


πŸ“¦ Installation

Add the dependency to your Compose Multiplatform project:

Option A β€” Version catalog (recommended)

  1. In gradle/libs.versions.toml:
[versions]
cmpWebview = "0.0.1"

[libraries]
cmp-webview = { module = "io.github.aryapreetam:cmp-webview", version.ref = "cmpWebview" }
  1. 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")
      }
    }
  }
}

πŸš€ Quick Start

Load a Remote URL

@Composable
fun MyScreen() {
  WebView(
    url = "https://example.com",
    modifier = Modifier.fillMaxSize(),
    onLoadStarted = { println("Loading started") },
    onLoadFinished = { println("Loading finished") },
    onLoadError = { error -> println("Error: $error") }
  )
}

Display HTML Content

@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 Bridge Communication

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
    }
  )
}

Compose β†’ JavaScript (optional)

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 htmlContent or same-origin content.

πŸ“š Documentation

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.

πŸ”§ Platform-Specific Notes

Android

  • Uses native Android WebView
  • JavaScript is enabled by default
  • Supports all standard web features

iOS

  • Uses WKWebView
  • Supports modern web standards
  • Bridge communication via WKScriptMessageHandler

Desktop (JVM)

  • 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)

Web (WASM)

  • 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 Sample App Executables

Download and try the sample app on your platform without building from source:

Platform Download Link
🍏 macOS (Intel) Download
🍎 macOS (Apple Silicon) Download
πŸͺŸ Windows Download
🐧 Linux Download
πŸ€– Android Download
🌐 Web (Wasm) Download
ο£Ώ iOS Simulator Download

πŸš€ How to Run the Sample App

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.


License

MIT License Β© 2025 aryapreetam and contributors. See LICENSE for details.


πŸ™ Acknowledgments


🀝 Contributing

See CONTRIBUTING.md for detailed contribution guidelines.

About

Simple WebView for Compose Multiplatform(Android, iOS, Web(wasm), Desktop)

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages