Hacker News
points by nicoburns 1 day ago | 0 comments
If you're interested in a GPU-rendered Rust UI framework, then maybe skip this vibe coded project, and check out some of following established projects:

  Rust-native GUI frameworks (by crates.io downloads):

   1. egui (30.3M total / 9.2M last 90 days)
      https://crates.io/crates/egui

   2. Iced (3.2M / 770k)
      https://crates.io/crates/iced

   3. Slint (2.4M / 955k)
      https://crates.io/crates/slint

   4. GPUI (447k / 255k)
      https://crates.io/crates/gpui

   5. Dioxus Native (181k / 26k)
      https://crates.io/crates/dioxus-native

   6. Freya (49k / 12k)
      https://crates.io/crates/freya

   7. Floem (45k / 15k)
      https://crates.io/crates/floem

   8. Vizia (7k / 700)
      https://crates.io/crates/vizia
List is hand-picked list, ordered by crates.io downloads. I had an LLM fetch the download counts and links. Webview based UI like Tauri and Dioxus Desktop are excluded. As are bindings to native toolkits and GTK.
[–] bryanlarsen 1 day ago
IMO, the top GUI framework crates for Rust are windows or native-windows-gui, objc2 or icrate for MacOS and gtk4-rs for Linux only. Actually for MacOS / iOS I'd use uniffi to embed the rust core in a SwiftUI app.

In 2026 IMO it's much better to use an LLM to keep 3 separate native front ends in sync than it is to try and use any sort of cross platform UI.

[–] the__alchemist 1 day ago
Could you please explain why? For example: Is it colors? A je-ne-sais-quoi? Widget rounding size and fonts? Performance reasons? Keyboard shortcuts? Do you really find it's easier to maintain 3 separate frontends using an LLM than using EGUI, GPUI, QT etc?

I have seen this suggestion several times recently regarding GUI framework threads on HN in the past week (There has been a surge!), but they are always described in the way you did: With no explanation of why. Just an assertion that native UI kits (of which you need a separate one for each OS family) are superior; cross-platform kids are an "inferior experience" etc. I have asked at 3 times and received no answer. I say this as someone who uses both Windows and Linux regularly, and find they are functionally equivalent once you have an application launched. There are ABI differences, driver differences, file system differences, etc which need to be smoothed over with feature gates for certain application functionality, if you're using certain functionality.

I want my applications to have the required functionality, run fast, and have a good UI. I don't care if the widgets look the same as on my other applications or the ones built in to the OS; this isn't even a thing because there is so little standardization broadly. Maybe on Macs it's standardized and cross-platform kits look out of place?

The proposed approach of "Use LLMs to vibe-code a system of 3 native kits" has cons to weigh. Or making your program Windows only or w/e for a look-and-feel reason or whatever it is has cons too. IMO all our OSes are so similar, PC software should almost all be cross-platform. Exceptions are mostly drivers and driver-adjacent code.

[–] grub5000 1 day ago
Windows and Linux actually feel pretty close these days, and I don’t generally feel any friction switching between them. But when I try to use a Mac it’s another story entirely. That OS just makes no sense to me - the keyboard shortcuts all seem to be insanely bad choices, just inconsistent with huge gaps and divergences from the other OSs. It feels so stuck in the 90s
[–] the__alchemist 18 hours ago
This checks out. My leading mental model to explain this mismatch is that the critique has been from people who are expecting Mac idioms, which I can't speak to. Window and Linux GUI look/feel are fragmented as-is.
[–] kleeklee 1 day ago
Keeping 3 separate native front ends in sync with custom visual components means you end up building a cross-platform UI. The only apps you can build without custom components are trivial ones.
[–] bryanlarsen 1 day ago
All 3 have a way of embedding webviews. In my perhaps limited experience, all desire for custom visual components are either specious or for read-only presentation views. In my opinion all controls should be native, but if you ask for a fancy data presentation I have no qualms reaching for a webview.
[–] jasomill 1 day ago
So long as the web views provide for accessibility (and thus UI automation), this sounds perfectly reasonable for the majority of applications.

Speaking of which, my dream is an "HTML light" standard with a subset of functionality specifically designed for local UI use, ideally with lightweight non-browser engines supporting this standard shipping with all major OS platforms as shared system components.

