DEV Community

Alex Spinov
Alex Spinov

Posted on

Leptos Has a Free API — Full-Stack Rust with Fine-Grained Reactivity

Leptos is a full-stack Rust framework with fine-grained reactivity, SSR, and hydration. Write your frontend AND backend in Rust — with a React-like component model that's faster than virtual DOM.

Why Leptos?

  • Fine-grained reactivity — no virtual DOM diffing, signals update exactly what changed
  • Full-stack — SSR, hydration, server functions — all in Rust
  • WASM frontend — compiles to WebAssembly for near-native speed
  • Type-safe — share types between server and client

Quick Start

cargo install cargo-leptos
cargo leptos new --git leptos-rs/start
cd myapp
cargo leptos watch
Enter fullscreen mode Exit fullscreen mode

Components and Signals

use leptos::*;

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

    view! {
        <div>
            <p>"Count: " {count}</p>
            <button on:click=move |_| set_count.update(|n| *n += 1)>
                "Increment"
            </button>
        </div>
    }
}
Enter fullscreen mode Exit fullscreen mode

Derived Signals

#[component]
fn App() -> impl IntoView {
    let (count, set_count) = create_signal(0);
    let doubled = move || count() * 2;
    let is_even = move || count() % 2 == 0;

    view! {
        <p>"Count: " {count}</p>
        <p>"Doubled: " {doubled}</p>
        <p>{move || if is_even() { "Even" } else { "Odd" }}</p>
    }
}
Enter fullscreen mode Exit fullscreen mode

Server Functions

#[server(GetUsers, "/api")]
pub async fn get_users() -> Result<Vec<User>, ServerFnError> {
    let users = db::get_all_users().await?;
    Ok(users)
}

#[component]
fn UserList() -> impl IntoView {
    let users = create_resource(|| (), |_| get_users());

    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>
                }.into_view(),
                Err(e) => view! { <p>{e.to_string()}</p> }.into_view(),
            })}
        </Suspense>
    }
}
Enter fullscreen mode Exit fullscreen mode

Forms and Actions

#[server(AddTodo, "/api")]
pub async fn add_todo(title: String) -> Result<(), ServerFnError> {
    db::insert_todo(&title).await?;
    Ok(())
}

#[component]
fn TodoForm() -> impl IntoView {
    let add = create_server_action::<AddTodo>();

    view! {
        <ActionForm action=add>
            <input type="text" name="title" />
            <button type="submit">"Add Todo"</button>
        </ActionForm>
    }
}
Enter fullscreen mode Exit fullscreen mode

Routing

use leptos_router::*;

#[component]
fn App() -> impl IntoView {
    view! {
        <Router>
            <nav>
                <A href="/">"Home"</A>
                <A href="/users">"Users"</A>
            </nav>
            <Routes>
                <Route path="/" view=Home />
                <Route path="/users" view=UserList />
                <Route path="/users/:id" view=UserDetail />
            </Routes>
        </Router>
    }
}
Enter fullscreen mode Exit fullscreen mode

Building data-intensive Rust apps? Check out my Apify actors for web scraping, or email spinov001@gmail.com for custom Rust development.

Leptos, Yew, or Dioxus — which Rust frontend framework do you prefer? Comment below!

Top comments (0)