Skip to content

Out of order HTML streaming ("patching") #11542

@noamr

Description

@noamr

What problem are you trying to solve?

When delivering HTML documents, often parts of the document are ready in the server before others. This order might not be the same as the DOM order.

For example, a <select> might appear early in the DOM, with the <option> elements for it fetched from a database in parallel, or 3rd party widgets that have a placeholder that gets filled later.

What solutions exist today?

Some frameworks like React & Svelte offer support for out-of-order streaming. React does so by injecting inline <script> elements that modify the DOM.

How would you solve it?

Suggesting to introduce <!marker> nodes and a marker attribute to define a target/sink/outlet, and extending the <template> element to make it into a patch:

<section marker="more-content">
  <p>Some content</p>
  <!marker>
</section>

<template for="more-content">
  <p>More content</p>
</template>

This replaces just the <!marker> node. <!marker start> and `<!marker end> replaces a range intead:

<section marker="more-content">
  <p>Some content</p>
  <!marker start>
  <p>Loading more content...</p>
  <!marker end>
</section>

<template for="more-content">
  <p>More content</p>
</template>

Additional/supporting APIs

  • Markers can be named for more complex scenarios
  • The outlet lookup is done with the template's parent as the scope, so those patches can't be deeply nested and then target some external element. An exception is that patches that are direct descendants of <body> can target the whole document.
  • In addition, the lookup is tree-scoped and has no affordances for escaping the shadow root.

Gritty details:

  • Scripts that are part of a patch are executed as normal if performed as part of the main parser.
  • Scripts, styles, other RAWTEXT elements (e.g. xmp), <plaintext> and the document (HTML) element cannot be directly streamed into. Markers surrounding such elements can be used to replace the elements instead.
  • From a parser perspective, a setup similar to fragment parsing is set up inside the <template>'s stack of open items. This makes parsing behave like parsing into the context element and into the <template> at the same time. The insertion target is the context element.
  • Conflicts/mismatches are handled as per A way to stream content into an element #2142 (comment)

Potential future enhancements

  • Markers can be implicit for common/simple scenarios
  • Fetching and patching from an external resource (using the existing src attribute))
  • More in the explainer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions