#1134: Incubation: patching (interleaved out-of-order streaming)

Visit on Github.

Opened Aug 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?

  • GitHub repo: https://github.com/WICG/declarative-partial-updates
  • Primary contacts:
    • @noamr, Google
    • @foolip, Google
  • Organization/project driving the design: Google
  • This work is being funded by: Google
  • Incubation and standards groups that have discussed the design:
    • WICG
    • WHATWG
  • Standards group(s) that you expect to discuss and/or adopt this work when it's ready: WHATWG

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

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 patchid used to target patches rather than id, but consider also https://github.com/webplatformco/explainers/blob/main/html-element-references/README.md. How does the scoping of patchid work 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-pending pseudo.
  • 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 pending state 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 the patchid attribute 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 :patching pseudo-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 appendto as 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 load event fires, do active patches block the load event? Or DOMContentsLoaded? 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.

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-pending pseudo.

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 pending state 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 the patchid attribute 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 :patching pseudo-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 appendto as 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 load event fires, do active patches block the load event? Or DOMContentsLoaded? 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
  • 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>

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.