Trunk (WASM bundler for Rust) - Tutorial

Page content

Try Trunk before Yew tutorial

Just follow the simplest official tutorial

To understand how Trunk behaves.

I’ve simply followed the official getting started.

Set up:

cargo install trunk
cargo new trunk-tutorial
cd trunk-tutorial

Keep the default Hello world code in src/main.rs.

Create index.html:

<html>
  <head>
    <link data-trunk rel="scss" href="index.scss"/>
  </head>
</html>

Create empty index.scss, and bild:

trunk build

Feb 03 18:59:17.553  INFO 📦 starting build
Feb 03 18:59:17.555  INFO spawning asset pipelines
Feb 03 18:59:17.783  INFO building trunk-tutorial
Feb 03 18:59:17.785  INFO compiling sass/scss path="index.scss"
Feb 03 18:59:17.805  INFO finished compiling sass/scss path="index.scss"
   Compiling trunk-tutorial v0.1.0 (/home/atlex00/trunk-tutorial)
    Finished dev [unoptimized + debuginfo] target(s) in 0.71s
Feb 03 18:59:18.541  INFO fetching cargo artifacts
Feb 03 18:59:18.599  INFO processing WASM
Feb 03 18:59:18.609  INFO calling wasm-bindgen
Feb 03 18:59:18.627  INFO copying generated wasm-bindgen artifacts
Feb 03 18:59:18.627  INFO applying new distribution
Feb 03 18:59:18.628  INFO ✅ success

The new directory dist was created:

➜ tree -I target
.
├── Cargo.lock
├── Cargo.toml
├── dist
│   ├── index-1f5c5c6a74caebef_bg.wasm
│   ├── index-1f5c5c6a74caebef.js
│   ├── index-597d90855083f957.css
│   ├── index.css
│   └── index.html
├── index.html
├── index.scss
└── src
    └── main.rs

Check the dist page:

cd dist
python3 -m http.server

If you open http://localhost:8000/, there is nothing on the page, because src/main.rs is just a Rust’s hello world program.

The only take-away is “trunk generate/bundle all required HTML+CSS+JS+WASM(Rust) and outputs to dist”.

Controll HTML using WASM (Rust)

Let’s apply WASM knowledge (my note).

Cargo.toml (use wasm-bindgen):

[package]
name = "trunk-tutorial"
version = "0.1.0"
edition = "2021"

[dependencies]
wasm-bindgen = "^0.2"

src/main.rs:

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern {
    pub fn alert(s: String);
}

#[wasm_bindgen]
pub fn greet(number_string: String) {
    alert(format!("From Rust: {}!", number_string));
}

fn main() {
    let mut a: usize = 1234;
    let b: usize = 56780000;
    greet( (a+b).to_string() )
}

Then, run trunk serve. If you open http://localhost:8000/dist, you will get the alert message “From Rust: 56781234!”.

Take aways

  • main function will be the entry point of your WASM
  • #[wasn_bindgen] macro and extern keyword bind the JS’ alert function.

Yew tutorial with Trunk

We now understand Trunk is not a magic. Let’s try Yew.

Follow this page:

The version of Yew is 0.19.

Cargo.toml:

[package]
name = "yew-app"
version = "0.1.0"
edition = "2018"

[dependencies]
yew = "^0.19"

src/main.rs:

use yew::prelude::*;

enum Msg {
    AddOne,
}

struct Model {
    value: i64,
}

impl Component for Model {
    type Message = Msg;
    type Properties = ();

    fn create(_ctx: &Context<Self>) -> Self {
        Self {
            value: 0,
        }
    }

    fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
        match msg {
            Msg::AddOne => {
                self.value += 1;
                true
            }
        }
    }

    fn view(&self, ctx: &Context<Self>) -> Html {
        let link = ctx.link();
        html! {
            <div>
                <button onclick={link.callback(|_| Msg::AddOne)}>{ "+1" }</button>
                <p>{ self.value }</p>
            </div>
        }
    }
}

fn main() {
    yew::start_app::<Model>();
}

This sample will generate a simple click counter. This impl Component inject body into index.html. (For furthur understanding, you need to know how yew works, so I don’t cover in this post.)

Create index.html (this is just a container):

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Yew App</title>
  </head>
</html>

Now, build and check:

trunk serve

Want to know more about WASM with Rust?

https://rustwasm.github.io/docs/book/