Use llmkit in WebAssembly

This guide runs the Rust SDK in the browser. You build it for wasm32-unknown-unknown, then call providers from a page, a Web Worker or Node. WebAssembly support arrives in the next Rust release.

The TypeScript SDK already runs in browsers without WebAssembly. Use it if your app is written in TypeScript.

Before you start

You need the WebAssembly target and a bundler such as trunk or wasm-pack:

rustup target add wasm32-unknown-unknown

Add the dependencies

Do not add tokio. It has no timer in the browser, and llmkit does not need it there.

[dependencies]
llmkit-rust = "<next release>"
wasm-bindgen-futures = "0.4"

Make a call

The calls are the same as on native targets. Start them with wasm_bindgen_futures::spawn_local, because the browser has no multi-threaded runtime.

use llmkit::builders::google;

pub async fn generate_png(api_key: String) -> Result<Option<Vec<u8>>, llmkit::Error> {
    let img = google(api_key)
        .image()
        .model("gemini-3.1-flash-image-preview")
        .aspect_ratio("16:9")
        .generate("A lighthouse on a granite island at dusk")
        .await?;
    Ok(img.images.first().map(|image| image.bytes.clone()))
}

pub fn start(api_key: String) {
    wasm_bindgen_futures::spawn_local(async move {
        match generate_png(api_key).await {
            Ok(Some(png)) => { /* show the image */ }
            Ok(None) => { /* the model returned no image */ }
            Err(err) => { /* show err.to_string() */ }
        }
    });
}

Check that the browser can reach the provider

A browser can read a provider’s response only when the provider sends CORS headers. Ask llmkit before you call:

use llmkit::providers::info;
use llmkit::ProviderName;

let direct = info(ProviderName::Google).browser_callable;

Today only Google returns true. For any other provider, send requests through your own proxy and point the client at it:

use llmkit::builders::anthropic;

let c = anthropic(user_token).base_url("https://your-app.example.com/proxy/anthropic");

The proxy must forward only the headers the provider needs. It must not forward the browser’s cookies.

Keep API keys safe

Code in a browser is readable by its user. A key in the page belongs to that user. Either let users enter their own key, or keep your key on the proxy.

What differs from native

  • Streaming. stream reads the whole response before it calls your callback, so every delta arrives at once when the response completes.
  • Timeouts. The client’s timeout has no effect. The request waits as long as the browser’s fetch allows.
  • Futures are not Send. Awaiting a VideoHandle, BatchHandle or TranscriptionHandle returns a future without Send. Browser futures are never Send.
  • No file system or environment. Uploading from a path, reading AWS credentials from the environment, and the built-in HTTP telemetry exporter return an error. Upload from bytes, pass credentials explicitly, and write your own telemetry export callback.

Video generation polls with setTimeout, so VideoHandle waits work in a page and in a worker.