Home
| Colors: |
February 2026

Rust WebAssembly/wasm Hello World Example

These are my notes on how to get a wasm tool up and running. It's largely the same as the MDN tutorial but with way less cruft.

  1. Install wasm-pack.

    cargo install wasm-pack
  2. Create a new library rust project and cd into the directory.

    cargo new --lib hello-world
    cd hello-world
  3. Add the wasm-bindgen crate.

    cargo add wasm-bindgen
  4. Add this to the Cargo.toml file.

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

    The file should look something like this when you're done:

    [package]
    name = "hello-world"
    version = "0.1.0"
    edition = "2024"
    
    [lib]
    crate-type = ["cdylib"]
    
    [dependencies]
    wasm-bindgen = "0.2.108"
  5. Replace the contents of:

    src/lib.rs

    with:

    use wasm_bindgen::prelude::*;
    
    #[wasm_bindgen]
    extern "C" {
        pub fn alert(s: &str);
    }
    
    #[wasm_bindgen]
    pub fn greet(name: &str) {
        alert(&format!("Hello, {}!", name));
    }
  6. Make sure you're still in the hello-world directory with the Cargo.toml file in it and run this to build the package:

    wasm-pack build --target web

    That will create a new pkg folder with the wasm files.

  7. Create an index.html file in the same folder as the Cargo.toml file with this content.

    <!doctype html>
    <html lang="en-US">
      <head>
        <meta charset="utf-8" />
        <title>hello-wasm example</title>
      </head>
      <body>
        <script type="module">
          import init, { greet } from "./pkg/hello_world.js";
          init().then(() => {
            greet("WebAssembly");
          });
        </script>
      </body>
    </html>

With all that done, you can serve the directory with the index.html file in it and everything should work as expected.

-a

end of line

Endnotes

Looking at this example that shows how to make a package without a bundler it sounds like you always need to do an async start-up of the module right now. That may change in the future.

The example isn't that useful. I haven't seen alert() used in forever.

This is code for another example that gives you a button to press. I need to polish it and move it to the main section. Just keeping it here until I get to that.

(this is for a crate named wasm_example instead of hello-world

src/lib.rs
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn increment(a: u32) -> u32 {
  a + 7
}
index.html
<!doctype html>
<html lang="en">
<body>
    <button class="increment">0</button>
    <script type="module">
        import init, { increment } from "./pkg/wasm_example.js"
        init().then(() => {
          document.querySelector(".increment")
            .addEventListener(
                "click", 
                (event) => {
                    const el = event.target;
                    el.innerHTML = increment(el.innerHTML);
                }
            );
        });
    </script>
</body>
</html>

Of interest is the fact that I didn't have to do parseInt(el.innerHTML) before passing to the wasm module. It seems to have just converted the type in flight.

References