ZyVOP Logo
Content That Connects
SeriesAI NewsWhy ZyVOPJoin Discord
ZyVOP Logo
Content That Connects

Empowering developers and creators with cutting-edge insights, comprehensive tutorials, and innovative solutions for the digital future.

Content

  • Categories
  • Tags
  • Badges
  • Leaderboard
  • Write Article
  • Newsletter

Company

  • About Us
  • Why ZyVOP
  • API Documentation
  • Write for Us
  • Contact

Connect

  • Privacy Policy
  • Terms of Service
  • Cookie Policy
  • DMCA Policy
  • Code of Conduct

© 2026 ZyVOP. Crafted with care for the developer community.

Made with ❤️ by the ZyVOP team
All systems operational
HomeDeclarative Partial Updates: Out-of-Order HTML Streaming Without a Framework

Declarative Partial Updates: Out-of-Order HTML Streaming Without a Framework

Danny Holloran
Danny HolloranSenior Developer
August 22, 2026
3 min read
Declarative Partial Updates: Out-of-Order HTML Streaming Without a Framework
#JavaScript#web-apis#performance#frontend
👍1

HTML has one stubborn rule that has quietly shaped a decade of frontend architecture: it renders in the order it arrives. If the third section of your page needs a slow database query, everything after it waits. The usual escape hatches are all compromises. You buffer the whole response and give up streaming entirely, you reorder with CSS and break the accessibility tree, or you ship a framework whose main job is turning that server delay into a client-side spinner.

Chrome 148 has an experimental answer that skips all three. Under the umbrella name Declarative Partial Updates, two related APIs let the server send a placeholder now and fill it in later, and let JavaScript stream markup into an element instead of waiting for the full string. They are behind chrome://flags/#enable-experimental-web-platform-features today, with polyfills on npm and positive noises from other vendors.

Placeholders you fill in later

The declarative half revives something HTML has ignored for its entire life: processing instructions. In XML they carry metadata; in HTML they have always been parsed as comments and thrown away. The new API gives them a job.

<div><?marker name="user-panel"></div>

<!-- ...the rest of the page streams... -->

<template for="user-panel"> Welcome back, <strong>Dan</strong>. </template>

When the parser reaches <template for="user-panel">, it finds the matching <?marker> and swaps its own content in. The DOM you end up with contains no marker and no template, just the paragraph. The server never had to hold back the rest of the document while it waited on that user lookup.

There is a range form too, which is the one you will reach for most, because it gives you a loading state for free:

<ul id="results">
  <?start name="results">
  <li class="skeleton">Loading…</li>
  <?end>
</ul>

Everything between <?start> and <?end> renders immediately and gets replaced when the template shows up. Better still, a template can re-emit a marker, which turns this into an append loop. Stream one <template for="results"> per row as your query yields them, each ending with <?marker name="results">, and the list grows in place. No appendChild, no framework, no client-side JavaScript at all.

The scoping rule is the important restriction: a <template for> can only patch markers inside its own parent element. That is deliberate, and it means a template dropped into <body> has reach over the entire document including <head>. Worth knowing before you generate one from user input.

The JavaScript side got a rewrite too

The second half addresses a mess most of us have stopped noticing. Ask yourself, honestly, which of innerHTML, setHTML, setHTMLUnsafe, insertAdjacentHTML, and createContextualFragment sanitize their input, which run <script> tags, and which respect Trusted Types. Nobody remembers, because the answers were never consistent.

The proposal replaces that with a grid you can actually reason about. Six positions, each with a static and a streaming form:

Action Static Streaming
Replace contents setHTML() streamHTML()
Replace the element itself replaceWithHTML() streamReplaceWithHTML()
Insert as first child prependHTML() streamPrependHTML()
Insert as last child appendHTML() streamAppendHTML()
Insert before / after beforeHTML() / afterHTML() streamBeforeHTML() / streamAfterHTML()

Every one has an Unsafe twin. The naming is the whole point: the plain versions sanitize by default, the Unsafe versions do not and additionally accept runScripts: true if you actually want scripts to execute. The word "unsafe" is a speed bump, not a prohibition.

The streaming versions are the genuinely new capability. They return a WritableStream, so a fetch response can go straight into the DOM as it arrives:

const el = document.querySelector("#content");
const response = await fetch("/api/content.html");

response.body
  .pipeThrough(new TextDecoderStream())
  .pipeTo(el.streamHTMLUnsafe());

That is the thing SPAs have never been able to do. Initial page loads have always streamed; every client-side route change since has thrown that away and waited for a complete payload before touching the DOM.

Where this actually goes

The two halves compose, and that is where it gets interesting. Because streamHTMLUnsafe() behaves like the main parser, it processes <template for> instructions as they land. So a client-side route change can be an outline page full of markers plus a stream of templates slotting into them, with no per-element querySelector bookkeeping. That is a surprising amount of a component framework, expressed in markup.

Temper expectations on timing. This is one engine, behind a flag, and the sanitizer that setHTML depends on is still missing in Safari. The two polyfills (template-for-polyfill and html-setters-polyfill) are worth a spike, but read the fine print: the setters polyfill buffers rather than streams, so it gives you the API shape without the performance win. Treat it as a preview of where the platform is heading, not something to put in front of users this quarter.

Sources: Declarative partial updates (Chrome for Developers) and the WICG explainer.

Danny Holloran

Danny Holloran

Senior Developer

Senior Frontend & Fullstack Developer with 14+ years building performant, scalable web applications. Passionate about architecture, mentorship, and finding the right tool for the job.

Comments (0)

Login to post a comment.

Related Posts

Caching Strategies Every Backend Developer Must Know

A practical, code-first guide to cache-aside, read-through, write-through, write-behind, and refresh-ahead patterns — with Redis examples, eviction policies, and the cache invalidation problem explained clearly.

Read article

React's Activity Component: Hide UI Without Losing Its State

React 19.2's Activity component hides a subtree instead of unmounting it, so state, scroll position, and DOM survive the round trip. Here's how it behaves, what it does to your Effects, and where it costs you.

Read article

revalidateTag vs updateTag: Next.js Split Cache Invalidation in Two

Next.js 16 gave cache invalidation two different functions instead of one, and the split maps to a real distinction: content that can lag versus content the user just typed.

Read article

How ZyVOP's Syndication Engine Works: Dev.to, Hashnode, Medium, and Bluesky

Cross-posting sounds simple until you try to build it. Every platform has different APIs, different formats, different quirks, and different failure modes. Here's the complete technical breakdown of how ZyVOP's syndication engine works under the hood.

Read article

Angular 22: The End of Boilerplate and the Consolidation of the Reactive Era

If you have been following the evolution of Google's framework over the last few years, you know it has been undergoing a silent reconstruction — piece by piece...

Read article