Writing a web app in one language removes a class of mismatches: the same types describe the data on the server and in the browser, and the compiler checks both sides. Leptos is a Rust framework that aims at that model. You write components in Rust, the browser part compiles to WebAssembly, and server functions let the client call the server without a hand-written API layer.
The reactive model
Leptos is built on fine-grained reactivity. A signal holds a value, and the parts of the interface that read it update when it changes. There is no virtual DOM diff for the whole component tree. Only the nodes that depend on a changed signal are updated.
use leptos::prelude::*;
#[component]
fn Counter() -> impl IntoView {
let (count, set_count) = signal(0);
view! {
<button on:click=move |_| set_count.update(|n| *n += 1)>
"Clicked " {count} " times"
</button>
}
}
The view! macro looks like HTML, and expressions inside braces are reactive. The model is familiar to developers who have used signals in other frameworks, with Rust's type system around it.
Server functions
A server function is an async Rust function marked with an attribute. Leptos generates the client call and the endpoint on the server. Arguments and return values are serialised for you, using types both sides share.
The benefit is that a form submission or a data fetch needs no separate REST layer and no duplicate type definitions. The cost is that the client and server are tightly coupled, which suits an application you control end to end and less well a public API.
Rendering modes
Leptos supports client-side rendering, server-side rendering, and hydration, where the server sends rendered HTML and the browser attaches interactivity. Choose by what the page needs: content that search engines must read usually wants server rendering, while an internal dashboard may not need it.
Trade-offs
- Compile times. Rust builds, including the WebAssembly target, are slower than a typical JavaScript dev server. Incremental builds help, but the loop is longer.
- Smaller ecosystem. Component libraries and integrations are fewer than in React or Vue, so you may write more of the interface yourself.
- Fast-moving API. Leptos has changed its APIs across releases. Pin the version and read the migration notes before upgrading.
- Bundle size. WebAssembly modules are larger than small JavaScript bundles, so measure the initial load for your audience.
When to choose it
Leptos suits teams that already write Rust on the backend, want shared types across the whole application, and accept a smaller set of ready-made components. It is less suitable for a marketing site that needs many third-party widgets, or for a team with no Rust experience.
Running it with the rest of a system
A Leptos application usually runs as one server process that serves rendered pages and handles server functions. It needs the same operational basics as any service: a health route, a port from the environment, and a bind address that accepts outside traffic. Build a release binary and the static assets in a container image, and keep the process non-root.
TDK CLI scaffolds its frontends with Vite and its Rust backends with axum, so it does not generate a Leptos project. To run a Leptos application inside a TDK stack, containerise it and register it as a bring-your-own resource, which handles routing and the health check.
Top comments (0)