The Many Frontends of Rust
Dioxus positions itself as a Rust framework for building user interfaces across mobile, desktop, and web from a single codebase. Its "fullstack" mode extends that ambition to the server, promising one codebase for client and server alike. But what does that actually imply for developers accustomed to Rust's compile-time guarantees?
Dioxus is, as of summer 2023, a YCombinator-backed startup. The framework's name, we're told, is "legally not inspired by any Pokémon," though the author does concede in a Hacker News comment that Deoxys is "awesome." It also receives funding from Huawei, which by the author's accounting makes it a legitimate Rust project.
The pitch evokes earlier cross-platform efforts like React Native or PhoneGap, which sought to unify app development across platforms. Dioxus "fullstack" goes further, merging client and server development into a single codebase. To understand what that entails, it helps to look at how web apps have evolved.
A Brief, Somewhat Imprecise History
In the first generation of web apps, the server was entirely responsible for generating HTML, with JavaScript (or worse) providing only minor embellishments. The second generation saw JavaScript reach critical mass, enabling full applications to live inside the browser. Developers used XMLHttpRequest to shuttle JSON around, assembling pages from structured data fetched from APIs. This produced single-page apps that could work offline but suffered from poor initial loading experiences. Visitors waited for the entire app to download, then for API calls to complete, then for rendering — a slow process that bred a collective distaste for the spinner.
SPAs introduced other problems: accessibility, navigation history, search engine optimization, and data loading. When every component makes its own API requests, pages can generate a cascade of network traffic. In an extreme case, a misbehaving React component making API calls in an infinite loop was part of what took Cloudflare down in a 2023 outage.
The third generation, full-stack frameworks, aimed for the best of both worlds. The server renders HTML and streams it to the client, which can display it immediately. But the server also sends the structured data used to produce that HTML, allowing the client to take over seamlessly.
A Concrete Example
Consider a counter written in Dioxus. Server-side rendering can handle a variable x starting at zero, referenced in the RSX macro, along with two buttons. But event handlers must be registered on the client — the server can only send hints. The HTML markup the server sends contains no onclick attributes on the buttons, only references to the structured data. The client, having the same data, performs the same render, creates a mapping between what the server sent and what it rendered, and then hydrates the document by installing event handlers.
This streaming approach lets the server show markup early, before the client app has loaded. But clicking a button mid-hydration is a problem. In theory, server markup could include links or forms that trigger standard browser actions, but few bother with that anymore. If the client's render doesn't match the server's during hydration, the mapping fails and the app breaks — the best possible recovery is replacing everything with the client-rendered version, which causes jarring page jumps.
The Hook Problem
Fetching data that takes time — from a database or API — is a family of problems the industry has wrestled with for years. Dioxus addresses this with a broad collection of hooks:
try_use_contextuse_after_suspense_resolveduse_callbackuse_contextuse_context_provideruse_coroutineuse_coroutine_handleuse_effectuse_futureuse_hookuse_hook_did_runuse_hook_with_cleanupuse_routeuse_routeruse_navigatoruse_memouse_on_unmountuse_reactiveuse_resourceuse_root_contextuse_set_compareuse_set_compare_equaluse_signaluse_signal_syncuse_reactive!use_server_futureuse_server_cacheduse_dropuse_before_renderuse_after_render
It's a list that offers something for everyone — synchronous and asynchronous hooks, reactive hooks, caching hooks, hooks that run only on the server or only on the client. It's also somewhat intimidating. This is a long way from the "if it compiles it works" comfort that Rust developers are used to. Breaking the rules of hooks doesn't produce a build error or even a runtime error — just weird behavior that can be hard to debug.
There's a reason for that, though. Full-stack development is genuinely complicated.
Revisiting first impressions
My initial plan was to be hard on Dioxus. I’d challenged myself to rebuild the quizzing software I used at Eurorust in Paris with it, and that experience was, honestly, pretty frustrating. But digging deeper, most of my complaints turned out to be misunderstandings, features already landed on the main branch, or limits inherent to the current Rust/WebAssembly ecosystem.
I had intended to start by praising the developer experience: the dx tool wraps Cargo and handles WebAssembly compilation, and you get a loading screen in the browser while everything builds.
Then I was going to complain that a panic in the app leaves it completely unresponsive, with no indication anything went wrong. On the main branch, that indicator exists. Of course it does — the team uses their own software and added it.
The next target was stack traces. In WebAssembly, they show mangled function names with numeric offsets into a large WASM file, which is near useless.
Then I found the Chrome extension C/C++ DevTools Support (DWARF). It looks exactly like what someone designing malware specifically for me would ship, and yet it works. It doesn’t restore real function names, but it does show source files and line numbers. Clicking them opens the source in DevTools with functioning breakpoints, stepping over and into calls — a real debugging experience.
It’s far better than I expected. I didn’t realize WASM debugging had come this far, or that DWARF had been adopted for it — though it makes sense in retrospect.
Hot patching, actually
Next on my list was Subsecond, Dioxus’s hot patching. In modern web development, editing a component’s source and saving the file should update that component in the browser without losing application state. If you’ve navigated deep into the UI, the page doesn’t reload — only the changed components update in place.
I thought I was testing this with Dioxus and found it barely worked, losing state and taking longer than a second. It turned out I hadn’t enabled it. You must pass --hot-patch, and I hadn’t known that.
Once enabled, it worked shockingly well. It still crashes regularly — what it does under the hood is far more complicated than what a JavaScript framework does, and it’s early days — but the promise is visible. Edit a file, and the result shows up in the browser quickly.
There’s a trade-off: enabling hot patching shows the actual mangled Rust function names in stack traces, but it breaks DWARF debugging. Your choice.
The verdict
So, does Dioxus spark joy? Not yet. Without Subsecond and the rest of the tooling, day-to-day development is still unpleasant compared to Svelte 5, which remains my gold standard. But the direction the Dioxus team is heading is clear, and I’m excited about it.
I went in skeptical, expecting great Rust enums and everything else to be miserable. I was wrong. Generational references make event handlers tolerable to write. Server-side functions over web sockets work well and remove substantial boilerplate.
The team is doing genuinely hard, interesting work. They maintain a Flexbox implementation shared with Servo and are building their own HTML and CSS renderer to support desktop apps without embedding a full browser engine.
I’m looking forward to Dioxus and the broader WASM-on-the-frontend ecosystem catching up with JavaScript-based solutions in developer ergonomics. In the meantime, my stack stays Rust on the backend, TypeScript on the frontend.
Afterword
Since giving this talk, I’ve written several thousand lines of Dioxus for an upcoming project, which makes that conclusion a lie. I remain conflicted, but clearly I’m invested now. Let’s see where this goes.



