#1134: Incubation: patching (interleaved out-of-order streaming)
Discussions
Log in to see TAG-private discussions.
Discussed
Sep 29, 2025 (See Github)
Jeffrey: Dan's draft comment looks good. We should rephrase some of the review bullets, concatenate it, and post it.
Dan: I'll clean this up and post it tomorrow.
Comment by @dandclark Oct 1, 2025 (See Github)
Thanks for sending this to the TAG! We have some questions and things you may want to consider.
- Since perceived performance is a primary motivation, we're wondering if you have expectations about how much time can be saved and where specifically the saved time is going to come from. Maybe the benefit of one-off (JS-driven) patches could be measured as INP improvement? How about Interleaved (HTML-driven) patches? Or is the goal more about reducing JS payload size because streamed updates no longer need to be managed in JS? Maybe both?
- It's good to see
patchidused to target patches rather thanid, but consider also https://github.com/webplatformco/explainers/blob/main/html-element-references/README.md. How does the scoping ofpatchidwork with shadow DOM? - Regarding "The first patch for a target in the document stream replaces its content, and the next ones append." The need to append is clear but it seems like it'd be simpler if all patches appended, without the first one replacing. If there's a desire to show a "Loading" message/icon that goes away when the first patch comes in, that could alternatively be managed with the proposed
:patch-pendingpseudo. - If the first patch appended rather than replaced the initial content, would there still be a use case for replacing a range?
- The Security & Privacy self-review answer to "Does data exposed by your specification carry related but distinct information that may not be obvious to users?" should probably be "Yes" since this is about streaming HTML, which carries lots of different kinds of information, to an arbitrary node, where different nodes might expect different kinds of information.
- What does interaction with the BFCache look like if the page navigates while there's an active stream? Does the streaming continue? Or does the fetch get cancelled in a way that might not be recoverable when the page is reloaded?
- The
pendingstate in https://github.com/w3c/csswg-drafts/issues/12579 seems useful, especially since it seems to replace the use case for the first patch to replace the initial content as discussed above. Triggering it based on the presence of thepatchidattribute is a little worrying. Maybe the declarative patch should also be able to update the original node's attributes? We're happy that the CSSWG is exploring how and whether to merge the proposed:patchingpseudo-classes into other pseudo-classes that already exist. - "Patch" might not be the right name. The action is either to replace or append to an existing node (and we'd prefer to avoid "replace" if possible), while "patch" implies changes in the middle of the content. Perhaps something like
appendtoas the declarative attribute is a better fit? - We see that the API shape and naming question has continued in https://github.com/whatwg/html/issues/11669. We think that's going in a good direction, with a consistent structure across the streaming and non-streaming methods, and different names for synchronous vs asynchronous methods. We don't have any further comments on the details.
- Can multiple patches be active at once? Can a patch be started when the main HTML parser is still active on the document? Would any issues arise from allowing multiple instances of the HTML parser to be active (in a paused state) simultaneously on the same document?
- If started before the
loadevent fires, do active patches block theloadevent? OrDOMContentsLoaded? More generally, is there a need for a central way for developers to track whether there are active patches, or is it enough to be able to track them individually? - Patching contents from a URL kind of sounds like what HTML imports was trying to do; it's worth revisiting the history there to ensure the same issues aren't getting repeated that led to its deprecation. An issue with HTML imports was that it was a new type of dependency that didn't integrate well with ES modules (also new at the time). Is that a problem for this proposal? ES modules seem incompatible with streaming, as they require all module dependencies to be loaded prior to execution.
- Can a single patch be directed into multiple elements?
- Can interleaved patches be chained as follows?
<section patchid=gallery>Loading...</section>
<template patchfor=gallery>
<div patchid=gallery-item>Loading item...</div>
...
</template>
<template patchfor=gallery-item>...</template>
Comment by @noamr Oct 2, 2025 (See Github)
Thanks for sending this to the TAG! We have some questions and things you may want to consider.
Thanks for the detailed review!
- Since perceived performance is a primary motivation, we're wondering if you have expectations about how much time can be saved and where specifically the saved time is going to come from. Maybe the benefit of one-off (JS-driven) patches could be measured as INP improvement? How about Interleaved (HTML-driven) patches? Or is the goal more about reducing JS payload size because streamed updates no longer need to be managed in JS? Maybe both?
These numbers are going to be very difficult to produce effectively. We do have evidence however that this technique is used in production today.
- It's good to see
patchidused to target patches rather thanid, but consider also https://github.com/webplatformco/explainers/blob/main/html-element-references/README.md. How does the scoping ofpatchidwork with shadow DOM?
References here are both tree-scoped and element-scoped to the parent of the patch template (the latter applies if it's not a child of body).
- Regarding "The first patch for a target in the document stream replaces its content, and the next ones append." The need to append is clear but it seems like it'd be simpler if all patches appended, without the first one replacing. If there's a desire to show a "Loading" message/icon that goes away when the first patch comes in, that could alternatively be managed with the proposed
:patch-pendingpseudo.
The rationale, which came from userland solutions that implement this, is that you want to be able to "replay" all the patches and reach the same result, e.g. when traversing history.
We are thinking of adding a patch "mode" (replace/set/append/prepend/before/after), with an "auto" mode that uses this mechanism.
- If the first patch appended rather than replaced the initial content, would there still be a use case for replacing a range?
See above. We are currently thinking of using those modes rather than replacing a range.
- The Security & Privacy self-review answer to "Does data exposed by your specification carry related but distinct information that may not be obvious to users?" should probably be "Yes" since this is about streaming HTML, which carries lots of different kinds of information, to an arbitrary node, where different nodes might expect different kinds of information.
Ack
- What does interaction with the BFCache look like if the page navigates while there's an active stream? Does the streaming continue? Or does the fetch get cancelled in a way that might not be recoverable when the page is reloaded?
If you're navigating away from the page, what would be cancelled is the fetch that instantiated the response rather than the stream itself. if you have an ongoing JS stream I don't think it would be affected. But it's a good detail to flesh out.
- The
pendingstate in [css-selectors-4] CSS reflection of "patching" w3c/csswg-drafts#12579 seems useful, especially since it seems to replace the use case for the first patch to replace the initial content as discussed above. Triggering it based on the presence of thepatchidattribute is a little worrying. Maybe the declarative patch should also be able to update the original node's attributes? We're happy that the CSSWG is exploring how and whether to merge the proposed:patchingpseudo-classes into other pseudo-classes that already exist.
Ack.
- "Patch" might not be the right name. The action is either to replace or append to an existing node (and we'd prefer to avoid "replace" if possible), while "patch" implies changes in the middle of the content. Perhaps something like
appendtoas the declarative attribute is a better fit?
Thinking of contentfor
- Can multiple patches be active at once? Can a patch be started when the main HTML parser is still active on the document? Would any issues arise from allowing multiple instances of the HTML parser to be active (in a paused state) simultaneously on the same document?
Currently patching the same element twice would cancel previous patches. There are a few permuations of this that are yet to be defined.
- If started before the
loadevent fires, do active patches block theloadevent? OrDOMContentsLoaded? More generally, is there a need for a central way for developers to track whether there are active patches, or is it enough to be able to track them individually?
Patches from <template> would block the load event by definition, since the HTML is not fully loaded.
With patchsrc - they probably should as well but patchsrc is currently on the back burner.
Patches instantiated in JS are likely not going to block the load event.
- Patching contents from a URL kind of sounds like what HTML imports was trying to do; it's worth revisiting the history there to ensure the same issues aren't getting repeated that led to its deprecation. An issue with HTML imports was that it was a new type of dependency that didn't integrate well with ES modules (also new at the time). Is that a problem for this proposal? ES modules seem incompatible with streaming, as they require all module dependencies to be loaded prior to execution.
Yes we've revisited the history carefully. But currently patchsrc is on the back burner so we'll come back to it later on.
- Can a single patch be directed into multiple elements? No
<section patchid=gallery>Loading...</section> <template patchfor=gallery> <div patchid=gallery-item>Loading item...</div> ... </template> <template patchfor=gallery-item>...</template>
- Can interleaved patches be chained as follows?
Yes as long as the scoping rules work and the outlet's parent is an ancestor of the patching template.
Discussed
Oct 6, 2025 (See Github)
Lola: Should we reassign Dan?
Jeffrey: Yes. And we can leave him assigned to the patching one because that's about ready to close.
Discussed
Oct 6, 2025 (See Github)
Jeffrey: Proponents replied. I'll reply satisfied and encourage them to keep iterating with the HTML group.
Comment by @jyasskin Oct 9, 2025 (See Github)
Thank you! It looks like this is being discussed in the right places, and we're happy to see it proceed.
Comment by @noamr Feb 3, 2026 (See Github)
Hello! Not sure if to re-open it or just put this as an FYI, but the work on this has evolved a lot, and the current direction is to allow the HTML parser to parse processing instructions, and then use them for patching. See updated explainer.
@jyasskin @dandclark can you think if this aligns with the previous resolution of sending this back to WHATWG, or does TAG want to take another look?
Discussed
Feb 9, 2026 (See Github)
JY: Noam reopened this. The design changed form attribute/elements to processing instructions. It adds processing instructions, which is not something we have traditionally have had in HTML.
DC: It talks about how to patch sub-trees, and things that are very complicated. Are we sure this are use cases that need to be supported.
JY: I'm worried about the strange sub-trees you can create.
DC: I'll try to comment something soon.
Discussed
Feb 23, 2026 (See Github)
Jeffrey: Please look at proposed comment.
Discussed
Mar 16, 2026 (See Github)
Jeffrey: I was asked to look at the comment, I think it's good, shall I post?
Marcos: Sure.
Comment by @jyasskin Mar 18, 2026 (See Github)
Thanks for letting us know about this change! We have some questions about it.
Mainly we'd like to better understand the reason for the switch away from using an element combined with contentmethod as the target for the patch. Introducing a new node type to HTML that can represent an arbitrary range of the document seems more complex, so we'd expect to see this switch satisfy a clear user or developer need.
The explainer's outline of the weaknesses of the element approach is helpful but still leaves questions.
Doesn't support replacing arbitrary ranges of nodes, only an element or all of its children.
Most cases where only some children need to be replaced seem to be achievable with the contentmethod/contentname approach from earlier versions of the explainer by just wrapping the content to be replaced with more elements.
For instance this example:
<div marker="part-one part-two">
<?start name="part-one">
Placeholder content
<?end name="part-one">
<hr>
<?start name="part-two">
Placeholder content
<?end name="part-two">
</div>
<template for="part-one">
<p>Actual 1st part of the content</p>
</template>
<template for="part-two">
<p>Actual 2nd part of the content</p>
</template>
Could be rewritten as:
<div>
<span contentname="part-one">
Placeholder content
</span>
<hr>
<span contentname="part-two">
Placeholder content
</span>
</div>
<template for="part-one" contentmethod="replace">
<p>Actual 1st part of the content</p>
</template>
<template for="part-two" contentmethod="replace">
<p>Actual 2nd part of the content</p>
</template>
A case where the contentname element falls short would be if the desired start and end of the range to be replaced do not fall under the same parent, e.g.:
<body>
<?start name="patch">
<section>
Content to replace
<?end name="patch">
This probably isn't replaced
</section>
</body>
Is this sort of scenario really something developers are asking for? It's not clear to us that there's a use case for replacing ranges whose start and end don't share a parent.
The algorithm to decide what content to delete when replacing a lopsided range like this will not be trivial. Would the algorithm for Range.deleteContents be reused here? And where would inserted nodes wind up? After the start, before the end, or at the highest point in the tree within the range?
Another stated weakness of the contentmethod approach is:
In order to support patching <title>, which uses the RCDATA tokenizer state, the tag name of the target element must be repeated. This is because switching to the RCDATA (or RAWTEXT) state in a
<template>element would change how the content is parsed in supporting and non-supporting parsers, which could be a security concern.
Unclear why repeating the tag is needed -- why doesn't this "just work"?
<title contentname="title">To be patched...</title>
<template for="title" contentmethod="replace-children">
Title content from the patch
</template>
It does seem that patching only a substring of the <title> would be tough with this approach. But have you confirmed that there's a real developer need to patch substrings of <title>, or patch <title> at all?
Lastly:
prepend can fail if the original first child of the element is removed, meaning that a patch can fail mid-stream, requiring some error handling/reporting.
This one is hard to follow. Once the patch has started, there is a single parser insertion point regardless of whether it was set using an element or ProcessingInstructions. Wouldn't we need to handle DOM mutations around that insertion point for either use case?
Other miscellaneous bits of feedback:
- The explainer shows the
markerattribute set on the parent node of ranges to be patched, but it's not clear why this is needed. Could the reason be stated more explicitly? Could this work without the attribute, only the patch ranges? - The script-initiated-patching section mentions
<template contentmethod>, should this say<template for>in the ProcessingInstructions version of the proposal? - What happens when patches overlap?
<div marker="part-one part-two">
<?start name="part-one">
One
<?start name="part-two">
Two
<?end name="part-one">
Three
<?end name="part-two">
Four
</div>
[Thanks to @dandclark for drafting this comment!]
Comment by @noamr Mar 18, 2026 (See Github)
Thanks for letting us know about this change! We have some questions about it.
Thanks for the thought out questions!
Mainly we'd like to better understand the reason for the switch away from using an element combined with
contentmethodas the target for the patch. Introducing a new node type to HTML that can represent an arbitrary range of the document seems more complex, so we'd expect to see this switch satisfy a clear user or developer need.The explainer's outline of the weaknesses of the element approach is helpful but still leaves questions.
Doesn't support replacing arbitrary ranges of nodes, only an element or all of its children.
It was brought on by several developers, some major potential adopters, and framework authors. See some discussion on https://github.com/WICG/declarative-partial-updates/issues/. I see this as the biggest design weakness of the contentmethod API.
Essentially, when incorportating this feature into a framework workflow, the "partials" don't always relate to their parent element, nor do they necessarily appear at the beginning or end of it.
Some simple examples include replacing a list of meta tags in the head or a range of rows in a table. This simply cannot be done with contentmethod.
At some point in the discussions at WHATWG we were thinking of having contentmethods in addition to those arbitrary ranges, but then we found that this doesn't add much and that using ranges covers all of the use cases.
Unclear why repeating the tag is needed -- why doesn't this "just work"?
<title contentname="title">To be patched...</title> <template for="title" contentmethod="replace-children"> Title content from the patch </template> It does seem that patching only a substring of the `<title>` would be tough with this approach. But have you confirmed that there's a real developer need to patch substrings of `<title>`, or patch `<title>` at all?
This is a problem because of how the tokenizer works. The title element changes the tokenizer to RCDATA - meaning that it parses everything as raw text until it sees the corresponding closing tag (the string </title>). That means, for example, that you can't nest titles - <title>This is a <title>inner</title>, tail</title> would resolve to This is a <title>inner as it would cut off RCDATA at the first </title>.
We can't change the tokenization to RCDATA inside <template> for this, because that would mean that the following would be parsed differently based on whether the feature is supported or not:
<template for=title contentmethod="replace-children">
Title
<template id=internal>Nested content</template>
After
</templalte>
In supported browsers the template would stop parsing before the "After" text because it sees a </template>.
An alternative would be to parse the template children normally and then re-serialize and escape the template content but this is a slippery slope of changing parser behavior in an intrusive way.
The same problem applies to textarea, and in some other but similar way to script and style.
With contentmethod you'd have to replace a script/style/title/textarea and you'd essentiallyl need a separate <template for contentmethod=replace> for each of them. With ranges you can replace a whole range of them once.
Other miscellaneous bits of feedback:
- The explainer shows the
markerattribute set on the parent node of ranges to be patched, but it's not clear why this is needed. Could the reason be stated more explicitly? Could this work without the attribute, only the patch ranges?- The script-initiated-patching section mentions
<template contentmethod>, should this say<template for>in the ProcessingInstructions version of the proposal?
the marker attribute has just today been removed from the explainer.
It was there for mXSS prevention reasons that we've later determined are a non-issue.
See also examples for overlaps and nesting.
OpenedAug 12, 2025
Explainer
https://github.com/WICG/declarative-partial-updates/blob/main/patching-explainer.md
The explainer
Where and by whom is the work is being done?
Feedback so far
You should also know that...
No response
<!-- Content below this is maintained by @w3c-tag-bot -->Track conversations at https://tag-github-bot.w3.org/gh/w3ctag/design-reviews/1134