What are the wasmJs() and wasmWasi() targets in a Kotlin Multiplatform project, and how do they differ?
answer
- Two Wasm targets: wasmJs + wasmWasi
- wasmJs = browser/Node + JS interop
- wasmWasi = standalone runtime, WASI, no JS
- Both need WasmGC + exception-handling proposals
- @ExperimentalWasmDsl on the Gradle DSL
basics
~10 sThey are two Kotlin Multiplatform targets that compile code to WebAssembly. wasmJs() runs in the browser and can talk to JavaScript; wasmWasi() runs in standalone runtimes outside the browser using the WASI system interface.
solid answer
~40 sBoth are Kotlin/Wasm targets declared in the `kotlin { }` block of a Gradle build, producing WebAssembly modules instead of JVM bytecode or native binaries. `wasmJs()` targets WebAssembly running in a JavaScript host (browsers, Node.js) and supports interop with JS via `external` declarations and the `js("...")` function. `wasmWasi()` targets standalone Wasm runtimes (Wasmtime, Node with WASI, etc.) using the WASI (WebAssembly System Interface) for OS-like calls (clock, stdio); it has no JS interop. Both require the experimental GC and exception-handling Wasm proposals, so they need a recent runtime. You configure browser/nodejs execution under `wasmJs { browser(); nodejs() }`. Picking the target depends on the host: JS environment vs server-side/CLI Wasm.
code
kotlin · 7 lineskotlin {
@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
wasmJs { browser(); nodejs() }
@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
wasmWasi { nodejs() }
}go deeper
Knows there are two Wasm targets and that one is for browsers and one is standalone.
Can declare both in Gradle, explain WASI vs JS host, and knows JS interop is wasmJs-only.
Discusses the WasmGC/exception-handling proposal requirements, runtime support, and library-availability differences when choosing a target.
Weighs wasmJs vs wasmWasi vs JS/Native for a product, factoring deployment runtimes, library ecosystem maturity, and experimental-status risk.
## What Kotlin/Wasm is **Kotlin/Wasm** is a compilation backend that turns Kotlin source into **WebAssembly (Wasm)** — a compact, portable binary instruction format that many runtimes can execute. It is distinct from Kotlin/JVM (bytecode), Kotlin/Native (machine code via LLVM), and Kotlin/JS (JavaScript). Kotlin/Wasm relies on two modern Wasm **proposals**: - **WasmGC** (garbage collection): lets the Wasm module use the host runtime's garbage collector for heap objects, so Kotlin doesn't ship its own GC. - **Exception handling**: native Wasm support for throwing/catching, used for Kotlin exceptions. Because of these, you need a recent runtime (modern Chrome/Firefox, current Node, Wasmtime). ## The two targets In a Multiplatform build's `kotlin { }` block you declare a target: ```kotlin kotlin { @OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class) wasmJs { browser() // run in a browser nodejs() // run under Node.js } @OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class) wasmWasi { nodejs() } } ``` - **`wasmJs()`** — Wasm meant to run **inside a JavaScript host**. The Wasm module is loaded by a small JS glue layer, and Kotlin can call into JS (DOM, `fetch`, libraries) through `external` declarations and the `js("...")` intrinsic. This is the target you use for **web frontends** (e.g. Compose Multiplatform for Web). - **`wasmWasi()`** — Wasm meant to run in a **standalone runtime** with no JavaScript. It uses **WASI** (WebAssembly System Interface), a standardized set of host functions for system-like capabilities: standard output, clock, environment, etc. Use it for **server-side / CLI** Wasm where there is no browser or JS engine. ## Key differences - **Host**: `wasmJs` = JS environment; `wasmWasi` = JS-free runtime. - **Interop**: `wasmJs` has JS interop; `wasmWasi` has none (you only get WASI host calls). - **Libraries**: many KMP libraries support `wasmJs` but not `wasmWasi` (or vice versa) — availability differs. Both share the same Kotlin/Wasm code generator and both currently require the experimental Wasm proposals and the `@ExperimentalWasmDsl` opt-in on the Gradle DSL.
- Why might a library support wasmJs but not wasmWasi?Because wasmWasi has no JS interop and only WASI host calls; libraries that depend on the DOM, fetch, or JS APIs simply can't run there, so authors often publish only the wasmJs artifact.
- Can the same Kotlin code be shared by both targets?Common code can be, but anything using JS interop must live in the wasmJs source set; wasmWasi code can only use WASI-backed capabilities, so platform-specific code differs.
wasmJs is Wasm with a JavaScript co-pilot; wasmWasi is Wasm flying solo with only a small set of system instruments (WASI).
saying these in an interview costs you the question
- Thinking wasmJs compiles to JavaScript (it compiles to Wasm; JS is only the glue/host)
- Claiming wasmWasi can call DOM or fetch APIs
- Saying Kotlin/Wasm ships its own GC inside the module
- Confusing wasmJs with the separate Kotlin/JS (IR) target