We had browsers that ran on systems with 8 MB RAM, after all, and while it's not a fair comparison, there's no technical reason why a system with three orders of magnitude more RAM should struggle to run more than a handful of applications merely because they use HTML and JavaScript for rendering.

[–] nicoburns 1 day ago
I am building such a lightweight browser engine: https://github.com/dioxuslabs/blitz. It targets most of HTML/CSS, but leaves out (or makes optinal) a lot of the extra stuff that browsers pull in). It also has the option to use Rust instead of JavaScript for the scripting layer.

Though 8 MB RAM is a non-starter when a single RGBA texture @4k is ~30Mb and modern system often require 2 or 3 for buffering. Somewhere around the 60MB to 100MB mark seems to be the base for modern setups, at least on macOS.

[–] simonask 1 day ago
> perhaps limited experience

New rule: Programmers with limited UI experience (that includes myself) should abstain from weighing in on what UI designers and programmers need.

[–] tadfisher 1 day ago
An advantage of this approach is that everyone's screen readers and other accessibility tools actually work with your app.
[–] treyd 1 day ago
There's also relm4 which builds a Elm-like paradigm on top of gtk4-rs.
[–] nextaccountic 1 day ago
rather than gtk4-rs maybe try relm4
[–] auraham 1 day ago
https://crates.io/crates/dioxus-native was released 3 months ago and does not have a link to GitHub, I do not think it can be considered as established project yet.
[–] nicoburns 1 day ago
(I am the maintainer of dioxus native, and also posted the comment above)

So:

- The initial version was released over a year ago (3 month is the most recent patch release of the stable version).

