Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Creating Runnable Components (Rust)

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 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’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.

Creating a command component

A command is a component with a specific export that allows it to be executed directly by wasmtime (or other wasi:cli hosts). In Rust terms, it’s the equivalent of an application (bin) package with a main function, instead of a library crate (lib) package.

Command components work by including a WebAssembly core export _start that indicates the component has a natural main-like starting point.

1. Create a new Rust binary project

To create a command with cargo, run:

cargo new runnable-example

Unlike library components, this does not have the --lib flag (--bin is the default for cargo new).

The created Rust source file is called main.rs instead of lib.rs, and contains a main function.

You can write Rust in this project, just as you normally would, including importing your own or third-party crates.

All the crates that make up your project are linked together at build time, and compiled to a single Wasm component. In this case, all the linking is happening at the Rust level: no WITs or component composition is involved. Only if you import Wasm interfaces do WIT and composition come into play.

2. Write the relevant Rust

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

pub fn main() {
    eprintln!("Hello World!");
}

3. Build the component

To build the component, use cargo:

cargo build --target=wasm32-wasip2

The component can also be built in release mode:

cargo build --target=wasm32-wasip2 --release

4. Run the component with wasmtime

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 exported interface) and executed standalone like a command component.

While reactor (library-like) components export interfaces that are meant to be used directly, they can also export the wasi:cli/run interface from WASI CLI, and signal to consumers that the library can also be run similarly to a binary that would run via a command line interface.

Unlike command components, library components have no _start, but by exporting the wasi:cli/run interface, tooling that recognizes these exports can easily execute a given WebAssembly binary (e.g. wasmtime run).

1. Create a new Rust library project

To build a simple component that exports wasi:cli/run, first create a new Rust project:

cargo new --lib runnable-example

After creating the new project, ensure it is a cdylib crate by updating runnable-example/Cargo.toml and adding the following lines:

[lib]
crate-type = ['cdylib']

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

cargo add wit-bindgen

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:

package example:runnable;

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

world greeter {
    export greet;
    export wasi:cli/run@0.2.7;
}

Building a library component this way does two things:

  • Enables other components/hosts to use the greet interface
  • Exposes an interface (wasi:cli/run) that indicates this component can be run like a CLI
    • Note that no guarantees are made about what the component does when it runs

While we created greet, wasi:cli is a well-known interface. We can resolve this interface to local WIT by 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 or platforms can use the greet interface export. More importantly, the component can also be recognized as a generically runnable component thanks to wasi:cli/run, so it can work with any tooling (ex. wasmtime run) that supports/recognizes the wasi:cli interface.

3. Write the code for the component

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

#![allow(unused)]
fn main() {
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
impl bindings::exports::wasi::cli::run::Guest for Component {
    fn run() -> Result<(), ()> {
        eprintln!("Hello World!");
        Ok(())
    }
}
}

4. Build the component

To build the component, use cargo:

cargo build --target=wasm32-wasip2

The component can also be built in release mode:

cargo build --target=wasm32-wasip2 --release

5. Run the component with wasmtime

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

$ 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.

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.