Seed – A Rust front-end framework for creating fast and reliable web apps
github.com
github.com
Overall, I'm happy with the way the Seed API turned out, but could never get the performance or package size to a good place.
That said, I love coding in Rust, and use it all the time for embedded!
Do you have any more details on that? I'm curious how close this got, and what were the main issues (on both size and speed).
- VDOMs in general have poor performance; they perform unnecessary computations in the diffing algorithm, and even well-designed ones perform extra DOM manipulations, compared to deliberately-written code.
- The Rust WASM files would come out pretty large; a few hundred KB for a minimal app. This is better than some packages using React + common JS dependencies like Lodash and Moment, but with much room for improvement
- DOM manipulation calls made in Rust via WASM(currently!) don't have any performance benefit over the native JS API
Benchmark results (look for "cope")[2][3]
I definitely think whatever happens over the next few years, VDOMs are on their way out.
[1]: https://github.com/whatisaphone/cope/blob/master/crates/js-f...
[2]: https://cdn.discordapp.com/attachments/751355413701591120/77...
[3]: https://cdn.discordapp.com/attachments/751355413701591120/77...
For anything low level like embedded, I highly recommend Rust.
For backend/servers, the language is fine, but it has no mature frameworks like Rails or Django. More like Flask analogues.
With Fable compiler in the frontend and .NET core on backend, you can write the same language on both and even share interfaces, types etc. and it’s just a pleasure to use. If you haven’t tried the .NET ecosystem in the past few years, it’s come a looong way from the old windows only days.
I am using this for my personal project: https://www.biblemaze.com which is a Bible trivia game.
Here are some things I like about Seed:
1. The Elm architecture seems to flow nicely in Rust.
2. The way of defining the elements on the page is more Rust like rather than html like. I initially tried Yew which is another similar framework, but since I am a more backend developer, I found writing in a more Rust style more comfortable than writing in a more html-like dsl. Other with different experiences may be more comfortable with more html-like syntax.
3. Very easy to perform REST API calls. I am using Actix for the backend, and I have a shared library that declares the types which both the backend and frontend use. This is one big advantage for using Rust for both backend and frontend, in that you never have to worry about your types getting out of sync.
4. In general as to why Rust for the front end, apart for being able to share types, for more complex algorithms, for me, having a compiled language with an emphasis on safety and that tries to ensure correctness as much as compile time (and especially pattern matching) makes writing complex code more enjoyable for me.
5. Excellent documentation on the Seed website. There are lots of examples and walkthroughs of basics as well as examples of how to do stuff like integrate with Javascript libraries.
Anyway, thanks again to the authors for making and supporting such an awesome library.
One thing to be aware of here, you may have users with stale clients after deploying an update to the server. e.g. picture someone who leaves a tab open for a week and then comes back to it and interacts with your app some more without refreshing.
Text based serializations will tend to be more forgiving of changes in the RPC schema (in the sense of erroring out when required fields are missing), while more compact serializations are more likely to incorrectly decode a message when they should fail.
It feels dirty because you’re not handling these issues silently. But it’s explicit, clear and it enables a consistent and performant experience.
Of course this is a hack compared to Rust BE and FE, but I’ve been finding that auto-generating Typescript type definitions from a Pydantic (Python) API works pretty nicely. Of course, what’s going on under the hood there is all a lot uglier than a beautiful modern compiler with an expressive modern type system, but it’s cool that it works.
Must we cede control of user agents to random third parties, loading and executing ever more obfuscated, inaccessible, bloated and hostile code? AFAICT, most of the coded loaded in a typical browser serves a user-hostile purpose.
Do you think wasm support should be mandatory for random web pages? Do you think it will not be used to shove even more bloated, hostile etc code? Will it not be used to circumvent/prevent adblockers etc? Do I need to link to a study showing most wasm is used in a hostile manner?
IMO, wasm is a net-negative on the web. Even though it does have significant good uses. It should be (or should have been) opt-in for specific pages that have a good use for it.
For ordinary web sites, the other extreme, DaisyUI, a CSS-only system, seems useful.
AFAIK Seed and Yew target the VDOM stuff which has always been an unnecessary overhead, Sycamore does what Svelte does.
It's very early days for Rust/WASM/Frontend, but all of this progress is very promising.
Svelte is a compiler through and through, providing and requiring its own syntax for things like conditionals and iteration, and declaring reactivity at the syntax level, inextricably tying its reactivity to properties on components, except for stores which allow regular JavaScript to get involved in reactivity (though components also get special support for stores via the $foo syntax). Dirty tracking happens at the component level.
Sycamore, on the other hand, is straight Rust (though typically assisted by procedural macros, which I will admit weakens the distinction), with only explicit reactivity (like Svelte’s stores), but keeping track of dependencies at runtime (more like Ember.js) and with things like conditionals handled as regular Rust code, and iteration as just a component that gets given an iterable rather than needing dedicated constructs. Dirty tracking happens at the data level.
(Qualifier: I’m expert with Rust and Svelte, fairly familiar with Ember.js, and quite familiar with some of Sycamore’s similar precursors, but I have only skimmed Sycamore, reading docs and a bit of code; I haven’t actually used it.)
The static part loads fairly fast, but none of the links work until after the wasm loads (you can tell because the header changes)
Looks like the entire site is in the wasm file as no additional network requests are made. Doesn't seem great for general purpose web sites (like your home page).
Frameworks like Next.js can help by adding seamless server side rendering so at least your links will work before your js/wasm is loaded. Maybe seed-rs can move into that direction as well.
Either way there are certain techniques like bundle-splitting and hydration that will probably have to be reinvented for these WASM frameworks, unfortunately. For the moment they're probably better suited to high-complexity browser-based tooling that needs the extra performance, vs simple crud sites.
Edit: on second thought, for an entire multi-page site in one bundle this wouldn't be super huge. I'm used to smaller numbers but I'm also used to split-bundles.
It doesn't really test Seed, but I guess Seed just works?! :shrug:
One reason it could fail for you on Chrome is that perhaps you tested on iOS; Chrome on iOS shouldn't make or break web applications because Apple forces WebKit onto every browser on that platform, so they're all Safari with a skin. A broken website in iOS Safari is usually broken in all other browsers by Apple's design.
Otherwise the code should work fine on up-to-date Chrome across all real Chrome platforms, unless you specifically toggled flags...
[0]: https://caniuse.com/mdn-javascript_builtins_sharedarraybuffe...
Anyone else think it’s insane that we’re still implementing JS date pickers in 2021? How was this not native like 20 years ago?
Don't get me wrong. I'm glad we finally have some consensus, but this should have happened before ES6 (2015!) IMO. jQuery had a date picker before 2009 at least (probably earlier, but I'm not sure).
Date inputs were standardized with HTML5, but browser implementations are of variable quality (and, important to designers, variable default look-and-feel, and limited, IIRC, customizability).
Unique or not, I bet it makes working with the borrow-checker a whole lot easier
It also supports server-side rendering, and is used in one of my other opensource project svgbob[3]
[0]: https://github.com/ivanceras/sauron
[1]: https://ivanceras.github.io/ultron/
[0]: https://github.com/ivanceras/sauron/blob/master/Changelog.md
This allows us to:
* Share code with our server written in Rust.
* Avoid keeping types in TypeScript in sync with Rust.
* Do server rendering without including a JavaScript runtime.
* Avoid maintaining a JS stack, including TypeScript, Prettier, ESLint, and a bundler.
Because we split our project into many crates, incremental build times are about 1-2 seconds on a linux desktop. This is not as convenient for quick iteration on UI/UX as hot reload with React, but on the other hand, code that type checks is more likely to run correctly.
Overall, I would say that the trade offs are not clear, even for our unique case where we have a server written in Rust.
I am hopeful that in the future, browsers will enable native DOM access from WASM, and the Rust compiler will support code splitting between multiple WASM entrypoints. When this happens, I think there will be a strong case to recommend Rust for more front end projects.
Out of curiosity, did you try auto-generating the typescript types from the Rust types?
It would be really nice if this could be improved in the future.
Having two separate coding languages in a single file that switch back and forth is such a ridiculous idea to me. And on top of that, I don't think jsx is any more readable than nested functions.
My thoughts;
Pros:
- Definitely will be useful for resource intensive application (rather not the majority of web and desktop apps)
- Better source code protection if you are shipping a proprietary product (some people may view it as a con)
Cons:
- Build times ?
- Larger download size (for web apps)
- Rust is safe, but a GCed language will always be safer and have less low level overhead for the developer
The beauty of JS and especially the beauty of ClojureScript is that you can immediately see the effects of your work in an interactive environment. Using the right libraries you can have true hot reloading without destroying state giving you a terrific, fast, fluid development experience.
What are acceptable compile and reload times for you guys loading WASM?
If ClojureScript is compiled, why can't WASM be the same?
What’s the equiv workflow when compiling to wasm? Save file > compile > hot reload? > reconstruct state somehow?
Is it like clojurescript where you interactively work on the app in the same way you might interactively craft a sql statement in a live db?
Realistically, hot reload is going to be something like inotify(7) hooked into a compiler and whatever it takes to reload seamlessly. If it's been solved in one general purpose language, it's likely to be possible in another.
[1] https://visualstudiomagazine.com/articles/2021/07/29/cpp-hot...
But this is missing the difference.
When we say hot reload in javascript (or even better, hot module replacement) or java or rust or c++, there's usually annoying limitations like you can't change the arity of a function or you have to pause all the threads or you need some mechanism to reset state to a known good starting point because we just broke it (potentially).
The difference with a lisp is that you don't need do any of that. You replace the function object in the system image (atomically) and you crack on like nothing happened. No waiting around, no rerunning state into the app. And you triggered it just from a keystroke in your editor.
The developer experience is unparalleled.
I fully agree that toolchain you mentioned gives blazing fast initial devX, hot reload and everything. If that’s what you are optimizing for, it is a perfect solution.
OTOH when you want to optimize for long term maintenance, growing codebase that is safe and easy to grow, easy to refactor, easy to check for odd things. I’d lean heavily on something compiled and with promise/guarantee of safety every time.
In other words, it is all about trade offs and to each his own.
There are also lots of frontend bugs that aren't really bugs in the compiler sense, but just cause the user to see a slightly wrong or confusing thing for a given state. Tests can help, but for a UI with a lot going on, there are usually too many possible paths to write tests for them all, so doing a lot of QA as you go is hard to avoid.
Typescript offers a pretty good balance here since you can turn off typechecking for live reloading and instead check types in the background or as part of the test suite.
Here is a couple of reasons: 1. It is backed up by a statically typed, safe, elegant language that is like a strict teacher that tells you to fuck off when you make a mistake... 2. HMR is possible with this... 3. Look, WASM is not fighting against you, it is fighting for you... anyone who is targeting JS and is forced to do so is welcome to choose any other language you want...
I'm not sure if this is because of the complexity of the undertaking (I imagine it would need some sort of virtual DOM) or just lack of interest
Time and space are in tension, and the web is more sensitive to binary size than most other targets, including many embedded targets.
To take one example that often contributes a ton to binary size, consider data serialization in Rust vs JS. JS will tend to just use JSON.parse and deserialize into a JS object with little to no data validation. The JS object is then exclusively accessed via dynamic field lookup (with a JIT doing its best to notice patterns and hopefully optimize the hot paths). The same code paths can handle many different message shapes. It's far from optimal in terms of runtime speed, but the code can be made very compact.
Rust deserializers by contrast seem to use compiler-time code generation to produce a different optimized struct layout, parser, and validator for every message type. When I've played with writing very small Rust + wasm apps, the choice of serialization library and format made a huge difference. If I recall correctly, using BSON + serde would have increased my total binary size by more than 3x.
This isn't a fundamental issue with Rust as a language, I think it's partly still early days for the tooling.
There is, however a certain fundamental amount of tension there in what's optimized for. In most environments, 30% faster deserialization for a binary that's 500KB larger (pulling numbers out of my ass) is in the "obviously yes, do that" category, but on the web adding 500KB to your binary is a steep price to pay, particularly for mobile users.
* SSR = server side rendering
Related terms include:
* CSR = client-side rendering
* PWA = progressive web app
P.S. I am not a bot. (But don't all bots say this?)
You might be surprised. JS is a very slow language to parse. WASM was designed to be parsed quickly. And browsers have implemented streaming compilation for WASM, which means once the last byte comes over the wire, 99% of the work is already done.
Check out js-framework-benchmark[1] startup metrics. The page weight for Yew (another WASM framework) is pretty heavyweight at 300KB, but it has a consistently faster time-to-interactive than vanillajs.
[1]: https://krausest.github.io/js-framework-benchmark/current.ht...
Does wasm app save memory significantly at runtime? If not why should one choose manual memory management outside of porting existing code?
Do you think Rust might succeed here where FP couldn't really make much of an impact?
And I wouldn't frame it as Rust succeeding where FP failed, but instead a way to sneak in FP into the frontend in a way that gets really angry at you when you try to structure your code as mutable and imperative. Yes, most of the frontend languages let you opt in to constness and compile time typesafety, but Rust makes you opt out. It's soft developer thing, but it seems to have benefits to code quality from my experience.
Let me be first saying that just like you don't need React, Ember, Angular, etc. to build your site, you also don't need the WASM as well!
Im a little disappointed to see the amount of boilerplate in the examples. The Todo app is infinitely-5 longer than the Clojurescript example.
Iron and Actix are the more established but more complicated ones
Edit: According to a reply, Iron is considered abandoned
I guess maybe these benchmarks only test the overhead of the framework itself, and ignore the benefits of having your business logic and middleware written in a faster language? The tests for node and PHP would pretty much only be running battle-tested native code if that's what's going on
0.5 however is async and is currently in rc, when it is released I would expect it to climb up the list a little bit.
Sentry is rewriting some of their libs from Actix to Axum: https://github.com/getsentry/symbolicator/commit/b6ef7cb00b7...
The most complicated pages are in React/TS (eg the actual scheduling board), and the Seed pages are/were mostly simpler ones, like tracking qualifications, personalized schedules etc. I'm actively rewriting the smaller pages from Seed/Rust and TS/React to HTML/CSS/JS for performance and simplicity reasons.
This doesn't directly answer your question, but is the context in why Seed started, with Yew already existing.
An immediately noticeable difference is that Yew uses a JSX-like syntax, while Seed uses a custom API based on list-like macros.
[0]: https://ivanceras.github.io/svgbob-editor/ [1]: https://ivanceras.github.io/ultron/
In two years, I fully expect Rust to begin taking off as a major frontend language choice. A framework like Seed will help usher this in.
It'll be faster than Javascript, eventually multithreaded, and produce portable binaries that can run on a wide variety of platforms and architectures.
It'll paint to DOM, but also do immediate mode and other types of rendering.
We'll see incredibly rich applications: games, video editors, IDEs, etc.
Electron will yield to this. We'll have fast, cross-platform apps that run not just on the browser, but natively on Windows, Linux, and Mac.
If we're lucky, we might even start writing mobile apps this way and ditch the iPhone/Android specific APIs.
Don't get me wrong, I hate Javascript as a language. But sometimes worse is better.
> It refers to the argument that software quality does not necessarily increase with functionality: that there is a point where less functionality ("worse") is a preferable option ("better") in terms of practicality and usability. Software that is limited, but simple to use, may be more appealing to the user and market than the reverse.
( Source: https://en.wikipedia.org/wiki/Worse_is_better )
JS is simpler and cheaper thus preferable.
I guess I took for granted that not everyone is familiar with that phrase, sorry
That's not true, current JS is getting complex, but we don't have a choice, except for WASM but the options aren't great. TypeScript is also very complex, but it's the most popular so people use it. Basic Rust isn't that complex, and I would argue is easier than TypeScript. I wouldn't want to be the guy building the framework, but using Rust to write business logic? That might be nice.
On the backend side, Java dominates, with PHP, JS/TS, Python. All of these (in their modern form) are complex, usually by way of OO soup, or big frameworks, or a large ecosystem that you have to learn at first. On the other hand, languages that people like calling "simple" like Go or Clojure are relatively nice (at least for "business logic" web apps, Go is popular for "infra/devops" web stuff").
Elm doesn't have the JS ecosystem, Rust doesn't, Rescript doesn't. The thing is, when you're doing backend stuff, it's usually relatively easy to pull a library with whatever framework you're using. On the frontend, for your components, you need basically a library by framework by language.
Not enough people care enough to make it happen. I think most people underestimate the breadth of the work that would have to be done. The people in the best position do do it (the browser vendors) aren’t willing to do that.