DEV Community

Alex Spinov
Alex Spinov

Posted on

Leptos Has a Free API: Build Full-Stack Web Apps in Rust With Fine-Grained Reactivity

Leptos is a Rust web framework with fine-grained reactivity (like SolidJS) and built-in SSR. Write your frontend AND backend in Rust, compile to WebAssembly for the client, and native binary for the server.

Why Leptos?

  • Fine-grained reactivity — only updates what changed (like Solid, not React)
  • Full-stack Rust — one language, one codebase, frontend + backend
  • Blazing fast — Rust → WASM, smallest bundle sizes
  • Server functions — call backend code like regular functions
  • SSR + hydration — SEO-ready out of the box

Quick Start

# Install cargo-leptos
cargo install cargo-leptos

# Create new project
cargo leptos new my-app
cd my-app

# Dev server with hot reload
cargo leptos watch
# App at http://localhost:3000
Enter fullscreen mode Exit fullscreen mode

Components (Fine-Grained Reactivity)

use leptos::*;

#[component]
fn Counter() -> impl IntoView {
    let (count, set_count) = signal(0);

    view! {
        <div>
            <h1>"Counter: " {count}</h1>
            <button on:click=move |_| set_count.update(|n| *n += 1)>
                "Click me"
            </button>
        </div>
    }
}

#[component]
fn App() -> impl IntoView {
    view! {
        <main>
            <h1>"My Leptos App"</h1>
            <Counter />
        </main>
    }
}
Enter fullscreen mode Exit fullscreen mode

Server Functions (Full-Stack Magic)

// This runs on the SERVER but is callable from the CLIENT
#[server(GetUsers)]
pub async fn get_users() -> Result<Vec<User>, ServerFnError> {
    // Server-side code: database queries, file system, etc.
    let users = sqlx::query_as::<_, User>("SELECT * FROM users")
        .fetch_all(&pool())
        .await?;
    Ok(users)
}

// This component calls the server function
#[component]
fn UserList() -> impl IntoView {
    let users = Resource::new(|| (), |_| get_users());

    view! {
        <Suspense fallback=|| view! { <p>"Loading..."</p> }>
            {move || users.get().map(|result| match result {
                Ok(users) => users.iter().map(|user| {
                    view! { <p>{&user.name}</p> }
                }).collect_view(),
                Err(e) => view! { <p>"Error: " {e.to_string()}</p> }.into_any(),
            })}
        </Suspense>
    }
}
Enter fullscreen mode Exit fullscreen mode

Routing

use leptos_router::*;

#[component]
fn App() -> impl IntoView {
    view! {
        <Router>
            <nav>
                <a href="/">"Home"</a>
                <a href="/about">"About"</a>
                <a href="/users">"Users"</a>
            </nav>
            <main>
                <Routes>
                    <Route path="/" view=HomePage />
                    <Route path="/about" view=AboutPage />
                    <Route path="/users" view=UserList />
                    <Route path="/users/:id" view=UserDetail />
                </Routes>
            </main>
        </Router>
    }
}

#[component]
fn UserDetail() -> impl IntoView {
    let params = use_params_map();
    let id = move || params.get().get("id").unwrap_or_default();

    view! { <h1>"User: " {id}</h1> }
}
Enter fullscreen mode Exit fullscreen mode

Performance Comparison

Framework Bundle Size TTI
Leptos ~30 KB ~0.5s
SolidJS ~7 KB ~0.6s
React ~130 KB ~1.2s
Next.js ~200 KB ~1.5s

Resources


Building high-performance web apps? I create custom tools and automation. Check my Apify actors or email spinov001@gmail.com.

Top comments (0)