Streaming HTML Out of Order Without JavaScript (2026)
Streaming HTML out of order without JavaScript: how Declarative Partial Updates and Declarative Shadow DOM reorder content natively in Chrome 148.

For thirty years, HTML has streamed one way: top to bottom, in the exact order the server wrote it. If the slow database query lived in your page header, everything below it waited. That constraint is finally breaking — and the fix needs zero JavaScript.
Streaming HTML out of order flips that: you send the fast parts of a page immediately, leave labeled holes where the slow parts go, and fill those holes later in the same response — in whatever order the data actually arrives. No client framework. No hydration. No fetch() on the client. The browser’s own HTML parser does the reordering.
This started as a clever hack built on Declarative Shadow DOM. In 2026 it became a real standards-track feature — Declarative Partial Updates — shipping behind a flag in Chrome 148. Here’s how it works, where it came from, and whether you should care yet.
TL;DR
- Streaming HTML out of order means sending page sections in the order they become ready on the server, not the order they appear in the document — so a slow widget never blocks the fast content around it.
- It’s been possible without JavaScript since 2024 using Declarative Shadow DOM (
<template shadowrootmode>) plus named<slot>elements. You stream a shell with placeholder slots, then stream the real content later and the browser slots it into place. - The 2026 evolution is Declarative Partial Updates (DPU) — a WICG proposal from Chrome’s Barry Pollard and Noam Rosenthal — that removes the Shadow DOM requirement. You drop
<?marker>/<?start>…<?end>processing instructions where content goes, then stream<template for="…">elements to fill them. - The declarative half is tracked in a WHATWG HTML pull request (
<template for>) authored by Philip Jägenstedt. There’s also a JS half —setHTML,streamHTML,appendHTML,streamAppendHTML— for updates after initial load. - Status (mid-2026): DPU is behind
chrome://flags/#enable-experimental-web-platform-featuresin Chrome 148. Firefox and WebKit are interested but haven’t shipped. It is not production-ready — but Declarative Shadow DOM streaming is, today, cross-browser.
What “streaming HTML out of order” actually means
Streaming HTML out of order is the technique of sending an HTML document in chunks where later chunks patch content into positions defined earlier in the stream, so the browser can render each section as soon as its data is ready rather than waiting for document order.
Compare the two models directly.
In-order streaming (the last 30 years): The server writes the response top to bottom. If your page is a header, a personalized recommendations widget backed by a 900ms query, and a footer, the bytes leave the server in that order. The user stares at a header while the query runs, because the footer literally cannot be sent before the widget above it.
Out-of-order streaming: The server sends the header, then a placeholder for the widget (“Loading…”), then immediately the footer — the whole shell arrives in ~50ms. When the 900ms query finishes, the server sends the widget’s real HTML as a trailing chunk, tagged to fill the placeholder. The browser swaps it in. The user saw a complete, laid-out page almost instantly, and the slow part filled in without blocking anything.
💡 Key insight: Out-of-order streaming decouples the order you author HTML from the order you can afford to compute it. Slow data stops being a layout-blocking tax on everything below it.
If that sounds like React Suspense or streaming SSR — it is the same idea. The difference is the machinery. React ships a runtime that receives the out-of-order chunks and repositions DOM nodes with JavaScript. The techniques in this post push that job down into the browser itself, so the reordering happens in the HTML parser with no script at all.
Why this matters right now
Perceived performance is the whole game. Largest Contentful Paint, Time to First Byte, and how “alive” a page feels are dominated by how fast something useful appears — not by when the slowest widget resolves. (If you’re optimizing these numbers, my Core Web Vitals optimization guide covers the metrics this technique moves.)
The old workaround was to make everything fast enough to send in order, or to punt the slow parts to client-side fetch() after load — which trades a blocked render for a JavaScript bill, a loading spinner, and a layout shift. Out-of-order streaming gives you the best of both: one HTTP response, first paint at the speed of your fastest content, and no client runtime.
This lands at a moment when the whole industry is rediscovering server-driven HTML — HTMX, Astro, Qwik, React Server Components, and the Cloudflare viNext rebuild of Next.js all lean on streaming. A native, framework-free primitive for out-of-order delivery is a big deal: it’s the plumbing every one of those tools has been faking in userland.
THE NUMBERS THAT MOTIVATE IT
1 request
Full page + slow widgets in a single HTTP response
No follow-up client fetch
0 KB JS
JavaScript required for the reordering
Parser-native
Chrome 148
First browser to expose Declarative Partial Updates
Behind experimental flag
The no-JavaScript technique that started it — Declarative Shadow DOM and slots
Before any new proposal existed, developers found a way to stream out of order with zero JavaScript using two features that already shipped: Declarative Shadow DOM (DSD) and <slot>.
Here’s the mechanism. A <slot> inside a shadow root is a placeholder. The browser fills it with any light-DOM child of the host element carrying a matching slot="…" attribute — and crucially, it does this whenever that child appears in the stream, even if that’s much later than the slot itself.
So you stream a shell first:
<article>
<template shadowrootmode="open">
<h1>My page</h1>
<slot name="reviews">
<p>Loading reviews…</p> <!-- fallback shown immediately -->
</slot>
<footer>Site footer</footer>
</template>
</article> The browser paints the heading, the “Loading reviews…” fallback, and the footer right away. Then — later in the same response, after your slow query resolves — you stream the real content as a light-DOM child targeting that slot:
<article>
<!-- ...the template above already streamed... -->
<div slot="reviews">
<p>★★★★★ Actually great.</p>
<p>★★★★☆ Pretty good.</p>
</div>
</article> The moment that <div slot="reviews"> is parsed, the browser removes the fallback and slots the reviews into place. No script ran. No event fired. The HTML parser did it. That’s out-of-order streaming, and it’s been Baseline across Chrome, Firefox, and Safari since early 2024.
The catch: this only works inside Shadow DOM. Every out-of-order region needs a custom-element host and a shadow root, you inherit shadow-DOM style scoping whether you want it or not, and it’s awkward for the common case of “just patch this one <div> in my normal document.” That limitation is exactly what the next proposal set out to remove.
Declarative Partial Updates — the native proposal
Declarative Partial Updates (DPU) is the web-platform proposal that turns out-of-order streaming into a first-class HTML feature with no Shadow DOM requirement. It was introduced in a Chrome for Developers article on May 19, 2026 by Barry Pollard and Noam Rosenthal, with a full WICG explainer and a WHATWG HTML pull request (#11818) for the <template for> element authored by Philip Jägenstedt.
The explainer states the problem plainly:
“Streaming of HTML existed from the early days of the web… However, it always had the following major constraints: 1. HTML content is streamed in DOM order. 2. After the initial parsing of the document, streaming is no longer enabled.”
DPU attacks both constraints. Two mechanisms, two audiences.
Mechanism 1 — declarative out-of-order streaming (no JavaScript)
You mark a spot in the document with a processing instruction, then fill it later with a <template for> whose for attribute matches the marker’s name.
Single-point insertion — drop content at a marker:
<div>
<?marker name="placeholder">
</div>
<!-- ...anything else streams here... -->
<template for="placeholder">
Here is some <em>HTML content</em>!
</template> Temporary placeholder — show fallback content until the real thing arrives, using a <?start> / <?end> pair to define the range to replace:
<div>
<?start name="reviews">
Loading…
<?end>
</div>
<!-- ...footer, other sections, all stream immediately... -->
<template for="reviews">
Here is some <em>HTML content</em>!
</template> When the <template for="reviews"> streams in, the browser replaces everything between <?start name="reviews"> and <?end> — the “Loading…” text — with the template’s content. Notice there’s no custom element and no shadow root. This patches a plain <div> in the normal document.
Multiple / chunked updates — a template can contain its own nested marker, so you can stream a list one item at a time:
<ul id="results">
<?start name="results">
Loading…
<?end>
</ul>
<template for="results">
<li>Result One</li>
<?marker name="results">
</template>
<template for="results">
<li>Result Two</li>
<?marker name="results">
</template> Each <template for="results"> appends its <li> and leaves a fresh <?marker name="results"> for the next chunk. This is how you stream search results or a chat transcript row-by-row, declaratively.
Mechanism 2 — streaming updates after load (this half needs JavaScript)
The second half of DPU modernizes DOM insertion for content that arrives after the initial page — think live dashboards or infinite scroll. It replaces the messy innerHTML / insertAdjacentHTML landscape with a consistent, sanitizer-aware, Streams-integrated set of methods:
const el = document.querySelector('#content-to-update');
// Static: parse a string of HTML into the element
el.setHTML(html, options);
el.appendHTML(html, options);
// Streaming: pipe an HTTP response straight into the DOM
const response = await fetch('/api/content.html');
response.body
.pipeThrough(new TextDecoderStream())
.pipeTo(el.streamHTMLUnsafe()); There are Unsafe variants (streamHTMLUnsafe, streamAppendHTMLUnsafe) that skip sanitization, mirroring the setHTMLUnsafe naming already standardized with the HTML Sanitizer API. This half obviously uses JavaScript — but the first half, the out-of-order document streaming, does not.
Old slot technique vs Declarative Partial Updates
Both give you no-JavaScript out-of-order streaming. Here’s how to choose.
| Concern | Declarative Shadow DOM + slots | Declarative Partial Updates |
|---|---|---|
| Ships in browsers today | Yes — Baseline since early 2024 | Chrome 148 behind a flag only |
| Requires Shadow DOM | Yes — a host element + shadow root per region | No — patches the normal document |
| Style scoping | Forced shadow-scoping (may fight your global CSS) | Normal light-DOM cascade |
| Replace an arbitrary range of nodes | Awkward — slot fills, not range-replace | Native via <?start>…<?end> |
| Post-load streaming updates | Not really its job | Built in — streamHTML / streamAppendHTML |
| Production-ready in mid-2026 | Yes | No — explicitly for developer testing |
The honest recommendation: if you need out-of-order streaming in production today, use Declarative Shadow DOM with slots. It’s cross-browser, it’s stable, and the Shadow DOM constraints are livable for coarse-grained regions. Reach for Declarative Partial Updates when you’re prototyping the future or building a framework’s rendering layer that you can gate behind feature detection and a polyfill.
How to try Declarative Partial Updates today
-
2021
Declarative Shadow DOM ships
Chrome 90 introduces the shadowroot attribute — but it attaches at the closing tag, so it can't stream.
-
Early 2024
shadowrootmode goes Baseline
Renamed attribute attaches the shadow root at the opening tag. Chrome, Firefox, and Safari all ship it — no-JS out-of-order streaming via slots becomes possible everywhere.
-
May 19, 2026
Declarative Partial Updates announced
Chrome's Barry Pollard and Noam Rosenthal publish the explainer; WHATWG PR #11818 adds <template for>.
-
Mid-2026
Chrome 148 developer testing
DPU available behind chrome://flags/#enable-experimental-web-platform-features. Firefox and WebKit interested; not yet shipped.
-
Future
Cross-browser standardization
Pending WHATWG HTML + DOM spec merges and multi-implementer support before it's safe for production.
To experiment right now:
- Enable the flag. Open
chrome://flags/#enable-experimental-web-platform-featuresin Chrome 148+ and turn it on. - Stream from a real server. The effect only shows if bytes actually arrive over time — flush the shell, do your slow work, then flush the
<template for>. A buffered response that sends everything at once will look correct but won’t demonstrate the streaming win. - Use a polyfill for reach. The proposal ships with polyfills so you can adopt the API shape today and let it fall through to native behavior as browsers catch up.
- Feature-detect, don’t assume. Gate on the presence of the API (e.g. checking for
Element.prototype.streamHTML) before relying on it.
Common mistakes and pitfalls
Most people who try out-of-order streaming trip on the same few things:
- Buffering the response and expecting magic. If your framework or reverse proxy buffers the full HTML before sending, there is no “out of order” — everything arrives at once. You must flush the shell early. Check your server and any nginx/CDN buffering in front of it.
- Assuming Suspense-style streaming is JavaScript-free. React’s out-of-order streaming is not the same class of thing — it ships a client runtime. If your goal is “works with JS disabled,” you need DSD/slots or DPU, not a framework’s streaming SSR.
- Fighting Shadow DOM styles. With the slot technique, your global CSS doesn’t pierce the shadow root. Plan for
::part(), inherited custom properties, or accept scoped styles — don’t discover this after your design looks unstyled. - Forgetting the fallback content. The whole point of
<slot>children or the<?start>…<?end>range is what the user sees before the slow content arrives. Ship a real skeleton or “Loading…” state, not an empty hole that causes layout shift. - Reaching for DPU in production. It’s one browser, behind a flag, with a syntax that may change. Treat it as R&D.
Best practices
- Default to Declarative Shadow DOM + slots for production out-of-order streaming. It’s the only cross-browser, no-JavaScript option that’s actually stable in 2026.
- Flush aggressively. Send the shell, headers, and any above-the-fold static content in the first chunk. The perceived-performance win is entirely about getting something painted fast.
- Order your slow work by user value, not document order. Stream the widget the user actually looks at first, even if it’s lower on the page.
- Keep JavaScript optional, not required. Use the declarative techniques for the initial render; layer the JS
streamHTMLmethods on top only for genuinely post-load updates. - Follow the standard, not one browser. Track the WHATWG PR and WICG explainer for syntax changes, and gate any DPU usage behind feature detection + polyfill.
OUT-OF-ORDER STREAMING READINESS
Track progress as you work through the list
0%
0/6 done
A concrete example: a product page with a slow reviews panel
Picture a product page. The product info comes from cache in 20ms. The reviews come from a service that takes 700ms. Personalized recommendations take 1.2s.
In-order: first byte of anything useful waits behind the slowest thing above it. Best case, you reorder your markup so slow things sit at the bottom — but then your layout is dictated by latency, not design.
Out of order: you flush the full page shell — header, product info, empty reviews slot, empty recommendations slot, footer — in ~30ms. LCP fires on the product image almost immediately. At 700ms the reviews <template for="reviews"> streams in and fills its slot. At 1.2s the recommendations do the same. One request, no client fetch, no spinner-driven layout shift, and the page is fully interactive-looking the entire time.
That’s the shape of nearly every modern page: fast core content plus a few slow, independent widgets. Out-of-order streaming is purpose-built for it — and it pairs naturally with server-first stacks like the ones I covered in the Cloudflare viNext teardown and FastAPI’s native SPA support.
WHERE OUT-OF-ORDER STREAMING PAYS OFF
E-COMMERCE
Slow reviews & recommendations
Ship product info instantly; stream reviews and personalization when their services resolve.
DASHBOARDS
Independent panels
Each tile fills as its query returns, instead of the whole board waiting on the slowest one.
SEARCH & FEEDS
Row-by-row results
Nested markers stream a list one item at a time — a chat transcript or infinite feed, declaratively.
FAQ
Questions readers usually have
Common questions about streaming HTML out of order without JavaScript.
Conclusion
HTML’s top-to-bottom, in-order delivery was a 30-year-old assumption, not a law of physics. Breaking it — sending pages in the order data is ready rather than the order it’s written — is one of the most consequential quiet upgrades the web platform has shipped in a decade, and the best part is that its core doesn’t cost a single kilobyte of JavaScript.
The pragmatic move today: use Declarative Shadow DOM and slots for real out-of-order streaming now, and start prototyping with Declarative Partial Updates so you’re fluent when it lands. Flush your shells, kill your buffering, and stop letting your slowest widget dictate when the whole page appears.
If this was useful, read the Core Web Vitals optimization guide next — it covers the exact metrics that out-of-order streaming is designed to move.
Sources and further reading
- Declarative partial updates — Chrome for Developers (Barry Pollard, Noam Rosenthal), May 19, 2026
- WICG Declarative Partial Updates explainer — problem statement, goals, and full syntax
- WHATWG HTML PR #11818 —
<template for>for out-of-order streaming — Philip Jägenstedt - Chromium Intent to Prototype: streaming declarative shadow DOM — the
shadowrootmoderename that unlocked streaming - MDN —
<template>shadowrootmode— Declarative Shadow DOM reference and Baseline status
Written for umesh-malik.com — no-fluff technical writing on AI, Web Dev, and Engineering.
Related Articles

Web Engineering
FastAPI Finally Has Native SPA Support: app.frontend() Explained
FastAPI 0.138.0 ships app.frontend() — a native way to serve React, Vue, and Svelte SPA builds. How it works, real use cases, and what it still can't do.

Web Engineering
Cloudflare viNext: The $1,100 Next.js-on-Vite Rebuild
Cloudflare's viNext rebuilt Next.js on Vite in 7 days for $1,100: 4.4x faster builds, 57% smaller bundles, already powering CIO.gov in production.

Web Engineering
Node.js Pointer Compression: Cut Heap Memory ~50%
V8 pointer compression finally lands in Node.js: one Docker image swap cuts heap memory ~50%, improves P99 latency, and can save $80K–$300K a year.
Keep reading
Get new posts on AI, Claude Code & LLMs
New deep-dives on AI engineering, Claude Code, and developer tooling — follow along however you prefer.
About the Author
Software engineer writing about AI, Claude Code, LLMs, OpenAI, Anthropic, and developer tooling. 5+ years building production systems at Expedia Group, Tekion, and BYJU'S.