
Cross-platform WebView component exposing WebViewState and navigator APIs; backed by native OS webviews via Rust+UniFFI (Wry), with JS-native bridge, cookie API, request interceptor.
ComposeNativeWebView is a Compose Multiplatform WebView whose API design and mobile implementations (Android & iOS) are intentionally derived almost verbatim from KevinnZou/compose-webview-multiplatform.
This project exists first and foremost to bring that same API to Desktop, backed by native OS webviews instead of a bundled Chromium runtime.
io.github.kdroidfilter.webview.*
๐ข Reused on purpose
WebViewState, WebViewNavigator, settings, callbacks, mental model)android.webkit.WebView)WKWebView)๐ If you already know compose-webview-multiplatform, you already know how to use this.
๐ What ComposeNativeWebView adds
โ
Android: android.webkit.WebView
โ
iOS: WKWebView
โ
Desktop: Wry (Rust) via UniFFI
Desktop engines:
@Composable
fun App() {
val state = rememberWebViewState("https://example.com")
WebView(state, Modifier.fillMaxSize())
}Thatโs it.
dependencies {
implementation("io.github.kdroidfilter:composewebview:<version>")
}Same artifact for Android, iOS, Desktop.
Wry uses native access via JNA.
compose.desktop {
application {
jvmArgs += "--enable-native-access=ALL-UNNAMED"
}
}Run the feature showcase first:
./gradlew :demo:run
./gradlew :demo-android:installDebug
iosApp/iosApp.xcodeproj in Xcode and RunResponsive UI:
loadUrl(url, headers)loadHtml(html)loadHtmlFile(fileName, readType)navigateBack(), navigateForward()
reload(), stopLoading()
canGoBack, canGoForward
isLoadingloadingStatelastLoadedUrlpageTitleUnified cookie API:
state.cookieManager.setCookie(...)
state.cookieManager.getCookies(url)
state.cookieManager.removeCookies(url)
state.cookieManager.removeAllCookies()navigator.evaluateJavaScript("document.title = 'Hello'")window.kmpJsBridge.callNative("echo", {...}, callback)Intercept navigator-initiated navigations only:
override fun onInterceptUrlRequest(
request: WebRequest,
navigator: WebViewNavigator
): WebRequestInterceptResultUseful for:
val state = rememberWebViewState(
url = "https://example.com"
) {
customUserAgentString = "MyApp/1.0"
}Supports:
val navigator = rememberWebViewNavigator()
WebView(state, navigator)Commands:
loadUrlloadHtmlloadHtmlFileevaluateJavaScriptstate.webSettings.customUserAgentString = "MyApp/1.2.3"Desktop note:
๐ Set it early.
state.webSettings.logSeverity = KLogSeverity.DebugWebView(
state,
navigator,
onCreated = { native ->
println(native.getCurrentUrl())
}
)Useful for debugging or platform-specific hooks.
wrywebview/ โ Rust core + UniFFI bindingswrywebview-compose/ โ Compose APIdemo-shared/ โ shared demo UIdemo/, demo-android/, iosApp/ โ platform launchersComposeNativeWebView is a Compose Multiplatform WebView whose API design and mobile implementations (Android & iOS) are intentionally derived almost verbatim from KevinnZou/compose-webview-multiplatform.
This project exists first and foremost to bring that same API to Desktop, backed by native OS webviews instead of a bundled Chromium runtime.
io.github.kdroidfilter.webview.*
๐ข Reused on purpose
WebViewState, WebViewNavigator, settings, callbacks, mental model)android.webkit.WebView)WKWebView)๐ If you already know compose-webview-multiplatform, you already know how to use this.
๐ What ComposeNativeWebView adds
โ
Android: android.webkit.WebView
โ
iOS: WKWebView
โ
Desktop: Wry (Rust) via UniFFI
Desktop engines:
@Composable
fun App() {
val state = rememberWebViewState("https://example.com")
WebView(state, Modifier.fillMaxSize())
}Thatโs it.
dependencies {
implementation("io.github.kdroidfilter:composewebview:<version>")
}Same artifact for Android, iOS, Desktop.
Wry uses native access via JNA.
compose.desktop {
application {
jvmArgs += "--enable-native-access=ALL-UNNAMED"
}
}Run the feature showcase first:
./gradlew :demo:run
./gradlew :demo-android:installDebug
iosApp/iosApp.xcodeproj in Xcode and RunResponsive UI:
loadUrl(url, headers)loadHtml(html)loadHtmlFile(fileName, readType)navigateBack(), navigateForward()
reload(), stopLoading()
canGoBack, canGoForward
isLoadingloadingStatelastLoadedUrlpageTitleUnified cookie API:
state.cookieManager.setCookie(...)
state.cookieManager.getCookies(url)
state.cookieManager.removeCookies(url)
state.cookieManager.removeAllCookies()navigator.evaluateJavaScript("document.title = 'Hello'")window.kmpJsBridge.callNative("echo", {...}, callback)Intercept navigator-initiated navigations only:
override fun onInterceptUrlRequest(
request: WebRequest,
navigator: WebViewNavigator
): WebRequestInterceptResultUseful for:
val state = rememberWebViewState(
url = "https://example.com"
) {
customUserAgentString = "MyApp/1.0"
}Supports:
val navigator = rememberWebViewNavigator()
WebView(state, navigator)Commands:
loadUrlloadHtmlloadHtmlFileevaluateJavaScriptstate.webSettings.customUserAgentString = "MyApp/1.2.3"Desktop note:
๐ Set it early.
state.webSettings.logSeverity = KLogSeverity.DebugWebView(
state,
navigator,
onCreated = { native ->
println(native.getCurrentUrl())
}
)Useful for debugging or platform-specific hooks.
wrywebview/ โ Rust core + UniFFI bindingswrywebview-compose/ โ Compose APIdemo-shared/ โ shared demo UIdemo/, demo-android/, iosApp/ โ platform launchers