Declarative Partial Updates
Description
This is not SSE and not a new protocol. It’s a plain text/html
streaming response, with two pieces the browser parser knows how to interpret
incrementally:
<?marker name="x">processing instructions in the shell mark insertion points.<template for="x">…</template>blocks emitted later in the same response carry the rendered fragment; the parser swaps each template’s contents into the matching marker as it appears.
The wire is just HTTP/1.1 chunked transfer (or HTTP/2 DATA frames):
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
-- chunk 1: the shell with <?marker …> placeholders, flushed immediately
-- chunk 2: <template for="ping">…</template> (200ms later)
-- chunk 3: <template for="herd">…</template> (900ms later)
-- chunk 4: <template for="recs">…</template> (1500ms later)
Native support shipped in Chrome 148+ behind
chrome://flags/#enable-experimental-web-platform-features. Other browsers
need the template-for-polyfill (the example loads the latest
from unpkg.com/template-for-polyfill).
Polyfill caveats (native Chrome 148+ has none). The polyfill defers a swap while either the
<template>, its<?start>, or its<?end>has nonextElementSibling, soPartialUpdatesappends\n<wbr>after each fragment chunk — the trailing<wbr>gives every template an element sibling at its own arrival, mirroring the spirit of Google’s photo-album demo. Markers also support the range form<?start name="x">…<?end>(start/end): the skeleton between is replaced wholesale on swap, so no CSS gymnastics to hide loading chrome — drop a<wbr>(or any element) right after<?end>though, so it isn’t last-sibling either. See Google’s explainer for the spec.
Rama Support
📚 Rust Docs: https://ramaproxy.org/docs/rama/http/service/web/response/struct.PartialUpdates.html
Enable the http feature and the html feature (or even better http-full to cover it all),
then use PartialUpdates together with the marker() helper from rama::http::html.
Example
http_declarative_partial_updates.rs
serves a llama-themed dashboard with three slow panels, fragments
streaming in completion order, and the unpkg-hosted polyfill in the
shell <head> so the swap works on browsers without native support.
Pass ?polyfill=false to skip the polyfill (useful for testing native
Chrome 148+ behind the experimental flag, or measuring the baseline
shell).
Relation to SSE
Server-Sent Events push typed events on text/event-stream
and are consumed by JS via EventSource. Declarative partial updates push
HTML fragments on text/html and are consumed by the browser parser
directly — no JS needed (on Chrome 148+). Use SSE for live data streams
that the page renders; use partial updates for the first page render itself
when some panels are slower than others.