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, plus trunk or dioxus-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.

What happens when one value changes A virtual DOM framework re-runs the component and diffs its output. A fine-grained framework re-runs only the closures reading that value. A hybrid keeps the component model while tracking dependencies underneath. Yew component re-runs tree diffed, patch applied familiar to React developers most mature ecosystem Leptos signals re-run readers one node updated components run once fewest crossings Dioxus components and hooks fine-grained underneath web, desktop, mobile targets broadest reach All three produce the same DOM. The model decides how much work an update costs and how the code reads, which is what you live with daily.

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.

What should decide it Desktop or mobile targets point to Dioxus, a full-stack Rust application with server functions points to Leptos, and a team wanting a familiar React-like model with the most mature ecosystem points to Yew. desktop or mobile too one codebase, several targets → Dioxus full-stack Rust server functions, smallest payload → Leptos familiarity and examples React-shaped, longest history → Yew If none of the three rows describes your situation, the honest answer is that any of them will do and the choice should go to whichever your team prefers after the prototype.

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.

What actually differs All three compile Rust to a browser interface. The differences that matter are the update model, the payload, and where else the same code can run. Yew virtual DOM and a component model familiar from React Leptos fine-grained signals; the smallest payloads of the three Dioxus one component model targeting web, desktop and mobile all three need a Rust toolchain and ship a Wasm payload Pick on the update model and where you need to run, not on a benchmark chart; all three are fast enough. Every one of them still pays the payload cost, which is the decision a JavaScript framework does not force.

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", lto and panic = "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.

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