Comparing Yew, Leptos and Dioxus
This guide answers one task: pick one of the three established Rust interface frameworks for a browser application, using criteria that actually distinguish them rather than benchmark numbers that will not survive contact with your requirements.
Prerequisites
- [ ] Rust with
wasm32-unknown-unknown, plustrunkordioxus-cli. - [ ] A representative slice of your interface to prototype — a form, a list, one real screen.
- [ ] A payload budget, stated as a number.
- [ ] An honest answer about whether you also need native or server rendering.
The reactivity model is the real difference
Yew uses a virtual DOM with component re-rendering, which will be immediately familiar to anyone who has written React. State changes mark a component dirty, its view function re-runs, the result is diffed against the previous tree, and a patch list is applied. The model is well understood and the framework has the longest history of the three.
Leptos is fine-grained and reactive. Signals track their readers, and a write re-runs only the closures that read that signal, each updating a specific DOM node. Component functions run once, at setup — which surprises people arriving from React, and is the source of most early confusion.
Dioxus offers a React-like component model with hooks, and its recent versions use fine-grained reactivity underneath, giving an ergonomic middle ground: the mental model of components and hooks with fewer re-renders than a pure virtual DOM.
Targets beyond the browser
Dioxus is the only one of the three that treats desktop and mobile as first-class targets. The same components render to a native webview, and increasingly to native widgets, from one codebase. If a desktop application is part of the plan — a tool, an editor, an internal application distributed as a binary — that alone can decide the choice.
Leptos and Yew are web frameworks. Leptos has strong server-side rendering with server functions that make the network boundary nearly invisible; Yew has server-side rendering support with a more conventional separation between front end and API.
Payload, measured the same way
Payload differences are real but smaller than the discussion around them suggests, and they are dominated by the framework’s floor rather than by your code. A like-for-like comparison of the same small application, built in release with size-optimised profiles and Brotli compression:
| Framework | Compressed module | Notes |
|---|---|---|
| Leptos (client-side) | 180–260 kB | Smallest of the three in typical builds |
| Dioxus (web) | 220–320 kB | Close behind, depending on features |
| Yew | 260–400 kB | Virtual DOM machinery adds to the floor |
Those are starting points, not verdicts. The floor matters for a small application and matters proportionally less as the application grows, because your code eventually dominates. Measure yours rather than trusting a table, including this one.
A prototype that actually decides it
The comparison that settles the question is one real screen built three times. It sounds expensive and usually takes a day, which is much less than the cost of choosing wrong.
Pick a screen with a form, a list, some conditional rendering and one asynchronous fetch — enough to exercise state, events, keys and loading states. Build it in each framework. Then measure four things and note one.
# for each prototype
trunk build --release && brotli -q 11 -c dist/*_bg.wasm | wc -c # payload
# in the browser: time to interactive, and update latency on a keystroke
# and: how long did it take you to write, and how often did you fight the framework
The last item is not measurable and is the most important. Fine-grained reactivity is elegant and has a learning curve; a virtual DOM is familiar and costs more per update. Which of those matters more to your team is a real input, not a soft one.
Build and iteration times
The number that affects a project most is not in the payload table: it is how long a change takes to appear in the browser. Interface work is iterative by nature, and a fifteen-second rebuild changes how people work in a way a hundred kilobytes never will.
All three sit in a similar range, dominated by Rust compilation rather than by framework differences. A small application rebuilds incrementally in two to six seconds; a large one with many dependencies can reach twenty or thirty. The levers are the same regardless of framework: keep the interface crate small and its dependency tree shallow, split pure logic into separate crates that rarely change, use a faster linker, and build in debug during development.
# .cargo/config.toml — a linker change alone often halves incremental link time
[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "link-arg=-fuse-ld=lld"]
Hot reloading is where the frameworks differ. Dioxus has invested heavily in reloading view changes without a full rebuild, which for markup and styling work is transformative. Leptos and Yew rely more on a rebuild-and-refresh cycle, which trunk automates but does not eliminate.
Factor this into the prototype. Spend an hour making small changes in each and note how it feels, because that hour is representative of every subsequent day and the payload table is not.
Ecosystem and the things you will need
None of the three has an ecosystem approaching a JavaScript framework’s, so the question is relative.
Yew has been around longest and has the most third-party components, examples and answered questions. Leptos has momentum and a coherent full-stack story, with its ecosystem growing quickly around server functions and its router. Dioxus benefits from serving several platforms, which brings contributions from people whose primary target is not the web.
For all three, expect to write more of the surrounding layer yourself than you would in JavaScript: form handling, complex tables, date pickers, rich text. Web components are the usual escape hatch, letting you wrap an existing JavaScript component and use it from any of them.
Expected output
A prototype comparison produces a table you can put in a decision record:
screen: order form with list, filter and async save
payload time to interactive keystroke update day-1 friction
Leptos 198 kB 310 ms 0.08 ms signals model
Dioxus 268 kB 390 ms 0.11 ms low
Yew 341 kB 470 ms 0.31 ms lowest
The update latencies are all far below anything perceptible, which is the useful conclusion: for ordinary interface work none of the three is too slow, so the decision rests on payload, targets and how the code reads.
Gotchas
- Benchmarking a counter. Every framework is fast at a counter. Use a real screen.
- Comparing debug builds. Sizes differ by an order of magnitude from release; the comparison is meaningless.
- Forgetting the size profile. Without
opt-level = "z",ltoandpanic = "abort", every number is inflated and not equally. - Assuming components re-run. In Leptos they do not, and code written as if they do will not react.
- Assuming they do not. In Yew they do, and putting expensive work in a view function costs on every update.
- Choosing on ecosystem alone. All three are small relative to JavaScript; the gap between them matters less than the gap to what you are used to.
Performance note
Across the prototype above, per-update cost differed by about 4× between the fine-grained frameworks and the virtual-DOM one — and all three were under a third of a millisecond, which no user can perceive. The differences that will affect the project are payload, which is a factor of 1.7, and build and iteration time, which is where a large application spends developer hours rather than user milliseconds.
Frequently Asked Questions
Can I switch later? Not cheaply. The view syntax, the state model and the component lifecycle all differ, so a switch is a rewrite of the interface layer. Pure logic in separate crates ports unchanged, which is a good reason to keep it there.
What about Sycamore and the others? Smaller communities and similar models — Sycamore is fine-grained like Leptos. They are worth a look if something specific appeals, with the usual caveat that a smaller project means fewer answered questions when you are stuck.
Should I use any of them instead of React? Only if one of the situations in the topic overview applies: existing Rust logic, a Rust-first team, or heavy computation inside the interface. Otherwise the integration pattern gives most of the benefit for a fraction of the cost.
Related
- Building a UI with Leptos — one of the three in depth.
- Integrating Wasm into a React app — the alternative to choosing any of them.
- Shrinking Rust Wasm with cargo profiles — making the payload comparison fair.
Whichever you choose, record why in a decision document alongside the prototype numbers. A year later, when someone asks whether to switch, the answer depends on whether the reasons still hold — and nobody will remember them otherwise.
← Back to Full-Stack Frameworks with Wasm