Leptos is a Rust framework for building full-stack web applications with fine-grained reactivity — similar to SolidJS but compiled to WebAssembly for near-native performance.
Why Leptos Matters
JavaScript frameworks ship a runtime to the browser. Leptos compiles to WebAssembly, delivering reactive UIs with zero JS runtime overhead. It also supports server-side rendering with hydration.
What you get for free:
- Fine-grained reactivity (only updates what changed — no virtual DOM)
- Full-stack: SSR + client hydration in one Rust codebase
- WebAssembly compilation for near-native browser performance
- Type safety across client and server
- Built-in router, forms, and server functions
- Component size: ~5KB gzipped for a counter app
Quick Start
# Install
cargo install cargo-leptos
# Create project
cargo leptos new --git leptos-rs/start
cd my-project
# Development
cargo leptos watch
# Build for production
cargo leptos build --release
Reactive Signals
use leptos::*;
#[component]
fn Counter() -> impl IntoView {
let (count, set_count) = create_signal(0);
let double = move || count() * 2;
view! {
<button on:click=move |_| set_count.update(|n| *n += 1)>
"Count: " {count}
</button>
<p>"Double: " {double}</p>
}
}
Server Functions: Full-Stack in One File
use leptos::*;
// This runs on the server — automatically creates an API endpoint
#[server(GetUsers, "/api")]
pub async fn get_users() -> Result<Vec<User>, ServerFnError> {
let pool = use_context::<PgPool>().unwrap();
let users = sqlx::query_as!(User, "SELECT * FROM users")
.fetch_all(&pool)
.await?;
Ok(users)
}
// This runs in the browser — calls the server function via fetch
#[component]
fn UserList() -> impl IntoView {
let users = create_resource(|| (), |_| async { get_users().await });
view! {
<Suspense fallback=move || view! { <p>"Loading..."</p> }>
{move || users.get().map(|result| match result {
Ok(users) => view! {
<ul>
{users.into_iter().map(|u| view! {
<li>{u.name}</li>
}).collect_view()}
</ul>
},
Err(e) => view! { <p>{format!("Error: {e}")}</p> }.into_view(),
})}
</Suspense>
}
}
Router
use leptos::*;
use leptos_router::*;
#[component]
fn App() -> impl IntoView {
view! {
<Router>
<main>
<Routes>
<Route path="/" view=HomePage />
<Route path="/users" view=UserList />
<Route path="/users/:id" view=UserProfile />
<Route path="/*" view=NotFound />
</Routes>
</main>
</Router>
}
}
#[component]
fn UserProfile() -> impl IntoView {
let params = use_params_map();
let id = move || params.with(|p| p.get("id").cloned().unwrap_or_default());
view! { <p>"User ID: " {id}</p> }
}
Performance Comparison
| Framework | Bundle Size (gzip) | Lighthouse Score | Reactivity |
|---|---|---|---|
| React | ~45KB | 85-95 | Virtual DOM |
| SolidJS | ~7KB | 95-100 | Fine-grained |
| Leptos | ~5KB | 95-100 | Fine-grained (Wasm) |
| Svelte | ~2KB | 95-100 | Compiled |
Useful Links
Building high-performance web scrapers? Check out my developer tools on Apify for ready-made actors, or email spinov001@gmail.com for custom solutions.
Top comments (0)