Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Creating Runnable Components (Rust)

<div class="version-notice">

This page has content for both **WASI 0.2** and **WASI 0.3**. Use the tabs below to switch between versions where they differ.

> **WASI 0.3 toolchain note.** Rust's [`wasm32-wasip3` target](https://doc.rust-lang.org/nightly/rustc/platform-support/wasm32-wasip3.html) is currently Tier 3 with no prebuilt artifacts; building for it requires constructing the standard library from source. The 0.3 example on this page therefore uses the library/reactor pattern targeting `wasm32-wasip2`, where [`wit-bindgen`](https://crates.io/crates/wit-bindgen)'s `async` feature handles the 0.3 binding generation. There is no Rust-idiomatic 0.3 path for the `fn main()` command-component pattern yet.

</div>

## Creating a command component

A _command_ is a component with a specific export that allows it to be executed directly by `wasmtime`
Expand Down Expand Up @@ -45,7 +53,7 @@ cargo build --target=wasm32-wasip2

The component can also be built in release mode:

```console
```sh
cargo build --target=wasm32-wasip2 --release
```

Expand All @@ -57,6 +65,8 @@ To run your command component:
wasmtime run ./target/wasm32-wasip2/debug/runnable-example.wasm
```

> For a runnable component that exports the 0.3 `wasi:cli/run` interface, see the library/reactor pattern below.

## Enabling a library component to be run via the `wasi:cli/run` interface

Sometimes, it is useful to create a component that can *both* be used as a library (via
Expand Down Expand Up @@ -91,15 +101,28 @@ crate-type = ['cdylib']

We'll also be generating Rust bindings from WIT interfaces, so add `wit-bindgen`:

{{#tabs global="wasi-version" }}
{{#tab name="WASI 0.2" }}
```sh
cargo add wit-bindgen
```
{{#endtab }}
{{#tab name="WASI 0.3" }}
```sh
cargo add wit-bindgen --features async
```

The `async` feature enables `wit-bindgen`'s async code generation, which is needed for the 0.3 `async func run()` signature.
{{#endtab }}
{{#endtabs }}

### 2. Add the appropriate WIT interfaces

Then, add the appropriate WIT interfaces. For example a simple component that prints "Hello World", add the following
contents to `runnable-example/wit/component.wit`:

{{#tabs global="wasi-version" }}
{{#tab name="WASI 0.2" }}
```wit
package example:runnable;

Expand All @@ -112,6 +135,26 @@ world greeter {
export wasi:cli/run@0.2.7;
}
```
{{#endtab }}
{{#tab name="WASI 0.3" }}
```wit
package example:runnable;

interface greet {
greet: func(name: string) -> string;
}

world greeter {
export greet;
export wasi:cli/run@0.3.0-rc-2026-03-15;
}
```

In WASI 0.3, `wasi:cli/run` is declared as `run: async func() -> result`, so the generated `Guest` trait expects an `async fn`.

> The pin here is `0.3.0-rc-2026-03-15` rather than the published `0.3.0` because Wasmtime, `wit-bindgen`, and other toolchain components currently ship this snapshot of the WIT. They are expected to refresh to `0.3.0` in upcoming releases.
{{#endtab }}
{{#endtabs }}

Building a library component this way does two things:

Expand All @@ -126,6 +169,8 @@ using `wkg`:
wkg wit fetch
```

> For WASI 0.3, use `wkg` 0.15 or later. Earlier versions fail to decode the `wasi:cli@0.3.0-rc-2026-03-15` package.

At this point, you should have a `wit` folder with a `deps` subfolder and your original `component.wit`.

The component we will create to satisfy the WIT above can be used as a library, as other components
Expand All @@ -139,6 +184,8 @@ with any tooling (ex. `wasmtime run`) that supports/recognizes the `wasi:cli` in

The following code can be inserted into `runnable-example/src/lib.rs`:

{{#tabs global="wasi-version" }}
{{#tab name="WASI 0.2" }}
```rust
mod bindings {
use super::Component;
Expand Down Expand Up @@ -166,9 +213,43 @@ impl bindings::exports::wasi::cli::run::Guest for Component {
}
}
```
{{#endtab }}
{{#tab name="WASI 0.3" }}
```rust
mod bindings {
use super::Component;

wit_bindgen::generate!();

export!(Component);
}

/// Component off of which implementation will hang (this can be named anything)
struct Component;

/// Implementation for the `greet` interface export
impl bindings::exports::example::runnable::greet::Guest for Component {
fn greet(name: String) -> String {
format!("Hello {name}!")
}
}

/// Implementation for `wasi:cli/run` interface export.
/// In WASI 0.3 the `run` function is asynchronous.
impl bindings::exports::wasi::cli::run::Guest for Component {
async fn run() -> Result<(), ()> {
eprintln!("Hello World!");
Ok(())
}
}
```
{{#endtab }}
{{#endtabs }}

### 4. Build the component

{{#tabs global="wasi-version" }}
{{#tab name="WASI 0.2" }}
To build the component, use `cargo`:

```sh
Expand All @@ -177,16 +258,49 @@ cargo build --target=wasm32-wasip2

The component can also be built in release mode:

```console
```sh
cargo build --target=wasm32-wasip2 --release
```
{{#endtab }}
{{#tab name="WASI 0.3" }}
Build the component against `wasm32-wasip2` using a nightly Rust toolchain:

```sh
cargo +nightly build --target=wasm32-wasip2
```

Release mode:

```sh
cargo +nightly build --target=wasm32-wasip2 --release
```

> A nightly toolchain is required because the `wasm-component-ld` bundled with current stable Rust cannot decode the component-type custom section that `wit-bindgen` 0.58 emits for 0.3 worlds. Nightly ships a newer linker that handles it.
{{#endtab }}
{{#endtabs }}

### 5. Run the component with `wasmtime`

{{#tabs global="wasi-version" }}
{{#tab name="WASI 0.2" }}
You can run the component with `wasmtime`, and unlike a generic reactor component, you do not need to specify
the interface and function to run (`wasi:cli/run` is detected and used automatically):

```console
$ wasmtime run target/wasm32-wasip2/runnable-example.wasm
$ wasmtime run target/wasm32-wasip2/debug/runnable_example.wasm
Hello World!
```

> Cargo converts hyphens to underscores in library crate names, so the artifact is `runnable_example.wasm` even though the package was created as `runnable-example`.
{{#endtab }}
{{#tab name="WASI 0.3" }}
Enable the WASI 0.3 ABI when running a 0.3 component, with Wasmtime 43 or later:

```console
$ wasmtime run -Sp3 -W component-model-async=y target/wasm32-wasip2/debug/runnable_example.wasm
Hello World!
```
{{#endtab }}
{{#endtabs }}

> **Version pinning.** WASI 0.3 tools (`wit-bindgen`, Wasmtime, jco, and so on) must all target the same WIT version. As of WASI 0.3.0's release on 2026-06-11, `wit-bindgen` 0.58 and Wasmtime 45 still ship the `0.3.0-rc-2026-03-15` snapshot of the WIT; pinning to the published `0.3.0` produces `no exported instance named wasi:cli/run@0.2.6` (or similar) errors at run time until those tools refresh. Use the RC pin until then.
Loading