I have personally tried and seen various front-end frameworks for Web Apps in Rust. To be honest, they are pretty good and do their job well. But they do have some drawbacks, like a steep curve for understanding the API, and your code getting more complex as your app grows in size.
Now when I see the JS side of things, I see easy adoption and usage. But nah, I'm a Rust fan.
How to make things better? Find flaws and fix them. ๐
Flaws
A natural way of writing UI ๐ค
I felt the lack of a natural way of writing UI. Writing UI should not be hindered by other factors like the language features itself. Like implementing a Trait
and all functions along with it to display something simple as a Hello, World!
, felt a bit cumbersome.
Message passing is the way to go but not match
ing them ๐ฅบ
Matching messages to functionality, using a match
, might be an extra boilerplate that we can aim to reduce.
Virtual DOM ๐คจ
Current computers are fast enough, that differences between using a Virtual DOM and not using it, are too far less to notice. But yes, if it reduces performance, we can try to get rid of it.
Data centric and composable ๐ฎ
It is a lot easier to build composable UI. Why should the feature not be there?
So, what's next?
I looked at all this and thought, let's write a hypothetical UI first which might use a hypothetical library and then try implementing the library in Rust.
fn ui() -> Something {
div("Hello, World!")
}
fn run() {
App::render(ui)
}
How should we manage variables and states? It should be something like this and it should somehow magically update.
fn ui() -> Something {
let x = 0;
div(
x,
button("Add 1").on_click(|| { x += 1; }),
)
}
And how would we bind data to inputs?
fn ui() -> Something {
let text = String::new();
div(
text,
input("text").bind(text),
)
}
Looking at these snippets, pretty much basic functionality is thought of. Our plan is to implement State variables, update them when changed and bind them to inputs. Now let's come back to implementing the library.
Rethinking States
- State update should be asynchronous. Period.
- We are aiming for a message passing system, which passes messages to the receiver when the value is updated, and the receiver will be attached to elements and update their value as and when necessary.
- This means we don't need diffing or any Virtual DOM.
- It should also be bindable to elements and update when an input changes (and also be double way bindable).
- It should also be derivable, i.e. a state can derive values from other states, i.e. if State A is deriving its value from State B, when State B updates its value, State A should also do the same.
The solution I am trying to build: Valerie
Enter Valerie. The idea behind Valerie was to enable people to build clean web UI with Rust.
Let's start with "Hello, World!".
use valerie::prelude::components::*;
use valerie::prelude::*;
fn ui() -> Node {
h1!("Hello, World!").into()
}
#[valerie(start)]
pub fn run() {
App::render_single(ui());
}
The above code when compiled, is a .wasm
file of 14.8KB (6.8KB gzip).
Let's have a look at states.
fn ui() -> Node {
let string = StateMutex::new(String::new());
let length = StateAtomic::from(&string, |x| x.len());
div!(
h3!(string.clone()),
h3!(length),
input!("text").bind(string)
)
.into()
}
This will show an input which binds to a String
, and the length
variable derives its value from the string
State. And when you type something in the input text field, the data is updated magically.
The above code when compiled, is a .wasm
file of 44.7KB (15.1KB gzip).
One thing to notice here is StateAtomic
and StateMutex
, they are for types implementing Copy
and Clone
respectively. Internally StateAtomic
uses atomics for achieving concurrency and StateMutex
uses a mutex.
What if you want to click a button and increment some counter variable?
fn ui() -> Node {
let count = StateAtomic::new(0);
div!(
h3!(count.clone()),
button!("Click Me!")
.on_event("click", count, |x, _| {
*x += 1;
})
)
.into()
}
The above code when compiled, is a .wasm
file of 33.1KB (13.5KB gzip).
These are some of the many features of Valerie that I have showed here. The library is still in a very early phase.
Valerie is also a no-std
library.
Every piece of UI like a String
or div
implements the Component
trait. You can even extract a part of UI into a different function which returns an impl Component
and then reuse it when needed.
Rust follows the principle of Zero-cost abstractions, i.e. you don't have to pay the price of performance for the library features you don't use and whatever you do use you couldn't hand code any better. This means if you don't use a feature provided in Valerie, then it won't end up in the compiled .wasm
file.
And comparing WASM with JS, after being received by the browser, your browser has to convert JS into an AST, compile the necessary functions and run it. Now, that's a lot of work. But in the case of WASM, your browser can directly run it using a JIT compiler, as your .wasm
file is being received. Because a .wasm
file is an optimised portable binary, the browser doesn't have to waste resources optimising it.
Thank you for reading through the article and do take a look at Valerie.
emmanuelantony2000 / valerie
Rust front-end framework for building web apps
Valerie
Rust front-end framework for building web apps.
Valerie is still in a very early phase A lot of features are not available at the moment A lot of work is left and you are welcome to try it out.
- No Virtual DOM.
- UI can be made in a simple manner, by following an MVVM architecture rather an MVC architecture.
- Use state variables to update the UI where required.
- Written without any unsafe code.
Architecture
- Every UI element has to implement the
Component
trait. - A page is a function which returns a
Node
. - Two type of State variables
-
StateAtomic
for types implementingCopy
. -
StateMutex
for types implementingClone
.
-
Setting up
- Run
cargo new --lib some_name
- Add
valerie
to the dependencies - Create a
static
directory and create anindex.html
inside it
<!doctype html>
<html lang="en">
<head>
<meta charset="
โฆ
Top comments (13)
This was a fun read because you've basically captured some of the ideas that's been swirling around in my head for a while and I've been interested in Rust for a long while, but still have not had time to sink into learning it as I waste time reading Rust articles and watching Rust videos on YouTube... lol
I'm going to toss a couple things onto the heap that may serve as inspirations that may or may not align with your current goals with Valerie:
1) have you checked out Phoenix the web framework in the Elixir community? The default mode in Elixir is message passing being that it runs on the Erlang VM and an actor-based model of concurrency. I used to write a lot of JS, but this gives me an alternative not to and, frankly, I thought
LiveView
was going to be a gimmick, but this was pretty spectacular: youtube.com/watch?v=MZvmYaFkNJI. The core team built a dashboard providing real-time observability and introspection: news.ycombinator.com/item?id=228924012) you may already be familiar with SvelteJS, but def check it out if you aren't, because it was the library that made it apparent to me that a virtual-DOM is NOT needed and it is additional overhead which I think aligns with what you're trying to accomplish
Thanks. ๐
I'll do check out Phoenix. Yes I have checked out Svelte. And I have also derived some ideas for the syntax from Jetpack Compose and SwiftUI.
13kb for a div with button is pretty solid, but the direction is nice. Keep it up!
Care to elaborate? What do you mean exactly by "state updates"? On every major reactive front framework (Vue+React at least), the mutation part of a state update is completely synchronous (though all other stuff : action emissions, rendering etc.... are not).
Have you seen github.com/schell/mogwai ?
Oh yes, I have. Now, Valerie is focussed at simplicity and performance. We don't have benchmarks yet. The project is still very young. The idea is for people to write high performance wasm web apps, small in size, even if they aren't that fluent with Rust without having to maintain a complex UI code.
Very intuitive!
I see this as a step in bring you own language on web.
Finally you're not limited to the language of web, you can use anything.
Pretty cool, keep goin
Is this SSR? How do I setup a server to handle it?
No this is Client side rendered, SSR support will be added soon, do keep an eye out. ๐
This looks extremely promising ๐
I'll be keeping an eye out.
Fun! I hope to see more of it :)