Streaming Loaders

Ship the HTML shell immediately and stream slow loader results into stream_slot placeholders.

Enable streaming

FlowApp::new()
    .streaming(true)
    .auto_pages("src/pages", PagesRegistry)
    .serve(FlowServeOptions::default())
    .await

Deferred loader

#[derive(Clone, Serialize, Deserialize)]
struct ProductData {
    name: String,
    price: u32,
}

#[load(stream)]
async fn product(req: &FlowRequest) -> ProductData {
    let id = req.param("id").unwrap_or("0");
    // Simulated slow query
    tokio::time::sleep(std::time::Duration::from_millis(800)).await;
    db::product(id).await
}

Page with placeholder

pub fn page(_req: FlowRequest) -> View {
    match use_product_load() {
        LoadValue::Pending => view! {
            <article>
                <h1>"Product"</h1>
                <div class="skeleton">{stream_slot("product")}</div>
            </article>
        },
        LoadValue::Ok(data) => view! {
            <article>
                <h1>{data.name.clone()}</h1>
                <p>"$" {data.price.to_string()}</p>
            </article>
        },
        LoadValue::Err(e) => error_page(&FlowError::Loader(e)),
    }
}

User experience

Users see layout and headings instantly. Product details replace the skeleton when the loader completes — no blank screen while waiting on the database.