{"slug": "declarative-partial-updates", "title": "Declarative partial updates", "summary": "The article announces new \"Declarative Partial Updates\" APIs being developed by the Chrome team, available for testing in Chrome 148. These APIs introduce out-of-order streaming using `<template>` elements and processing instruction placeholders (like `<?marker>`, `<?start>`, and `<?end>`) to allow HTML to be delivered and inserted non-linearly, breaking free from the traditional top-to-bottom parsing order. This approach aims to improve web performance by enabling asynchronous, component-based content delivery without the need for heavy JavaScript frameworks.", "body_md": "Published: May 19, 2026\n\nThe web has long since moved on from the static, document-driven medium that it started as. Modern, rich web apps are used by everyone for many reasons, from communicating, purchasing, consuming rich content, to managing our complex lives.\n\nHTML, despite all its advances, is still delivered in-order in a top-to-bottom fashion with little regard for when content is ready or when the user consumes it. CSS lets you change the ordering of content, but often with significant accessibility side effects. JavaScript lets you manipulate the DOM through various APIs to break free of this somewhat, but those often require verbose syntax or construction of DOM trees to plug into HTML.\n\nPerformance is incredibly important for the web, given the client-server nature of the medium but suboptimal choices are often made to circumvent this in-order nature of HTML, which slows down performance. This includes waiting until the whole page is ready or using a heavy framework to deliver components in an asynchronous manner. The popularity of JavaScript frameworks shows that web developers prefer a component-based model rather than the rigid document mental model of the web's origins.\n\nThe Chrome team has been considering this problem and has been developing new additions to the web platform under the name of [Declarative Partial Updates](https://github.com/WICG/declarative-partial-updates).\n\nTwo new sets of APIs make it easier to deliver HTML in a less linear fashion, whether out-of-order in the HTML document itself or through easier ways to dynamically insert HTML into existing documents using new JavaScript APIs. These are ready for developer testing from Chrome 148 using the `chrome://flags/#enable-experimental-web-platform-features`\n\nflag. Polyfills are also available to let you use these new APIs right away, even in browsers that don't yet support them.\n\nThese additions to the web platform are being standardized with positive feedback from other browser vendors and standardization avenues. The relevant standards are in the process of being updated to include these new APIs.\n\n## Out-of-order streaming\n\nThe first set of changes are new [out-of-order streaming APIs](https://github.com/WICG/declarative-partial-updates/blob/main/patching-explainer.md) using the `<template>`\n\nHTML element and processing instruction placeholders. For example:\n\n```\n<div>\n  <?marker name=\"placeholder\">\n</div>\n\n...\n\n<template for=\"placeholder\">\n  Here is some <em>HTML content</em>!\n</template>\n```\n\n[Processing instructions](https://developer.mozilla.org/docs/Web/API/ProcessingInstruction) have existed in XML for a long time, but have been treated as comments in HTML and ignored. This new API changes that and brings processing instructions to HTML. When the browser sees the `<?marker name=\"placeholder\">`\n\nprocessing instructions, it doesn't do anything straight away—much like before—but they can be referenced later.\n\nThe [ <template> element](https://developer.mozilla.org/docs/Web/HTML/Reference/Elements/template) looks up the corresponding processing instructions with a\n\n`name`\n\nattribute and replaces the content. In this case, after being parsed, the DOM ends up as:\n\n```\n<div>\n  Here is some <em>HTML content</em>!\n</div>\n```\n\nAs well as the `<?marker>`\n\nattribute for replacements, there are also `<?start>`\n\nand `<?end>`\n\nrange markers which allow for temporary placeholder content to be shown before the template is processed:\n\n```\n<div>\n  <?start name=\"another-placeholder\">\n  Loading…\n  <?end>\n</div>\n\n...\n\n<template for=\"another-placeholder\">\n  Here is some <em>HTML content</em>!\n</template>\n```\n\nIn this case, `Loading…`\n\nshows until the `<template>`\n\nis seen and then is replaced with the new content.\n\nIt is also possible to include processing instructions in templates to allow multiple updates:\n\n```\n<ul id=\"results\">\n  <?start name=\"results\">\n  Loading…\n  <?end>\n</ul>\n\n...\n\n<template for=\"results\">\n  <li>Result One</li>\n  <?marker name=\"results\">\n</template>\n...\n\n<template for=\"results\">\n  <li>Result Two</li>\n  <?marker name=\"results\">\n</template>\n...\n```\n\nThis results in the following HTML after being parsed:\n\n```\n<ul id=\"results\">\n  <li>Result One</li>\n  <li>Result Two</li>\n  <?marker name=\"results\">\n</ul>\n```\n\nWith the final processing instruction at the end in case any more `<template for=\"results\">`\n\nare added to the document later.\n\n### Demo\n\nIn this video, a basic photo album application is implemented with streaming HTML:\n\nBoth the status and photos are streamed into the HTML after the initial layout.\n\n### Use cases\n\nThere are many use cases for this out-of-order patching HTML when coupled with streaming HTML:\n\n**Island architecture.** A common pattern popularized by frameworks like Astro the[island architecture](https://jasonformat.com/islands-architecture/)where components are hydrated independently on top of static HTML. The`<template for>`\n\nAPI lets static content be handled in a similar fashion directly in HTML. JavaScript frameworks can also use this for more interactive islands or to handle components.**Delivery content when it is ready.** Thanks to this island architecture, content can be streamed when it is ready rather than held back for content that requires extra processing, for example, a database lookup. While many platforms allow streaming HTML, the in-order nature of HTML means that content is often held back, or by resorting to complex JavaScript DOM manipulations. Now you can deliver the static content while waiting, and then plug in the more expensive content at the end of the HTML stream.**HTML can be delivered in the optimal order for page load performance.** Taking this one step further, you can change the order even when it is ready. For example, mega menus are a common navigation feature that contain a lot of HTML that the user won't see until the page becomes interactive. This large chunk of HTML can be delivered later in the HTML document to prioritize more important HTML needed for the initial page load. Order is no longer a barrier with HTML.\n\nThese are just some use cases, and it is exciting to see what developers use this new API for.\n\n### Restrictions and subtleties\n\nThe API includes a few restrictions and subtleties to be aware of:\n\n`<template for>`\n\ncan only update processing instructions within the same parent element for security reasons. Adding`<template for>`\n\ndirectly to the`<body>`\n\nelement gives it access to the whole document (including the`<head>`\n\n).- The\n`<?end>`\n\nprocessing instruction is optional and if missing, the content between the`<?start>`\n\nelement and the end of the containing element will be replaced. - Moving processing instructions after a\n`<template for>`\n\nhas started streaming can also have unexpected consequences with the new content continuing to stream into the old location. - Note that when inserting\n`<template for>`\n\ndynamically with method like`setHTML`\n\nor`innerHTML`\n\n, the \"parent\" of the template when it is parsed is an intermediate document fragment. That means that inserting HTML using these methods cannot modify existing DOM, and the patching happens \"in place\" inside the fragment. However, when streaming using methods like`streamHTMLUnsafe`\n\n(that we're about to cover!), there is no intermediate fragment so the templates can replace existing content.\n\n### Future potential additions\n\nSome potential future additions that are under consideration include:\n\n**Client side includes.** For example,`<template for=\"footer\" patchsrc=\"/partials/footer.html\">`\n\n.**Batching.** Client-side, fragment includes could also be extended to handle batching to ensure multiple updates happen at the same time.**Preventing overwriting content that will not change.** This could be achieved with a content revision number or versioning. This would let state be maintained between route changes or other updates rather than resetting the content.**Sanitizing while patching.** For example,`<template for=icon safe><svg id=\"from-untrusted-source\">...</svg></template>`\n\n### Polyfill\n\nThe Chrome team has [released a template-for-polyfill](https://github.com/GoogleChromeLabs/template-for-polyfill) which is\n\n[available on npm](https://www.npmjs.com/package/template-for-polyfill)to let sites use this new functionality right away even before this lands in other browsers.\n\nThere are [some limitations](https://github.com/GoogleChromeLabs/template-for-polyfill#limitations) as it cannot directly update browser's HTML parsers, but the most common use cases are covered. Sites should still test in other browsers.\n\n## Renewed HTML insertion and streaming methods\n\nNot all content can be delivered in HTML. A second part of the work Chrome is doing in this area involves making it easier to update content through JavaScript.\n\nThere already are multiple ways to dynamically inject HTML into an existing document using JavaScript:\n\n`setHTML`\n\n`setHTMLUnsafe`\n\n`innerHTML`\n\nand`outerHTML`\n\nsetters`createContextualFragment`\n\n`insertAdjacentHTML`\n\nHowever, they all work in slightly different ways with subtleties and differences that developers may not always consider:\n\n- Does the new content overwrite or append?\n- Do they sanitize potentially dangerous HTML by escaping\n`<script>`\n\ntags, for example? - If not, should\n`<script>`\n\n's run? - How do they work with TrustedTypes?\n\nFew developers could honestly look at those APIs and confidently answer those questions for each of them.\n\nA big limitation is that they can only be used for a complete set of HTML known in advance, when there have been calls to allow [HTML to be streamed](https://github.com/whatwg/html/issues/2142). Practically, this means you need to download the entire content before inserting it, when one of the strong points of HTML is the ability to stream in content right away. This can be worked around in a limited fashion by splitting up payloads or using hacky, deprecated methods like `document.write`\n\n, but they introduce their own problems.\n\n### A new set of Static and Streaming APIs\n\nChrome has proposed a [suite of new APIs](https://github.com/WICG/declarative-partial-updates/blob/main/dynamic-markup-revamped-explainer.md) and extensions to the existing `setHTML`\n\nand `setHTMLUnsafe`\n\nthat cleans this up, as well as introducing streaming functionality:\n\nThere are methods to set or replace along with methods to insert content before or after existing HTML. Each method has stream equivalents:\n\n| Action | Static | Streaming |\n|---|---|---|\n| Set the HTML contents of the element | `setHTML(html, options);` |\n`streamHTML(options);` |\n| Replace the entire element with this HTML | `replaceWithHTML(html, options);` |\n`streamReplaceWithHTML(options);` |\n| Add the HTML before the element | `beforeHTML(html, options);` |\n`streamBeforeHTML(options);` |\n| Add the HTML as the first child of the element | `prependHTML(html, options);` |\n`streamPrependHTML(options);` |\n| Add the HTML as the last child of the element | `appendHTML(html, options);` |\n`streamAppendHTML(options);` |\n| Add the HTML after the element | `afterHTML(html, options);` |\n`streamAfterHTML(options);` |\n\nThere are also `Unsafe`\n\nversions we'll cover shortly. While there might appear to be a lot of them (especially when you add in the `Unsafe`\n\nequivalents), the consistent naming convention makes it more obvious what each does compared to the unrelated methods mentioned previously.\n\nThe static versions take new HTML as a DOM String argument, along with optional options:\n\n``` js\nconst newHTML = \"<p>This is a new paragraph</p>\";\nconst contentElement = document.querySelector('#content-to-update');\n\ncontentElement.setHTML(newHTML);\n```\n\nThe streaming versions work with the [Streams API](https://developer.mozilla.org/docs/Web/API/Streams_API) such as with a `getWriter()`\n\n:\n\n``` js\nconst contentElement = document.querySelector('#content-to-update');\nconst writer = contentElement.streamHTMLUnsafe().getWriter();\n\n// Example stream of updating content\nwhile (true) {\n await writer.write(`<p>${++i}</p>`);\n await new Promise((resolve) => setTimeout(resolve, 1000));\n}\n\nwriter.close();\n```\n\nOr, alternatively from a fetch response, with [pipe chains](https://developer.mozilla.org/docs/Web/API/Streams_API/Concepts#pipe_chains):\n\n``` js\nconst contentElement = document.querySelector('#content-to-update');\n\nconst response = await fetch('/api/content.html');\n\nresponse.body\n  .pipeThrough(new TextDecoderStream())\n  .pipeTo(contentElement.streamHTMLUnsafe());\n```\n\nWe're also [planning to add a convenience method](https://chromestatus.com/feature/5146752165478400) where you can stream directly without needing the intermediate `TextDecoderStream()`\n\nstep.\n\nThe `options`\n\nargument lets you specify a custom `sanitizer`\n\nwhich defaults to `default`\n\nmeaning [the default sanitizer config](https://developer.mozilla.org/docs/Web/API/HTML_Sanitizer_API/Default_sanitizer_configuration). It is used like so:\n\n``` js\nconst newHTML = \"<p>This is a new paragraph</p>\";\nconst contentElement = document.querySelector('#content-to-update');\n\n// Only allows basic formatting\nconst basicFormattingSanitzer = new Sanitizer({ elements: [\"em\", \"i\", \"b\", \"strong\"] });\n\ncontentElement.setHTML(newHTML, {sanitizer: basicFormattingSanitzer});\n```\n\n### \"Unsafe\" methods\n\nThere are also \"unsafe\" versions of each of the APIs:\n\n| Action | Static | Streaming |\n|---|---|---|\n| Set the HTML contents of the element | `setHTMLUnsafe(html,options);` |\n`streamHTMLUnsafe(options);` |\n| Replace the entire element with this HTML | `replaceWithHTMLUnsafe(html, options);` |\n`streamReplaceWithHTMLUnsafe(options);` |\n| Add the HTML before the element | `beforeHTMLUnsafe(html, options);` |\n`streamBeforeHTMLUnsafe(options);` |\n| Add the HTML as the first child of the element | `prependHTMLUnsafe(html, options);` |\n`streamPrependHTMLUnsafe(options);` |\n| Add the HTML as the last child of the element | `appendHTMLUnsafe(html, options);` |\n`streamAppendHTMLUnsafe(options);` |\n| Add the HTML after the element | `afterHTMLUnsafe(html, options);` |\n`streamAfterHTMLUnsafe(options);` |\n\nThese \"unsafe\" methods switch off the sanitizer by default (you can specify a custom sanitizer if you wish) and also allow scripts to be run with an optional `runScripts`\n\noption (which defaults to `false`\n\n).\n\nLike `setHTML`\n\n, `setHTMLUnsafe`\n\nis an existing method, but the `runScripts`\n\noptions parameter has been added to it to allow it to be used with script execution:\n\n``` js\nconst newHTML = `<p>This is a new paragraph</p>\n                 <script src=script.js></script>`;\nconst contentElement = document.querySelector('#content-to-update');\n\ncontentElement.setHTMLUnsafe(newHTML, {runScripts: true});\n```\n\nThe \"unsafe\" wording in the method is to remind developers of the potential risk and how they may want to sanitize or restrict scripts, not to say that these methods shouldn't be used.\n\nHow \"unsafe\" this is depends on how trusted the inputs are. The `Unsafe`\n\nstatic methods all work with both DOM String or [ TrustedHTML](https://developer.mozilla.org/docs/Web/API/TrustedHTML) as the\n\n`html`\n\narguments and also allow sanitizers to be used. Though with `runScript`\n\nthe whole intent is to allow scripts, hence why by default no sanitizer is used.### Use cases\n\nThese new APIs make it easier for developers to add HTML to existing pages, adding new APIs with consistent names and options. The streaming APIs bring the performance benefits of not having to wait until all the new content is available to the platform.\n\nUse cases include:\n\n**Dynamic streaming of large content updates in Single Page Apps.** As mentioned previously, a big drawback of current SPAs is they couldn't benefit from the streaming nature of initial HTML loads—until now!**Inserting common content like HTML footers.** Using the JavaScript APIs lets you pull in partials and insert them into the page, benefiting from caching, rather than repeating them in every page sent. However, given the dependency on JavaScript to run, this should only be used for content that is not going to be visible in the initial load.\n\nAgain, those are just a couple of examples and we're eager to see what you all come up with!\n\n### Restrictions and subtleties\n\nThese new APIs also include a few restrictions and subtleties to be aware of:\n\n- Integration of streaming with the Trusted Types API requires using a new\n`createParserOptions`\n\nmethod which allows injecting a sanitizer to any HTML setting operation. See[the explainer for more details on trusted types integration](https://github.com/WICG/declarative-partial-updates/blob/main/dynamic-markup-revamped-explainer.md#trusted-types-integration) - Similar to\n`<template for>`\n\nmoving elements that are being streamed into, can create unexpected consequences or stream errors. `streamHTMLUnsafe`\n\nworks more like the main parser in many ways, including processing`<template for>`\n\ninstructions as they are added to the main document and deferring`defer`\n\nscripts until the end of the stream.\n\n### Polyfill\n\nThe Chrome team has [released a html-setters-polyfill](https://github.com/GoogleChromeLabs/html-setters-polyfill) which is\n\n[available on npm](https://www.npmjs.com/package/html-setters-polyfill)to let sites use this new functionality right away even before this lands in other browsers.\n\nNote that this polyfill does not stream, and instead buffers and applies when complete. It's more a polyfill for the API shape than for functionality.\n\nAdditionally, setting of safe content depends on `setHTML`\n\nand the [Sanitizer API](https://developer.mozilla.org/docs/Web/API/HTML_Sanitizer_API) which is not supported in Safari.\n\n## Use these both together\n\nWhile these are two separate APIs, real power comes from combining them. By streaming new `<template for>`\n\nelements into the HTML, you can dynamically update different parts of content without having to directly target each with separate JavaScript references to the DOM.\n\nA basic SPA-style page load could be implemented by loading an outline page with processing instructions and then streaming each new page's templates into the bottom of the HTML to slot into those processing instructions.\n\nUndoubtedly there is more potential and use-cases for both these APIs so don't let our (limited!) imaginations hold you back. By making partial updates easier to manage you can reduce some of the boilerplate code, make updates easier, and unlock new potential for the web!", "url": "https://wpnews.pro/news/declarative-partial-updates", "canonical_source": "https://developer.chrome.com/blog/declarative-partial-updates?hl=en", "published_at": "2026-05-19 07:00:00+00:00", "updated_at": "2026-05-24 05:07:40.336373+00:00", "lang": "en", "topics": ["developer-tools", "products"], "entities": ["Chrome"], "alternates": {"html": "https://wpnews.pro/news/declarative-partial-updates", "markdown": "https://wpnews.pro/news/declarative-partial-updates.md", "text": "https://wpnews.pro/news/declarative-partial-updates.txt", "jsonld": "https://wpnews.pro/news/declarative-partial-updates.jsonld"}}