<template src>#dpu-nextTPAC 2026
Dublin, Ireland & online
26–30 October 2026
<iframe seamless> and <link rel="import"> — not
really itwhatwg/html#2791 — 280+ comments, 225+ 👍Including one piece of HTML inside another is one of the oldest requests on the web. SSI and PHP were invented for it in 1995. On the client, <iframe seamless> had heavy Window/Document overhead and style/security issues, and HTML Imports loaded an inert document that still needed JS to stamp DOM. Meanwhile whatwg/html#2791 has been active since 2017.
“PHP was literally created to solve this problem… Imagine if the client could cache the entire header and footer and only need to download the main content.”
— @grepsedawk (
whatwg/html#2791)
“DRY HTML authoring without running code server-side or requiring JS. It's actually what I naively expected HTML imports to do at first.”
— @brandondees (74 👍) · GitHub has used
<include-fragment>in prod for 10+ years (@josh)
Developers in whatwg/html#2791 repeatedly highlight both the beginner/static-site ergonomics and the caching win of client-side includes. GitHub has used its <include-fragment> custom element in production for over a decade as a client-evaluated <esi:include>.
Client-side includes unlock four main benefits: buildless DRY HTML on static hosts without a server or build step; split caching between shared chrome or CDN-cached shells and dynamic user islands; loading async subsections (comments, feeds, recommendations) without delaying the main page; and composing fragments across separate services with native sanitization instead of iframes.
Client-side includes stalled on three core problems: synchronous fetching created parser-blocking network waterfalls, asynchronous fetching caused FOUC and layout shifts, and injecting fetched HTML carried XSS risk without a native sanitizer.
<template for>buffer (see later...)Each of those three blockers now has a platform primitive: out-of-order streaming (<template for>) decouples fetching from parser position, rel="expect" and buffer prevent FOUC and manage layout shifts, and the Sanitizer API makes external markup safe by default.
<template for src>Streams from a URL into a location in the DOM
Building directly on out-of-order streaming (<template for="name">), adding a src attribute fetches an external HTML fragment and streams it into the matching <?start>...<?end> marker range.
DOMContentLoaded but not the parserFirst mitigation: instead of blocking the HTML parser on a synchronous fetch, <template for src> fetches asynchronously (delaying DOMContentLoaded like <script defer>) so fetches can start early while the parser renders placeholders and <footer> immediately.
Second mitigation (part 1): unlike media subresources such as <img src> (where rel="expect" only waits for the tag to be parsed, not fetched), <template for src> expands into DOM structure—so expecting #content also waits for any client-side includes preceding #content in tree order. The HTML parser continues through <main> without blocking, and header + main paint together.
Second mitigation (part 2): for async fragments that shouldn't block initial page render, adding buffer parses the response into the template and swaps the placeholder atomically when ready.
buffer Works in Practice<template> stays in the DOM while receiving content<template><template> contents to the destination and removes the
<template>In practice, a buffered <template> isn't consumed immediately on its start tag: it stays in the DOM while chunks stream into its inert template contents, and once complete, its contents are moved into the target destination and the <template> element is removed.
buffer on inline HTMLBecause buffer is a general <template> primitive rather than a fetch-only flag, you can also use <template for="comments" buffer> on the incoming main HTML stream so streamed patches swap in atomically.
buffer Waits for SubresourcesWhile buffering into the template, the speculative preload scanner initiates subresource fetches. The atomic swap waits for external stylesheets, <link rel="preload">, and any internal <template for src> includes inside the buffered template to finish.
src is sanitized by default (removeUnsafe)sanitize="unsafe" opts outThird mitigation: external includes (<template for src>) are sanitized by default using the browser's built-in Sanitizer API, which strips scripts, inline event handlers, and nested <template for> (preventing slot-hijacking or recursive includes).
forfor: Patch "in place" of the templatesrc and buffer without PIsWhen you don't need out-of-order targeting, an empty for attribute (<template for src="header.html">, <template for buffer>, <template for sanitize>) acts in place—replacing the <template> element itself without needing <?start>/<?end> markers, and composing cleanly inside <template shadowrootmode="open">.
MutationObserver)activeTemplateAttributes: filtered <template> attributesactiveTemplateStatus: "start" | "update" | "end" | "abort" (e.g. fetch
error / truncated stream)Because <template for> is consumed by the parser and never enters the DOM, how does JS observe patch metadata or recover from a failed src fetch / truncated stream? Opting into activeTemplateAttributeFilter in MutationObserver.observe() populates record.activeTemplateAttributes and record.activeTemplateStatus ("start", "update", "end", "abort").
<template src> always uses CORS and text/htmlfragment-src, falls back to script-srcAdditional details: <template src> is always CORS; CSP adds fragment-src (falling back to default-src) while script-src still governs sanitize="unsafe"; relative URLs inside fragments resolve against document.baseURI; and external SVGs can be included inline with CSS currentColor styling and built-in sanitization.
<template>?<script>, can be used with inline contentA new <fragment> element is rejected because <template> already has the self-consuming "disappear from the DOM" behavior and is valid everywhere in the HTML parser without foster-parenting.
Summary of all the new <template> attributes in one place: for (named slot or empty for in-place), src (external CORS fetch), buffer (atomic swap after stylesheets/preloads/nested includes), and sanitize (safe by default with Sanitizer API; sanitize="unsafe" opts out).
<template> vs. a new elementbuffer wait for?for shorthand (<template for src>)404 / 500)Try it: Chrome Canary flag · WICG/declarative-partial-updates
Open the floor for feedback on <template src>, buffer, sanitize, and rel="expect" integration.
Quickly highlight sibling developments in WICG/declarative-partial-updates: progressive shells (<?shellboundary>), DOM preservation/merging (preserve attribute for islands inside patched ranges), and parser-aware MutationObserver. Invite everyone to the Seamless Navigation breakout on Wednesday at 9:00 and the WHATWG session.
Explainer: https://github.com/WICG/declarative-partial-updates/blob/main/fragment-include-explainer.md. Covers extending <template> with src, buffer, and sanitize to solve WHATWG HTML issue #2791.