|
1 | | -# Fragment include |
| 1 | +# Fragment Include |
2 | 2 |
|
3 | 3 | ## Overview |
4 | | -The introduction of [patching](https://github.com/WICG/declarative-partial-updates/blob/main/patching-explainer.md) allows HTML to stream out of order declaratively. |
5 | | -While this opens up a lot of new options, this has a few limitations that were said to be addressed as future enhancements: |
6 | 4 |
|
7 | | -1. A patch *always* streams, and there is no declarative way to make it apply in one batch when desired. |
8 | | -2. A patch is interleaved within the original response, requiring the server to multiplex content from different sources. |
9 | | -3. A patch cannot be independently sanitized. It inherits the safety features of its embedder. |
| 5 | +One of the longest-standing design gaps in the HTML standard is the lack of a native, declarative client-side include mechanism (see [WHATWG Issue #2791](https://github.com/whatwg/html/issues/2791)). Historically, developers have relied on Server-Side Includes (SSI), Edge-Side Includes (ESI), or custom JavaScript fetch-and-inject scripts to compose HTML modularly. |
10 | 6 |
|
11 | | -A few relevant missing features in `<template for>` are: |
12 | | -1. Client-side sanitization is only possible when inserting markup using script. HTML is unsafe by default, unless it is JS-inserted. |
13 | | -2. There is no standard way to observe latency and errors when streaming different parts of HTML |
| 7 | +While server-side composition is highly performant for many use cases, it is not always the optimal trade-off: |
| 8 | +1. **Tooling & Multiplexing Complexity:** Forcing the server to multiplex and assemble all page elements on every request increases runtime complexity, especially for auxiliary widgets (e.g., ads, sidebars, related feeds) that could be fetched asynchronously. |
| 9 | +2. **Client-Side Sanitization Gap:** Server-side templating engines do not have access to the browser's native client-side sanitization context. Performing safety filtering at the server level often relies on custom, downstream sanitizers that can easily fall out of sync with browser security updates. |
| 10 | +3. **Ergonomic Dev-Time Modularity:** In modern web development, standard ergonomics favor modular development enablers. Similar to how JS Modules (`import`) allow developers to author modular code while letting build/optimization tooling (like bundlers) handle merging if necessary, HTML needs an equivalent modular primitive. |
| 11 | + |
| 12 | +## Relationship to HTML Patching |
| 13 | + |
| 14 | +The proposed [Declarative Out-of-order streaming specification](https://github.com/WICG/declarative-partial-updates/blob/main/patching-explainer.md) addresses out-of-order streaming of markup. While patching offers a powerful streaming update model, it has several limitations when applied to client-side resource inclusion: |
| 15 | + |
| 16 | +1. **Mandatory Streaming:** Patches always stream directly into the live DOM chunk-by-chunk. There is no declarative way to buffer the template content and render it atomically once parsing completes to avoid layout jank. |
| 17 | +2. **Mandatory Multiplexing:** Patches must be interleaved inside the main HTML response stream, requiring the server to run interleaving and multiplexing logic. |
| 18 | +3. **No Independent Sanitization:** Patches inherit the safety/sanitization permissions of their parent context. They cannot easily be configured to sanitize untrusted content separately. |
14 | 19 |
|
15 | | -## Proposed solution |
16 | 20 |
|
17 | | -See also https://github.com/WICG/webcomponents/issues/645 and https://github.com/whatwg/html/issues/2791 |
| 21 | +## Proposed solution |
18 | 22 |
|
19 | | -Proposing to extend the `<template>` element to support native, client-side HTML includes and dynamic content updates by introducing the `for` attribute and the `src` attribute. |
| 23 | +Proposing to extend the `<template>` element to support native, client-side HTML includes and dynamic content updates by introducing the `active` attribute, as well as fetching attributes (`src`, `crossorigin`, `referrerpolicy`, `nonce`, `integrity`, `blocking`). |
20 | 24 |
|
21 | | -##### 1. Activation Model and Modes |
22 | | -The operational mode of the `<template>` element is explicitly declared and activated using the `active` and `for` attributes: |
23 | | -- **Omitted (Default)**: |
24 | | - Standard HTML `<template>` behavior. The template is parsed inertly into `template.content` and does not render or run scripts. |
25 | | -- **`active` (Token List)**: |
26 | | - Activates the template by accepting a space-separated list of configuration tokens (represented as a `DOMTokenList` in JS): |
| 25 | +### Activation Model and Modes |
| 26 | +The operational mode of the `<template>` element is explicitly declared and activated using the `active` attribute |
| 27 | +which accepts a space-separated list of configuration tokens (represented as a `DOMTokenList` in JS): |
27 | 28 | - **`buffered`**: Parses content into a detached buffer and renders it atomically on stream completion. |
28 | | - - **`unsafe`**: Disables sanitization entirely, allowing full HTML fragment parsing and script execution. |
| 29 | + - **`unsafe`**: Disables sanitization, allowing full HTML fragment parsing and script execution. |
29 | 30 | - **`async`**: Configures the network fetch (`src` present) to be asynchronous and non-blocking relative to document parsing. |
30 | 31 |
|
31 | 32 |
|
32 | | -##### Default `active` Resolution |
| 33 | +#### Default `active` Resolution |
33 | 34 | To determine the template's activation state and delivery/safety modes, the browser resolves the `active` token list using the following ordered algorithm: |
34 | 35 |
|
35 | 36 | 1. **If the `active` attribute is explicitly present**: |
36 | 37 | Use the declared token list (e.g. `active=""` resolves to streaming, sanitized; `active="buffered"` resolves to buffered, sanitized). |
37 | 38 | 2. **Else if the `src` attribute is present**: |
38 | 39 | Resolve the activation state to **`""`** (streaming, sanitized). |
39 | 40 | 3. **Else if the `for` attribute is present**: |
40 | | - Resolve the activation state to **`"unsafe"`** (streaming, unsanitized, matching default patching behavior). |
| 41 | + Resolve the activation state to **`"unsafe"`** (streaming, unsanitized, matching existing "patching" behavior). |
41 | 42 | 4. **Otherwise**: |
42 | 43 | Resolve to **`null`** (inert classic template behavior). |
43 | 44 |
|
@@ -81,18 +82,15 @@ Targeting is controlled via the `for` attribute: |
81 | 82 | </template> |
82 | 83 | ``` |
83 | 84 |
|
| 85 | +During active non-targeted template processing (streaming or buffered), the browser temporarily attaches the `<template>` element to the DOM at its declared position to act as the parser's insertion anchor. Incoming content is parsed and inserted directly **before** the template element. Once parsing/streaming completes (network EOF or closing tag), the template element is detached and removed, leaving **zero DOM footprint** in the final tree. |
84 | 86 |
|
85 | | - |
86 | | -During active template processing (streaming or buffered), the browser temporarily attaches the `<template>` element to the DOM at its declared position to act as the parser's insertion anchor. Incoming content is parsed and inserted directly **before** the template element. Once parsing/streaming completes (network EOF or closing tag), the template element is detached and removed, leaving **zero DOM footprint** in the final tree. |
87 | | - |
88 | | -### 2. Resource Fetching and Script Attributes |
| 87 | +### Resource Fetching and Script Attributes |
89 | 88 | When the `src` attribute is present, the template fetches its HTML payload over the network. |
90 | 89 | - `<template active="async" src="fragment.html"></template>` |
91 | 90 | - Reuses `<script>`'s other network configuration attributes: `blocking`, `nonce`, `crossorigin`, and `referrerpolicy`. Async/non-blocking behavior is controlled directly by the `async` token in the `active` token list. |
92 | | -- Loading or applying a template include creates its own performance entry for latency and error observability. |
93 | 91 |
|
94 | 92 |
|
95 | | -### 3. Buffering vs. Streaming |
| 93 | +### Buffering vs. Streaming |
96 | 94 | - **Streaming (Default active mode)**: |
97 | 95 | If the `buffered` token is absent (e.g. `<template active>`), content is progressively parsed and inserted into the live DOM before the marker/template anchor. |
98 | 96 | - **Buffered (`active="buffered"`)**: |
@@ -124,7 +122,7 @@ When the `src` attribute is present, the template fetches its HTML payload over |
124 | 122 | <template for="comments-patch" active="buffered" src="comments.html"></template> |
125 | 123 | ``` |
126 | 124 |
|
127 | | -### 4. Security & Sanitization |
| 125 | +### Security & Sanitization |
128 | 126 | - **Explicitly Activated (`active` present)**: **Sanitized by default**. To disable sanitization and execute scripts, include the `unsafe` token in the `active` token list. |
129 | 127 | - **Implicitly Activated (`for` present, `active` omitted)**: **Unsafe by default** (implicitly resolves to `active="unsafe"`), matching standard patching behavior. |
130 | 128 |
|
@@ -185,7 +183,6 @@ An alternative is introducing a new bespoke element specifically for in-place or |
185 | 183 | 1. **HTML Parser Foster-parenting:** Unrecognized/custom elements (and standard layout elements that aren't specific table components) are subject to foster-parenting by the HTML parser. Placing `<fragment>` inside a table (e.g. `<table><tbody><fragment src="rows.html"></fragment></tbody></table>`) will cause the parser to throw it outside the table structure, breaking streaming updates for tables. Modifying HTML parsing table rules is a non-starter due to cross-browser backward compatibility. |
186 | 184 | 2. **Layout Footprint:** Keeping `<fragment>` in the DOM (even with `display: contents`) pollutes the sibling structure, breaking CSS selectors like `:first-child`, `:nth-child`, and sibling combinators (`+`/`~`), as well as JS DOM traversal APIs (`nextSibling`). |
187 | 185 |
|
188 | | - |
189 | 186 | ### 2. Extending the `<script>` element |
190 | 187 | Another alternative is using `<script>` to fetch and render markup: |
191 | 188 | - `<script type="text/html" src="fragment.html"></script>` |
|
0 commit comments