Skip to content

skarab42/htms-rs

HTMS 💨 Stream Async HTML, Stay SEO-Friendly

"What if async content didn’t nuke your SEO?"

(HTML streaming: ancient wisdom, modern awakening, progressive enhancement™.)

HTMS is the absurdly simple answer to a dumbly persistent problem: you want async content that shows up progressively, and you want search engines to actually see it.

HTMS does both. It streams HTML fragments as they’re ready, wraps them in tiny self-cleaning WebComponents, and makes sure the whole thing lands in the very first HTTP response. No hydration, no React-like ceremony, no SEO tax. Just async HTML that behaves like… HTML.

HTML5 Streaming License Crates.io

🟨 JavaScript dev? Check out htms-js


⚡ The 30-Second Demo

HTMS is primarily about streaming HTML. The core idea? Bind HTML data-htms attributes to auto-generated async methods that progressively replace content. Here's how it looks:

Write normal HTML with data-htms attributes:

<header>Static content loads instantly</header>
<div data-htms="fn:breaking_news">Loading news...</div>
<div data-htms="fn:user_dashboard">Loading dashboard...</div>
<footer>Static content loads instantly</footer>

Write normal Rust async functions:

use htms::Template;

#[derive(Template)]
#[template = "page.html"]
pub struct HomePage;

// The `HomePageRender` trait is auto generated by the derive macro
impl HomePageRender for HomePage {
    async fn breaking_news_task() -> String {
        // Some API call - takes 2 seconds
        fetch_latest_news().await
    }

    async fn user_dashboard_task() -> String {
        // Heavy computation - takes 5 seconds
        generate_user_analytics().await
    }
}

Stream it all in one request with your favorite backend, here with Axum and our adapter:

use htms::axum::HtmlStream;

async fn home_page_handler() -> Response {
    let stream = HomePage::default().render();

    HtmlStream::from(stream).into_response()
}

Result: Your page appears instantly with static content, then sections fill in progressively as async tasks complete. All content is SEO-visible in the initial HTTP response.


💻 Try the live demo

cargo run -p demo --example dashboard
# Visit http://localhost:3000 and watch the magic

🧠 How It Actually Works

The “magic” isn’t magic at all — it’s just one boring HTTP response, streamed in chunks:

  1. Instant paint: The browser gets real static HTML immediately, no waiting room.
  2. Async chunks: Slow stuff (APIs, DB crunching, AI hallucinations…) arrives later, wrapped in <htms-chunk> tags inside the same stream.
  3. Self-cleaning swap: Each <htms-chunk> runs a microscopic JS snippet that swaps the placeholder with the real thing, then yeets itself out of the DOM.
  4. SEO jackpot: Because it’s all part of the original response body, search engines see the full page — async content included.

No hydration. No virtual DOM. Just HTML showing up as it’s ready, like the web has always pretended to work.

😉 The "Trust Me Bro" Comparison Chart

Feature HTMS 💨 Traditional SPA SSR + Hydration
First Paint 🟢 Instant (real HTML) 🔴 Slow (JS boot) 🟢 Fast
SEO 🟢 Perfect (all in response) 🔴 Needs hacks 🟢 Good but complex
Progressive Load 🟢 Native streaming 🟢 Possible (XHR/fetch) 🔴 Not really
Complexity 🟢 Low (HTML) 🔴 High (framework tax) 🔴 Very high (double work)
JS Required 🟢 Minimal (self-cleaning WC) 🔴 Heavy bundle 🔴 Heavy bundle
Developer Vibes 🟢 “Wait, that’s it?” 🔴 “Where’s my state?” 🔴 “Why am I hydrating HTML?”

⚠️ The Fine Print (AKA Reality Check)

HTMS is experimental AF. 🧪

This project started as "hold up, HTML streaming has been a thing since the late 90s, and nobody talks about it?! Time to experiment" and spiraled into a full-blown rabbit hole of Rust learning and web development philosophy. The starting point of this journey was this article by Chris Coyier.

🎯 What This Really Is:

  • A learning playground where I'm leveling up my Rust skills
  • An experiment in making web development more enjoyable
  • A proof of concept that streaming HTML doesn't have to suck
  • Definitely NOT production ready (yet?)

🎢 What to Expect:

  • Rapid iteration - things might change dramatically between versions or not at all
  • Breaking changes - semantic versioning is more like... semantic suggestions
  • Rough edges - some features are held together with digital duct tape
  • Fun surprises - new features might appear overnight (or disappear)

🤷 The Honest Truth:

I'm having way too much fun building this to promise anything stable. Use it, break it, contribute to it, but maybe don't bet your startup on it just yet.

But hey, if you're as curious about the future of HTML streaming as I am, welcome aboard this beautiful mess! 🚀

💡 Ideas / Maybe Planned

Some features I’m thinking about experimenting with:

  • Lazy-load compiled templates → right now everything is baked into the binary.
  • Some configuration options: e.g. define a root path for templates via an environment variable (currently resolved from CARGO_MANIFEST_DIR).
  • More data-htms modifiers:
    • fn: → bind an async Rust function (already supported)
    • include: → include another file in the template (already supported)
    • wrap: → wrap a template inside another with a <slot/> system
    • stream: → stream a list of HTML fragment into a target container
  • DOM injection helpers: ergonomic APIs/modifiers for where the streamed content lands:
    • replace (default), append, prepend, before, after
    • Optional selector: to target a child node inside the placeholder
  • Cache system for async fragments (avoid recomputing heavy tasks).
  • Error boundaries: gracefully handle and display partial errors in chunks.
  • Integrations: helper adapters for frameworks (Actix, Warp, Poem, etc.) (Axum is already supported).

Nothing guaranteed, just a playground of ideas. Contributions and experiments welcome 🚀

Have an idea or found a gap? Open an issue


Disclaimer: Readme and docs written by human + AI. We're still debating who did the heavy lifting. 🤖✍️

About

HTMS 💨 Stream Async HTML, Stay SEO-Friendly

Topics

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Sponsor this project

  •  

Packages

No packages published

Languages