Desktop web-apps won. Simply because native UI libraries never evolved past their 90s days. Either the UI is defined in some DSL, that’s loaded (or compiled) and then you spend most of the time writing getElement(pathToElement) and wiring it up, or you have to boilerplate create each element and parent.addChild(element).
And wiring it up is also a pain. Send a signal or event, add a listener or slot, or whatever fancy name each framework comes up with, and if you have to modify another element, it means querying for it, or having a singleton, or passing a reference/pointer, or whatever. It’s so friggin-old school.
In the meanwhile, the web discovered reactivity, components, declaring the UI and having the logic in the same file, live debugging, tight development loops, and so much more.
Is it just too difficult for native frameworks? Is it a sunken cost issue or fear of breaking backwards compatibility? Why can’t native UI development be as easy and approachable as web dev?
Don’t get me wrong, I need webdev like a child needs cancer, but I’ve tried Slint, imGUi, Qt, Gtk, wxWidgets, and more and the experience makes me want to blow my brains out every single time. I dread writing any native GUI that I got desperate enough to try writing a TUI but that’s unbelievably worse!
It’s gotten so bad, that Tauri and Dioxus are now on the menu. I never wanted to mix web dev into my native applications, but it feels like the abominably anachronistic state of native UI development is just forcing not only me, but anybody who wants to have a good experience writing native UI apps (especially those that are multi-platform), to use a fucking web view! A memory-hogging web view!
my personal theory is that it’s a cultural problem. same reason web apps are such memory hogs. front end web devs, as a culture, do not care about performance, but they really care about developer experience. the opposite is true of systems developers
As a developer who has mostly worked with web, but also dabbled in some native app work: It’s not that the web UI frameworks are so much nicer. The native libraries I’ve used, at least, are actually much nicer to work with. I’ve worked with Delphi, Java Swing, various Windows frameworks, etc. React and friends are a chaotic mess in comparison and HTML was not designed for app development. You want a button? Here’s a div, go ahead and style it. Thanks, let me add 500 npm packages to my project.
No, the main reason I prefer to develop web apps is because they’re effortlessly cross-platform and automatically updated and distributed. No maintaining multiple versions. Updates are basically instant and happen across your user base. No code signing or paying to compile code on a Mac. No asking for install permissions on stupidly locked down enterprise workstations. Just deploy and go.
I can count on one hand the number of times I’ve needed to create a native app due to some restriction like local file access or device access. Most of the time you’re just entering values into a database, so that can just be a website. PWAs are a pain to develop but they are much easier to deal with once they’re in the wild.
YMMV of course.
I recently needed to delete a node_modules directory for a relatively small app. 20 minutes of my life on that shite 💀
I’m pretty sure that most developers that use web UIs do so for portability.
There are non-Web-based cross-platform GUI toolkits, like Java’s Swing.
As to why cross-platform desktop toolkits haven’t really caught on…I’d say that it’s because there are things that you can’t really abstract all that well. There are ways that a well-written MacOS app should function, ways that a well-written Windows app should function, ways that a well-written GTK app should function, and so forth.
EDIT:
I dread writing any native GUI that I got desperate enough to try writing a TUI but that’s unbelievably worse!
Honestly, the same is true of TUIs, too. Like, MS-DOS/Windows TUI conventions and Unix TUI conventions aren’t really the same. Like, in a typical Unix TUI program, I expect “q” probably quits. At text prompts, I expect that I probably have readline support and likely the vi/emacs key support there. It probably defaults to white-on-black. In an MS-DOS TUI program, Escape probably exits, and I’m probably using white-on-blue. I’m probably using Code Page 437 box-drawing characters.
EDIT2:
ncduon Unix:
WordPerfect for DOS:

I haven’t seriously used it myself, but maybe Qt Quick is somewhat like you’re looking for?
OP mentioned Qt but didn’t make it clear whether they were referring to Widgets or QuickSync I reckon they didn’t know about Quick because properly written QML doesn’t need much signals. It’s downright magical sometimes.
I feel you. I don’t particularly like any frontend work, but desktop frameworks can go to hell. No wonder why electron apps took off in the last decade, despite all its problems.
I share your frustration, so I am also interested in seeing some responses here.
The closest I have got is trying out flutter.
Mac UI development was the nicest last i tried. Even that has a slow dev loop compared to Web.
I did ok with GTK plus a nicely fixtured launcher. Cut the cycle time down to dozens of seconds. But still… Meh.
I had my own library for UI that tried to have a element model, but it was bloated and didn’t scale well. Now im drawing everything via vulkan compute shaders and its fast and looks great.
I think the philosophy needs to switch from elements back to pixels, atleast that is what worked for me.
i dare to say its as easy as webdev
snippet from my game:
DrawMenuButton(buf, btnExit, "Exit to title", exitHover, exitHover && window.mouseLeftHeld, false, font, topFontPx, exitCol); DrawMenuButton(buf, btnSave, "Save changes", saveHover, saveHover && window.mouseLeftHeld, false, font, topFontPx, saveCol); PushPanel(buf, titleRect, panelBgColor, 6.0f * s); PushBorder(buf, titleRect, borderColor, 6.0f * s, 1.5f * s); DrawText(buf, "OPTIONS", titleRect.x + titleRect.w * 0.5f, titleRect.y + titleRect.h * 0.5f + topFontPx * 0.32f, font, topFontPx, textUnfocusedColor, TextAlign::Center);this is in the draw function, and along with the other code produces this menu:







