Out-of-order HTML streaming, a pattern that allows users to view and interact with a page before it fully loads, is making its way to web browsers. Under the proposal, the browser patches placeholders as data arrives via declarative HTML or matching JavaScript APIs. Chrome 150 ships the declarative features, while Safari and Firefox have signaled support.
The declarative proposal introduces out-of-order streaming with the standard <template> element via a new for attribute. Developers declare single insertion points (e.g., <?marker name="profile">), or wrap temporary fallback content (e.g., <?start name="profile">Loading profile...<?end>).
When the server streams a matching <template for="profile"> later in the document, the HTML parser matches the identifier, removes the <?start> and <?end> markers along with any intermediate fallback nodes between them, and inserts the template’s contents into that DOM position. Developers may also include processing instructions in templates to allow multiple updates:
<ul id="results">
<?start name="results">
Loading…
<?end>
</ul>
<!-- Streamed later in the HTTP response -->
<template for="results">
<li>Result One</li>
<?marker name="results">
</template>
...
<template for="results">
<li>Result Two</li>
<?marker name="results">
</template>
...
After the template HTML is parsed and processed, the resulting HTML on the page becomes:
<ul id="results">
<li>Result One</li>
<li>Result Two</li>
<?marker name="results">
</ul>
To prevent cross-component injection attacks (e.g., untrusted markup hijacking sensitive forms or navigation targets elsewhere in the document), a <template for> can strictly only patch processing instructions (markup nodes that begin with <? delimiters) located within its immediate parent element (i.e., its direct siblings and their descendants). As an explicit specification exception, placing the template directly under <body> grants it global document scope, allowing deferred updates to reach elements inside the <head> tag or nested structural containers.
To extend streaming workflows to client-side scripting, the initiative pairs these markup primitives with a unified suite of static and streaming DOM insertion methods. The specification defines a predictable matrix of operations: setHTML, replaceWithHTML, beforeHTML, prependHTML, appendHTML, and afterHTML, along with matching streaming methods (streamHTML, streamAppendHTML, etc.) and *Unsafe variants. Invoking streamHTMLUnsafe(), for instance, returns a WritableStream that incrementally pipes incoming chunks into an internal HTML fragment parser. The Fetch API complements this with the response.textStream() helper:
// Stream a dynamic HTML fragment directly into a target container
const feedContainer = document.querySelector("#feed-container");
const response = await fetch("/api/feed-stream");
// Pipe UTF-8 text straight into the browser's fragment parser
await response.textStream().pipeTo(
feedContainer.streamHTMLUnsafe({ runScripts: false })
);
By internalizing out-of-order delivery and rendering into the browser engine, the proposal standardizes rendering techniques that front-end frameworks have long implemented in different ways. Originally pioneered in 2009 by Facebook’s BigPipe and later popularized by frameworks such as React (with Suspense streaming) and Next.js, the differing techniques have the same underlying goal: to prevent a slow-to-compute part of the page from blocking the entire page.
The declarative markup primitives (<template for> and processing instruction targets) have been incorporated into the WHATWG HTML Living Standard, with initial support shipping in Chrome and Edge 150 alongside textStream() in version 151, while the companion JavaScript DOM streaming methods continue through a separate standardization process. WebKit has published a positive standards position, and Mozilla has signaled receptive interest.