Frontend Frameworks (Leptos / Yew)
Build web UIs entirely in Rust with Leptos or Yew on top of WASM.
Recipe
use leptos::*;
#[component]
fn App() -> impl IntoView {
let (count, set_count) = create_signal(0);
view! {
<button on:click=move |_| set_count.update(|n| *n + 1)>
"Clicked " {count} " times"
</button>
}
}When to reach for this: Teams that want Rust end-to-end for web apps with component-based UI and fine-grained reactivity.
Working Example (Leptos)
use leptos::*;
#[component]
fn Greeting(name: String) -> impl IntoView {
view! { <p>"Hello, " {name} "!"</p> }
}
#[component]
fn App() -> impl IntoView {
let (name, set_name) = create_signal(String::from("Rust"));
view! {
<input
prop:value=move || name.get()
on:input=move |ev| set_name.set(event_target_value(&ev))
/>
<Greeting name=name.get()/>
}
}
pub fn main() {
mount_to_body(|| view! { <App/> })
}What this demonstrates:
- Components are Rust functions with
#[component] - Signals drive reactive updates
view!macro compiles to DOM operations- Leptos can hydrate SSR pages or run client-only
Deep Dive
Leptos vs Yew
| Framework | Model | Strength |
|---|---|---|
| Leptos | Fine-grained signals | Performance, SSR/hydration |
| Yew | Component virtual DOM | Mature ecosystem, HTML-like macros |
Yew Snapshot
use yew::prelude::*;
#[function_component]
fn Counter() -> Html {
let counter = use_state(|| 0);
html! {
<button onclick={Callback::from(move |_| counter.set(*counter + 1))}>
{ *counter }
</button>
}
}Project Structure
my-app/
Cargo.toml # leptos or yew deps
src/lib.rs # components
index.html # mount point
Trunk.toml # Trunk bundler config (common)
Gotchas
- Bundle size - Framework + WASM adds up. Fix: Release builds,
wasm-opt, lazy routes. - Ecosystem gaps - Not every JS widget has a Rust binding. Fix: Bridge via
wasm-bindgenfor specific libraries. - SEO without SSR - Client-only WASM hurts SEO. Fix: Leptos SSR or hybrid rendering.
- Router integration - Client routing needs history API setup. Fix: Use
leptos_routeroryew-router. - Build tool churn - Trunk, wasm-pack, and cargo-leptos differ. Fix: Follow framework book for one blessed path.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| React + WASM module | UI in JS, compute in Rust | Full Rust UI team |
| Svelte/Solid + WASM | Smaller JS UI bundles | Rust-only shop |
| web-sys direct | Tiny DOM utilities | Full application UI |
FAQs
Leptos CSR vs SSR?
CSR mounts to empty HTML. SSR renders on server then hydrates in browser for faster first paint.
Styling?
Tailwind via trunk, vanilla CSS, or stylers crate. No single standard yet.
State management?
Leptos signals and context. Yew uses hooks and context API similar to React.
Testing components?
Leptos leptos::mount_to_body in tests. Yew TestRenderer for unit tests.
API calls?
reqwest with wasm-bindgen-futures or gloo-net for browser fetch.
Deployment?
Static hosting (Netlify, Cloudflare Pages) for CSR. SSR needs Rust server (Axum + Leptos).
TypeScript interop?
Expose WASM compute crate to TS frontend, or go full Rust UI with frameworks here.
Accessibility?
Use semantic HTML in view!/html!. Test with axe and keyboard navigation.
Hot reload?
trunk serve and cargo leptos watch provide dev reload workflows.
Which framework in 2026?
Leptos for new full-stack Rust web apps. Yew if your team already invested in it.
Related
- wasm-bindgen - underlying bridge
- DOM & Web APIs (web-sys) - raw browser APIs
- wasm-pack & Bundling - build pipeline
- Performance & Size - bundle optimization
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.