Examples
Runnable crates in examples/ — clone and run with cargo run -p PACKAGE. All listen on http://127.0.0.1:3000 by default.
Documentation is served at resuma-docs.fly.dev (source in apps/resuma-docs, not an example crate).
| Example | Command | App type | What it demonstrates |
|---|---|---|---|
| todo | cargo run -p example-todo | ResumaApp | Full showcase: signals, #[server], #[island], js!, theme, backend security (guards, DTOs, service layer). Docs → |
| counter | cargo run -p example-counter | ResumaApp | Minimal resumable counter — smallest interactive app. |
| flow-demo | cargo run -p example-flow-demo | FlowApp | #[load], streaming SSR, #[load(stream)], deferred chunks. Docs → |
| flow-pages | cargo run -p example-flow-pages | FlowApp | File-based routing, layouts, auto_pages, resuma routes --generate. Docs → |
| production template | resuma new app --template production | FlowApp | Security stub, Dockerfile, fly.toml, /ops dashboard. Docs → |
Choose an example
- Learning Resuma? →
counterthentodo - Production backend patterns? →
todo+ Security docs - Multi-page site? →
flow-pagesorresuma new --template flow - Bookings / calendars? →
resuma new --template flow-booking+ Query params - Streaming / loaders? →
flow-demo - Full-stack + SQL? →
resuma new --template flow-fullstack - Production + ops dashboard? →
resuma new --template production+ Resuma OS
Full app from scratch
This is the smoke-test shape we use for a fresh external project. It starts from the public CLI, then exercises routing, NavLink, #[load], #[submit], #[server], #[data], signals, client handlers, redirects, flash messages, and middleware.
cargo install resuma --version 1.0.2 --force
resuma new launchops --template basic
cd launchops
cargo check
cargo run
# open http://127.0.0.1:3000src/main.rs pattern
use resuma::prelude::*;
use std::sync::{Mutex, OnceLock};
#[data]
struct Project {
id: u32,
name: String,
owner: String,
budget: u32,
}
#[data]
struct ProjectForm {
name: String,
owner: String,
budget: String,
}
#[data]
struct PlanInput {
topic: String,
urgency: i32,
}
#[data]
struct PlanOutput {
title: String,
next_step: String,
score: i32,
}
static PROJECTS: OnceLock<Mutex<Vec<Project>>> = OnceLock::new();
fn projects_store() -> &'static Mutex<Vec<Project>> {
PROJECTS.get_or_init(|| {
Mutex::new(vec![Project {
id: 1,
name: "Launch Analytics".into(),
owner: "Ada".into(),
budget: 2600,
}])
})
}
#[load]
async fn projects(_req: &FlowRequest) -> Vec<Project> {
projects_store().lock().expect("project store lock").clone()
}
#[submit]
async fn add_project(form: ProjectForm, _req: &FlowRequest) -> std::result::Result<Redirect, SubmitError> {
if form.name.trim().len() < 3 {
return Err(SubmitError::new("Fix the project").field("name", "Use at least 3 characters"));
}
let budget: u32 = form.budget.trim().parse()
.map_err(|_| SubmitError::new("Fix the project").field("budget", "Budget must be a number"))?;
let mut projects = projects_store().lock().expect("project store lock");
let id = projects.iter().map(|p| p.id).max().unwrap_or(0) + 1;
projects.push(Project {
id,
name: form.name.trim().into(),
owner: form.owner.trim().into(),
budget,
});
Ok(redirect_with_flash("/projects", "Project created"))
}
#[server]
async fn generate_plan(input: PlanInput) -> Result<PlanOutput> {
if input.topic.trim().is_empty() {
return Err(ResumaError::validation("topic is required"));
}
let score = (input.urgency * 17).clamp(10, 100);
Ok(PlanOutput {
title: format!("Plan for {}", input.topic.trim()),
next_step: "Create a focused implementation checklist".into(),
score,
})
}
#[middleware]
async fn request_log(req: FlowRequest) -> resuma::Result<FlowRequest> {
println!("[launchops] {} {}", req.method, req.path);
Ok(req)
}
fn nav() -> View {
view! {
<nav>
<NavLink href="/" activeClass="active">"Dashboard"</NavLink>
<NavLink href="/projects" activeClass="active">"Projects"</NavLink>
<NavLink href="/workbench" activeClass="active">"Workbench"</NavLink>
</nav>
}
}
#[component]
fn ProjectsPage() {
let rows = use_projects_load();
let flash = current_request().and_then(|req| flash_message(&req));
view! {
<main>
{nav()}
<h1>"Projects"</h1>
{flash.map(|msg| view! { <p class="flash">{msg}</p> }).unwrap_or(View::Empty)}
<section>
{rows.iter().map(|project| view! {
<article key={project.id.to_string()}>
<h2>{project.name.clone()}</h2>
<p>{format!("Owner: {} - Budget: ${}", project.owner, project.budget)}</p>
</article>
}).collect::<Vec<_>>()}
</section>
<Form submit={add_project}>
<input name="name" placeholder="Mobile dashboard" />
<input name="owner" placeholder="Ada" />
<input name="budget" placeholder="3200" inputmode="numeric" />
<button type="submit">"Create"</button>
</Form>
</main>
}
}
#[component]
fn WorkbenchPage() {
let urgency = signal(3_i32);
let topic = signal("release readiness".to_string());
let result = signal("No plan generated yet".to_string());
view! {
<main>
{nav()}
<h1>"Workbench"</h1>
<p>"Urgency: " {urgency}</p>
<button type="button" onClick={js! {
state.urgency.set(Math.max(1, state.urgency.value - 1));
}}>"-"</button>
<button type="button" onClick={js! {
state.urgency.set(Math.min(5, state.urgency.value + 1));
}}>"+"</button>
<input value={topic.get()} onInput={js! { state.topic.set(event.target.value); }} />
<button type="button" onClick={js! {
const plan = await __resuma.action("generate_plan", [{
topic: state.topic.value,
urgency: state.urgency.value,
}]);
state.result.set(plan.title + ": " + plan.next_step + " (" + plan.score + ")");
}}>"Ask server"</button>
<p>{result}</p>
</main>
}
}
#[component]
fn DashboardPage() {
view! {
<main>
{nav()}
<h1>"LaunchOps"</h1>
<p>"Flow routes, loaders, submits, server actions, signals, SPA navigation, and SSR."</p>
</main>
}
}
#[tokio::main]
async fn main() -> std::io::Result<()> {
FlowApp::new()
.with_title("LaunchOps")
.component("/", DashboardPage)
.component("/projects", ProjectsPage)
.component("/workbench", WorkbenchPage)
.serve(FlowServeOptions::default())
.await
}CLI templates
resuma new my-app --template basic scaffolds like a minimal counter. --template todo copies the todo example (main + security + todo_store).