- It is part of the Dioxus project (https://github.com/dioxuslabs/dioxus) which has been going since 2021 (but the other renderers are web/webview based).

- The native renderer (https://github.com/dioxuslabs/blitz) has been under development since December 2024 (building a whole new web engine is just a big project).

You're ultimately kind of right that it's not as established as 1-4 in the list. But I think it justifies it's place with the other frameworks lower down the list as one of the lesser-known frameworks that compete technically with those further up the list.

[–] nextaccountic 1 day ago
Is dioxus native usable in android already?
[–] nicoburns 9 hours ago
To the limit that any Winit-based app is. Which is to say it builds and runs, and there is basic touch and text input support. But system integration is pretty limited.
[–] drnick1 1 day ago
Stick to Qt, none of those solutions are mature enough for actual production use.
[–] satvikpendem 1 day ago
Lots of companies use them in production.
[–] embedding-shape 14 hours ago
Interesting to say given you know zero about the context which other's use it. Not everyone has the same needs everywhere, and blanket saying "No not ready" when you don't understand the context feels very premature, ironically.
[–] plaguuuuuu 18 hours ago
most downloaded is a poor metric
[–] amelius 1 day ago
Slint is proprietary. Given that AI makes it so easy to spec out and build even entire GUI frameworks, why would you even go with proprietary anymore? And why doesn't the article showcase a full-fledged set of UI components?
[–] dmantis 22 hours ago
I might be wrong, but I believe Slint has qt-style dual-licensing and provides AGPL version for GPL projects.

I assume you make an open source project giving the critics about proprietary nature, so it shouldn't be a problem.

[–] xedrac 20 hours ago
> AI makes it so easy to spec out and build even entire GUI frameworks

When a vibe coded gui framework becomes so good that it simply cannot be ignored, I'll believe this can work. Until then, all I've seen is half-baked disposable slop that looks really good on paper.

[–] noodletheworld 1 day ago
> List is hand-picked list, ordered by crates.io downloads.

O_o

Just say it’s a hand picked list of ones you like and why.

Do you like them? Have you used them?

I found iced to be unreliable (couldn't reliably render basic widgets); slint overly relies on their DSL and honestly seemed to be moving away from rust; and GPUI felt sloppy. That was my personal “lets try rust gui” journey before I went; nope, guess we’re doing native ui and a shared rust core.

This is just a random list of stuff; I can't tell if you think they're all good (I dont) or just copy pasted chatgpt output.

For each one: why is it on the list? Do you like it? Why do you think, for example, slint, deserves to be on the list, when you dont even write the ui scripts in rust?

[–] ogoffart 1 day ago
(Slint author here) Slint is written in Rust, and Rust API is at its core. That's not changing. What's wrong with the DSL? the UI is described in the DSL, the application logic in Rust. I wrote about why here: https://slint.dev/blog/domain-specific-language-vs-imperativ...

What gave you the impression we're moving away from Rust?

[–] noodletheworld 1 day ago
The website developer docs.

Look, bluntly, you can’t have your cake and eat it to. Either it’s a rust library or it’s not.

If you’re not writing your ui in rust, then even if parts of it are implemented in rust, it’s not a rust ui library any more than a js library like calls a c backend is a c library.

I really really wanted to like slint and absolutely did not.

> Domain Specific Languages like Slint offer a great alternative to traditional GUI toolkit APIs.

No they don’t.

If you can arbitrarily assert something so can I.

A DSL needs it clear the minimum bar of “why is this not just using a real programming language?”.

UI programming is tricky, but declarative approaches have almost universally failed, because there’s always some edge case logic that needs a full programming language to handle it.

The few UI DSLs that Ive seen work (eg. blueprint) exist a) as stand alone languages, and b) fill a niche that an existing language doesn’t.

What does your DSL do that is unique? Why was using js like qt quick not good enough? Why not just use rust?

The issue I have with it is you’ve never articulated why it exists.

Good DSLs like those implemented using clojure (1) are additions to the language that can natively be mixed into the existing language ecosystem.

You could have done the same in rust using proc macros; but instead you’ve chosen (as far as I can tell) to chase the non-rust UI market with a custom DSL.

Well.. you want an opinion? There you go. I don’t like slint, sorry. It’s not a rust UI library. It just happens to be implemented in rust, in my opinion.

(1) - https://clojure-doc.org/articles/cookbooks/growing_a_dsl_wit...

[–] ogoffart 23 hours ago
> Either it's a rust library or it's not.

It is one. Your app is a Rust program that depends on a crate. The UI compiles to Rust code, and we also have the `slint!` proc macro.

> there's always some edge case logic that needs a full programming language

Right, and that language is Rust. The DSL only describes the GUI. You operate it from Rust.

> Why was using js like qt quick not good enough?

QML was the inspiration. I worked on Qt and wrote QML bindings for Rust before (qmetaobject-rs). Slint is a new take on it, compiled ahead of time, no GC, strongly typed.

[–] noodletheworld 22 hours ago
> The UI compiles to Rust

Right, so not rust.

Languages that compile to c are not c.

Languages that compile to rust are not rust.

> QML was the inspiration

Yes, except you invented a new programming (1) language unnecessarily.

Whatever.

We'll have to agree to disagree.

If the programming language you write is not rust it is not a rust ui library.

Slint requires you to write your UI in slint. I havent used it much, but Ive used it enough to know that this:

> The DSL only describes the GUI

Is about as disingenuous as you can get.

Thats like saying that javascript only describes the GUI of a website, a backend rust website is really just a rust application. Really you're building in rust. React is really a rust UI library at heart. <—- obviously BS.

(1) - https://docs.slint.dev/latest/docs/slint/guide/language/conc...

[–] well_ackshually 15 hours ago
>A DSL needs it clear the minimum bar of “why is this not just using a real programming language?”.

No, a DSL only needs to be better than the language itself to write a layout in. UI is a whole different beast than anything else, where deep nesting is common, layouts flow in many ways, and more. Rust is simply not appropriate to make that pleasant.

Take a simple layout like you'd declare it in Kotlin using Compose:

    @Composable
    fun GreetingScreen(title: String, onBack: () -> Unit) {
        var count by remember { mutableStateOf(0) }
        val scope = rememberCoroutineScope()

        Column(Modifier.fillMaxSize()) {
            Surface(Modifier.weight(1f)) {
                 Text("Count: $count", style = TextStyles.h1)
            }

            Button(onClick = onBack) {
                 Button(onClick = { count++ }) { Text("+1") }
                 Button(onClick = {
                    scope.launch {
                        delay(1000)
                        count++
                    }
                 }) { Text("+1 later") }
            }
        }
    }

Kotlin naturally has trailing lambdas, method references, has the advantage of being a GC'd language so memory management is out the window.

In pure rust ?

    pub fn counter_screen(ui: &mut Composer, on_back: Rc<dyn Fn()>) {
        let count = remember(ui, || 0_i32);

        column(ui, Modifier::new().fill_max_size(), {
            let count = count.clone();
            let on_back = Rc::clone(&on_back);
            move |ui, col| {
                surface(ui, Modifier::new().weight(col, 1.0), {
                    let count = count.clone();
                    move |ui| {
                        let label = format!("Count: {}", *count.lock().unwrap());
                        text(ui, TextProps {
                            value: &label,
                            style: Some(&TextStyles::H1),
                            ..Default::default()
                        });
                    }
                });

                row(ui, Modifier::new(), {
                    let count = count.clone();
                    move |ui, _row| {
                        button(ui, ButtonProps {
                            on_click: {
                                let count = count.clone();
                                move || count.update(|n| *n += 1)
                            },
                            enabled: true,
                            modifier: Modifier::new(),
                        }, |ui| text(ui, TextProps { value: "+1", ..Default::default() }));

                        button(ui, ButtonProps {
                            on_click: {
                                let count = count.clone();
                                move || {
                                    let count = count.clone();
                                    spawn(async move {
                                        sleep(Duration::from_secs(1)).await;
                                        count.update(|n| *n += 1);
                                    });
                                }
                            },
                            enabled: true,
                            modifier: Modifier::new(),
                        }, |ui| text(ui, TextProps { value: "+1 later", ..Default::default() }));
                }
            });

            button(ui, ButtonProps {
                on_click: { let on_back = Rc::clone(&on_back); move || on_back() },
                enabled: true,
                modifier: Modifier::new(),
            }, |ui| text(ui, TextProps { value: "Go back", ..Default::default() }));
        }
    });
}

What a delicious piece of trash. Since you've said no macros, you're carrying an additional variable everywhere (Composer is a Compose system, but you have the same thing in every UI library that does the same. Call it Context, whatever you want). You have shitty lambdas that need to pass the scope too (because Modifier.weight() only works on a ColumnScope). You have no named parameters, and the only way you would would be crap like having a ButtonProps struct. You have impl Fn() + 'static written in your code. And then if you start to want to do some actual work like "clicking on the button increases a counter", you're suddenly spamming Arc::clone, |move| Rc's everywhere in your code because Rust is simply not a good language for UIs.

(And if you want to go the old school way of simply managing yourself a widget tree, good luck, not only is it always full of bugs, it's also an unmaintainable garbage fire.)

Clojure is not a good language for DSLs either, and the post you've linked is just jerking itself off saying "look i can convert some clojure into the exact bash equivalent by only changing one thing" when it's the exact reason why every single clojure project ends up reimplementing the wheel twelves times and is a piece of shit unmaintainable kludge of whatever code the local genius wanted to commit in this morning. Good languages have constraints.

So yes: DSLs _always_ offer a better alternative. QML is a good DSL, and using Javascript is not the reason why.

[–] eaf7e281 1 day ago
It seems like self promotion, but in a smart way.

https://news.ycombinator.com/item?id=50394569#50398826

[–] HackerThemAll 1 day ago
Yeah, have you tried to do anything in egui? I tried today a ComboBox, but I wanted one with filter input box, "X" icon to clear it, and automatic focus.

I've given up when the code reached 300 lines and was still buggy/not working.

egui is not mature. I'd prefer declarative approach, data binding, and styling. Therefore will skip egui.

[–] slopinthebag 1 day ago
i've built pretty complex stuff in egui. any modern agent will one-shot that component, so i'd say it's more just a lack of familiarity with the library.
[–] jbirnick 1 day ago
Luckily GPUI isn't vibe coded at all but a mature and well thought-out project.. /s
[–] hmry 1 day ago
I tried using it and yeah, GPUI felt very sloppy. Something about that lack of documentation + having to install it from some subdirectory in a giant monorepo + Tailwind-based API...

And the recommended component library for it (GPUI Kit) seems to have been 100% Clauded, down to the website and README.

[–] nextaccountic 1 day ago
the component library is gpui-component which I think is a too old of a project to have been built primarily by AI

gpui-kit is meant to solve your second point: installing it from crates.io rather some unrelated monorepo

it doesn't matter if gpui-kit is vibe coded because it's just upstream gpui, repackaged

[–] hmry 1 day ago
Hmm, I see, thanks for the correction
[–] Varelion 1 day ago
Brutal and based