@Antonin Komenda asked about client side Roc... so I made a small demo using wasm-pack.
https://github.com/lukewilliamboswell/roc-experiment-js-dom
I've made some major upgrades to this demo... we now have (partially) working Action-State. :smiley:
I almost got it fully working, but ran into a lambda set issue specifically with handling events. I've got a workaround using an update
function and I hope this will be easy to remove in future.
Anyway, here is a demo of the click example (inspired by the counter example from Action-State proposal).
I'm pretty stoked with the performance and size. This demo .wasm
is only 190KB for the debug build**.
The API is a bit rough... and the "events" are hardcoded to onclick
, but it's pretty much all there and working.
Hope to see someone build something cool with this.
[edit] ** actually by default wasm-pack builds without debug symbols, see https://rustwasm.github.io/wasm-pack/book/commands/build.html we need to use --dev
to get the full debug symbols and run debug assertions etc, which blows the size up to 1MB.
I wanted to explore what was taking up so much space in the minimal example. I suspect most of it is rust std
library stuff which we can remove by adding #![no_std]
. This would mean we'd have to roll our own no_std VDOM in rust, but that should be doable I think.
Here's the smaller size branch https://github.com/lukewilliamboswell/roc-experiment-js-dom/tree/minimal-size, I just hacked some things out and made sure it was still calling roc in a meaningful way.
So basically this no_std host impl...
#[wasm_bindgen]
pub fn run() {
console::log("INFO: STARTING APP...");
let boxed_model = roc::roc_init();
let roc_html = roc::roc_render(boxed_model);
assert_eq!(
roc_html.discriminant(),
roc::glue::DiscriminantHtml::Element
);
console::log("HELLO");
}
Gives us these sizes...which when compressed is around 11KB
for a minimal hello-world.
Screenshot 2024-11-28 at 11.49.36.png
$ ls -hl
total 64
-rw-r--r--@ 1 luke staff 397B 28 Nov 11:49 package.json
-rw-r--r--@ 1 luke staff 1.5K 28 Nov 11:49 web.d.ts
-rw-r--r--@ 1 luke staff 4.6K 28 Nov 11:49 web.js
-rw-r--r--@ 1 luke staff 579B 28 Nov 11:49 web_bg.wasm.d.ts
-rw-r--r--@ 1 luke staff 11K 28 Nov 11:49 web_bg.wasm.gz
Luke Boswell said:
we'd have to roll our own no_std VDOM in rust, but that should be doable I think.
I think @Brian Carroll may have already made some progress in this direction if I remember right?
11K compressed for the hello world Action-State application (without a real host) is smaller than I would have guessed! :smiley:
maybe with a real (no_std
) host the whole thing could end up being smaller than React
probably not Elm and certainly not Svelte, but React seems very plausible with that number as a starting point
Richard Feldman said:
I think Brian Carroll may have already made some progress in this direction if I remember right?
No I didn't use any Rust at all. My unfinished project was almost entirely in Roc with some JS and a tiny amount of Zig. It's still in the repo and there's a README explaining what I did and didn't get done.
https://github.com/roc-lang/roc/tree/main/examples/virtual-dom-wip
Basic idea was to do the diffing in wasm and then call out to some JS functions to apply the patches. That minimises the amount of data you need to send over the Wasm/JS boundary (that is a bottleneck since everything needs to be serialized).
Last updated: Jul 06 2025 at 12:14 UTC