I'm really not sure how this "composition" is any different to the usual inheritance you see in frameworks like QML *in practice*.
This in Ribir:
```
Column {
align_items: Align::Center,
item_gap: 12.,
@H1 { text: "Todo" }
}```
Would be this in QML:
```
ColumnLayout {
spacing: 12
Text {
Layout.alignment: Qt.AlignHCenter
text: "Todo"
font.pointSize: 17
}
}```
Yet both examples use macros.
I might still got ptsd from a job where literally all of the rust codebase was written as macros. Since then I avoid them at all costs.
I find the route, that gleam took, way more elegant with squirrel (sqlx-ish) and lustre (elm-like) being examples of what we could have instead. Avoiding language mixing is so important for proper/clean lsp-support - yet macros are a different language as i see it.
As for the rest of this: i also don't see how it's any different from iced, egui etc. but maybe I didn't take the time to check the details...
I like the idea of using macros to clean syntax; am writing some for EGUI right now to make colored text easier.
There are parts of the rust ecosystem that are only built for trivial apps (And demos, blog posts etc), but GUI is not one.
Use Rust to build multi-platform applications from a single codebase.

Ribir is a Rust GUI framework that helps you build beautiful and native multi-platform applications from a single codebase.
Experience a novel approach to UI development that's directly based on your data structure APIs. Any data mutation will trigger a precise UI update. Your focus should be on designing your data structure and its APIs. Then, you can describe your data's UI without intruding on its logic.
A simple example of a counter:
|
|
To use Ribir without DSL:
use ribir::prelude::*;
fn main() {
App::run_with_data(
|| Stateful::new(0),
move |cnt: &'static Stateful<i32>| {
let c_cnt = cnt.clone_writer();
let mut btn = Button::declarer();
btn
.on_tap(move |_| *c_cnt.write() += 1)
.with_h_align(HAlign::Center)
.with_v_align(VAlign::Center);
btn.finish().with_child(pipe!($read(cnt).to_string()))
});
}
More Examples
Compose, from other widgets composition.Render, implement your layout and paint anything you want.ComposeChild, control the compose logic between parent and child widgets and specify the template of child widgets.| Platform | Support situation |
|---|---|
| Linux | ✅ |
| Windows | ✅ |
| macOS | ✅ |
| iOS | 🚧 Not yet |
| Android | 🚧 Not yet |
| Web | ✅ |
Ribir is actively being developed and tested on desktop platforms, with both desktop and web platforms integrated into our CI/CD pipeline.
While the framework can compile and run on mobile platforms, we haven't fully tested or adapted the user interface for them yet. We plan to focus on mobile support once the core framework is stable and a production-ready widget library is released.
If you like Ribir, give our repo a ⭐ STAR ⬆️ and WATCH 👀 our repository to stay updated with the latest developments!
Every encouragement and feedback can support us to go further.
We are grateful to the community for contributing bug fixes and improvements.
😎 New to Ribir?
Start learning about the framework by helping us improve our documentation. Feel free to open a new "Documentation" issue. We are also very welcome:
For more information please read:
🤔 Confused about something?
Feel free to go to Discussions and open a new "Q&A" to get help from contributors. Often questions lead to improvements to the ergonomics of the framework, better documentation, and even new features!
😱 Found a bug?
Please report all bugs! We are happy to help support developers fix the bugs they find if they are interested and have the time.
This project exists thanks to all the people who contributed:
We also found inspiration from the following frameworks:
Ribir is MIT-licensed
- It's not multi-window, so it doesn't have to integrate with a bunch of the OS window management affordances.
- It doesn't have any complicated typesetting or rich text editing, so you get to pretty much ignore that whole mess.
- Since it's a very visual tool that doesn't make much sense for blind folks, you haven't invested much in accessibility support.
- And so on...
This is a great use case for EGUI, and EGUI works great in this sort of UI-lite scenario. Whereas I wouldn't want to use it to implement something on the complexity of VSCode/Excel/FireFox.
From the various experiments that popped up over the years, it's pretty clear that the React way works pretty well for Rust, but it's also too slow to be desirable for Rust (what's the point of using Rust for UI if you're going to have web-like performance).
And then again, making a half decent UI framework is a gigantic task, there's just not a whole lot of languages with a decent UI story at all, no matter what's the paradigm of the programming language. (And if you want a language for cross-platform UI, I'd argue that the only one that ticks the box is JS with React in Electron and React Native, and it's not even truly a single framework).
And quite amazing work, so thank you for sharing. This will be fun to dive into. I'm generally liking egui but have just recently been using it, so I'm still new at it.
You either end up needing to:
- handle all your state via interior mutability (i.e. Arc<RefCell<_>>)
- use a reducer (i.e. the state blob is immutable during rendering, updates are deferred via events that are delivered between frames)
- or invert the relationship between state and view (i.e. immediate-mode) which comes with it's own implementation challenges (caching immediate mode views is hard)
This is how I implemented my last Angular project, works fine for non-trivial tasks.