v0.3.3 · Rust · Server functions · Zero hydration

Build instantly-interactive web apps in Rust

Ship HTML. Resume interactivity — never rehydrate.

Build your whole app in Rust — UI, server functions, and forms on web standards. Components run once on the server; signals drive targeted DOM updates without re-rendering the tree. 901 B loader, progressive enhancement, no WASM hydration.

Install: cargo install resuma · one crate for core + Flow + CLI

counter.rs
#[component]
fn Counter() -> View {
    let count = use_signal(0);
    view! {
        <button onClick={
            move |_| count.update(|c| *c += 1)
        }>
            "Count: " {count}
        </button>
    }
}
// Handler lazy-loads from /_resuma/handler/Counter.js

No hydration. rs2js translates the closure; the runtime resumes signal state on first click.

901 Binitial JS (gzip)
4.2 KiBfirst interaction
0 Bstatic pages
1cargo dependency

Try it

Server functions — no page reload

Like Leptos server functions: Rust guaranteed to run on the server, callable from the browser. Errors surface in the UI without refreshing.

Server function demo

Rust runs on the server only. Click reload — no page refresh. Every 3rd call returns an error (check your server logs).

Positioning

Resuma vs resumable peers

Three ways to ship interactive UI after the first paint — Rust-native resumability compared to Qwik and WASM hydration.

Qwik

Resumable JS — tiny preloader, lazy chunks on interaction. Closest mental model to Resuma.

Leptos

Rust SSR + WASM hydration and optional islands.

Resuma

Rust SSR + resumability + lazy JS handlers — no WASM by default.

Measured

Counter page benchmark (gzip)

Same UX across frameworks: SSR heading + one increment button. Gzip transfer sizes from production build artifacts (May 2026).

FrameworkInitial loadFirst interactionStatic page
Resuma901 B4.20 KiB0 B
Leptos79.02 KiB79.02 KiB
Next.js142.43 KiB142.43 KiB
React (Vite)57.99 KiB57.99 KiB
Astro57.76 KiB57.76 KiB
SvelteKit27.71 KiB27.71 KiB
Qwik1.96 KiB22.32 KiB
SolidStart16.75 KiB16.75 KiB
templ + HTMX16.21 KiB16.21 KiB

Hydration frameworks load the same JS on page load — initial and first click match. Next.js uses the default create-next-app scaffold (~142 KiB). Resuma static pages ship 0 B client JS. Methodology & external validation · node benchmark/run.mjs

Performance model

Interactive from the first click

Resumability means the client never re-runs your component tree. State and handlers are already in the HTML — the tiny runtime wires them up lazily.

🦀

Full Rust stack

#[server] RPC, #[submit] forms, and #[load] data — axum-native, no adapter boilerplate.

📋

Progressive enhancement

<Form submit> works as plain HTML POST before JS loads; runtime enhances in place.

🎯

Targeted updates

Signals update bound DOM nodes only — no full component re-render on the client.

🧩

Resumable by default

Every #[component] is a lazy boundary. Handlers externalise to /_resuma/handler/{Component}.js.

Under the hood

How does it work?

One SSR pass. One resumability payload. Lazy execution on the client.

1

SSR renders once

Rust walks the View tree, emits HTML + data-r-on:* attributes, and serialises signals into <script type="resuma/state">.

2

Payload travels light

Handler sources move to lazy chunks. computed! / effect! / debounce! replay on the client via rs2js.

3

Browser resumes

Loader (901 B gzip) bootstraps signals. Core loads on first interaction. Handlers fetch on demand — or prefetch in viewport.

Components

Write UI once — on the server

Use view! with JSX-like syntax, fine-grained signals, and onClick handlers that compile to lazy JavaScript. No WASM bundle. No client-side component re-execution.

  • use_signal for reactive state
  • computed! / effect! for client replay
  • #[component] props builder generated for you
Component guide →
#[component]
fn SearchBar() -> View {
    let q = use_signal(String::new());
    let len = computed!([q], move || q.get().len());

    view! {
        <input
            value={q}
            onInput={move |e| q.set(e.value)}
            placeholder="Filter…"
        />
        <p>{format!("{} chars", len.get())}</p>
    }
}

Server actions

Call Rust from the browser

#[server] registers JSON-RPC at /_resuma/action/:name. Invoke from translated handlers or js!{} — CSRF-protected, typed, no manual API wiring.

  • Async Rust functions as RPC endpoints
  • Forms via #[submit] and progressive enhancement
  • Security defaults: CSRF, headers, rate limits
Server actions →
#[server]
async fn search(q: String) -> Vec<String> {
    db::search(&q).await
}

#[component]
fn LiveSearch() -> View {
    let query = use_signal(String::new());
    view! {
        <input onInput={ js! {
            state.query.set(event.target.value);
            const r = await __resuma.action(
                'search', [event.target.value]
            );
            state.results.set(r);
        }} />
    }
}

Why Resuma?

Everything you need for modern SSR

Resumable SSR in Rust — one install, progressive enhancement, full-stack Flow when you need it.

🌊

Resuma Flow

File-based pages, #[load], #[submit], layouts, middleware — built into the same crate.

📄

Static export

resuma build --static scaffolds HTML from src/pages/ for edge-friendly deploys.

🔧

Dev experience

resuma dev with HMR WebSocket, resuma new templates (basic, todo, flow).

🔗

JS bridge

view! translates Rust closures via rs2js. js!{} for escape hatches when you need raw client code.

🏝️

Islands (optional)

#[island(load = "visible")] for heavy widgets — most UI only needs #[component].

🛡️

Security built in

Crypto CSRF, security headers, rate limits — see examples/todo for production patterns.

One package

Resuma¹ + Flow²

Two layers, one dependency. Core stays stable; Flow adds routing, data loading, and forms.

RESUMA¹ — CORE

Components & resumability

  • view!, #[component], use_signal
  • computed! / effect! / debounce!
  • #[server], ResumaApp, ~3KB runtime
+

FLOW² — FULL-STACK

Pages, loads & submits

  • FlowApp, src/pages/, #[layout]
  • #[load], #[submit], #[middleware]
  • Streaming SSR, cache headers

Integrations

Database, auth, and tooling

Integration guides for SQLx, Turso, auth, validation, i18n, and E2E testing.

All integrations · Search docs

Start building in 60 seconds

Install the CLI, scaffold a project, and serve instantly-interactive Rust UI — no Node.js required for app development.

Read the tutorial
cargo install resuma && resuma new my-app --template todo