宣言型部分更新

公開日: 2026 年 5 月 19 日、最終更新日: 2026 年 9 月 8 日

ウェブは、静的なドキュメント主導のメディアとして始まった当初から大きく変化しました。最新の機能豊富なウェブアプリは、コミュニケーション、購入、リッチ コンテンツの消費、複雑な生活の管理など、さまざまな理由で多くの人に利用されています。

HTML は、さまざまな進歩を遂げたにもかかわらず、コンテンツの準備ができたタイミングやユーザーがコンテンツを消費するタイミングをほとんど考慮せずに、上から下へと順番に配信されます。CSS を使用するとコンテンツの順序を変更できますが、アクセシビリティに大きな副作用が生じることがよくあります。JavaScript では、さまざまな API を介して DOM を操作することで、この制約からある程度解放されますが、多くの場合、冗長な構文が必要になったり、HTML に接続するための DOM ツリーの構築が必要になったりします。

ウェブはクライアント サーバー型のメディアであるため、パフォーマンスは非常に重要です。しかし、HTML の順序性を回避するために最適ではない選択がなされることが多く、パフォーマンスが低下します。これには、ページ全体が準備できるまで待つことや、重いフレームワークを使用してコンポーネントを非同期で配信することが含まれます。JavaScript フレームワークの人気は、ウェブ デベロッパーがウェブの起源の厳格なドキュメントのメンタルモデルではなく、コンポーネント ベースのモデルを好んでいることを示しています。

Chrome チームはこの問題を検討し、宣言型部分更新という名前でウェブ プラットフォームに新しい機能を追加しています。

最初の 2 つの新しい API セットを使用すると、HTML ドキュメント自体が順不同であるか、新しい JavaScript API を使用して既存のドキュメントに HTML を動的に挿入する簡単な方法を使用しているかに関係なく、HTML をより線形でない方法で簡単に配信できます。ポリフィルも利用できるため、まだサポートしていないブラウザでも、これらの新しい API をすぐに使用できます。

順不同ストリーミング

Browser Support

  • Chrome: 150.
  • Edge: 150.
  • Firefox: not supported.
  • Safari: not supported.

Source

最初の変更セットは、処理命令プレースホルダと for 属性を持つ <template> HTML 要素を使用する新しい順不同ストリーミング API です。次に例を示します。

<div>
  <?marker name="placeholder">
</div>

...

<template for="placeholder">
  Here is some <em>HTML content</em>!
</template>

処理命令は XML には以前から存在していましたが、HTML ではコメントとして扱われ、無視されていました。この新しい API は、この状況を変え、HTML に処理命令をもたらします。たとえば、ブラウザが <?marker name="placeholder"> 処理命令を認識しても、以前と同様にすぐには何も行いませんが、後で参照できます。

for 属性を持つ <template> 要素は、name 属性を持つ対応する処理命令を検索し、コンテンツを置き換えます。この場合、解析後の DOM は次のようになります(空白文字の違いは無視します)。

<div>
  Here is some <em>HTML content</em>!
</div>

置換用の <?marker> 属性の他に、テンプレートが処理される前に一時的なプレースホルダ コンテンツを表示できる <?start><?end> の範囲マーカーもあります。

<div>
  <?start name="another-placeholder">
  Loading…
  <?end>
</div>

...

<template for="another-placeholder">
  Here is some <em>HTML content</em>!
</template>

この場合、<template> が表示されるまで Loading… 文字列が表示され、その後、新しいコンテンツに置き換えられます。

テンプレートに処理手順を含めて、複数の更新を許可することもできます。

<ul id="results">
  <?start name="results">
  Loading…
  <?end>
</ul>

...

<template for="results">
  <li>Result One</li>
  <?marker name="results">
</template>

...

<template for="results">
  <li>Result Two</li>
  <?marker name="results">
</template>

...

解析と処理の後、最終的に次の HTML になります。

<ul id="results">
  <li>Result One</li>
  <li>Result Two</li>
  <?marker name="results">
</ul>

最後に最終処理の指示を記述します。これは、後でドキュメントに <template for="results"> プレースホルダが追加された場合に備えたものです。

標準の HTML 要素ではなく処理命令を使用する理由

これは、この API を初めて使用するユーザーからよく寄せられる質問です。<slot> 要素や <template> 要素自体を使用することを提案します。この提案の初期バージョンは標準の HTML 要素で動作していましたが、設計の反復処理が進むにつれて処理命令に切り替えられました。処理命令により、DOM に影響を与えることなくパッチを適用できます。これにより、<head><title> の更新など)で使用したり、<table> などの他の要素内(オプションの追加行を追加するなど)で使用したりできます。

多くのウェブ デベロッパーにとって構文はなじみのないものですが、処理命令は後方互換性の問題が発生する可能性が低く、はるかに柔軟性が高くなっています。この提案により、XML で使用されていたものが HTML 標準の一部になりました

デモ

この動画では、ストリーミング HTML を使用して基本的なフォトアルバム アプリケーションを実装しています。

順序どおりでないストリーミングで実装されたフォトアルバムのデモ(ソース

ステータスと写真は、初期レイアウト後に HTML にストリーミングされます。

ユースケース

ストリーミング HTML と組み合わせると、この順序外の HTML パッチ適用には多くのユースケースがあります。

  • アイランド アーキテクチャ。Astro などのフレームワークで普及した一般的なパターンであるアイランド アーキテクチャでは、コンポーネントが静的 HTML の上で個別にハイドレーションされます。<template for> API を使用すると、静的コンテンツを HTML で直接同様に処理できます。JavaScript フレームワークは、よりインタラクティブなアイランドやコンポーネントの処理にもこれを使用できます。
  • 準備ができたらコンテンツを配信します。このアイランド アーキテクチャにより、コンテンツは準備ができ次第ストリーミングできます。データベース検索など、追加の処理が必要なコンテンツのために遅延することはありません。多くのプラットフォームで HTML のストリーミングが許可されていますが、HTML の順序付けの性質上、コンテンツが遅延したり、複雑な JavaScript DOM 操作に頼らざるを得ないことがよくあります。これで、待機中に静的コンテンツを配信し、HTML ストリームの最後に高価な動的コンテンツを挿入できます。
  • HTML をページの読み込みパフォーマンスに最適な順序で配信できます。さらに、準備が整った後でも注文を変更できます。たとえば、メガメニューは一般的なナビゲーション機能で、ページがインタラクティブになるまでユーザーに表示されない HTML が多く含まれています。この大きな HTML のチャンクは、HTML ドキュメントの後半で配信して、初期ページの読み込みに必要なより重要な HTML を優先させることができます。HTML では順序は障壁になりません。

これらはユースケースの一部にすぎません。デベロッパーがこの新しい API をどのように活用するのか、楽しみにしています。

制限事項と注意点

この API には、注意すべき制限事項と微妙な違いがいくつかあります。

  • セキュリティ上の理由から、<template for> は同じ親要素内の処理命令のみを更新できます。<template for><body> 要素に直接追加すると、ドキュメント全体(<head> を含む)にアクセスできるようになります。
  • <?end> 処理命令は省略可能です。省略した場合、<?start> 要素と包含要素の末尾の間のコンテンツが置き換えられます。
  • <template for> のストリーミング開始後に処理手順を移動すると、新しいコンテンツが古い場所にストリーミングされ続けるという予期しない結果が生じる可能性があります。
  • setHTML などのメソッドや innerHTML プロパティを使用して <template for> を動的に挿入する場合、解析時のテンプレートの「親」は中間ドキュメント フラグメントになります。つまり、これらのメソッドで HTML を挿入しても既存の DOM は変更されず、パッチ適用はフラグメント内で「インプレース」で行われます。ただし、streamHTMLUnsafe などのメソッド(後述)を使用してストリーミングする場合、中間フラグメントがないため、テンプレートで既存のコンテンツを置き換えることができます。

標準化のステータス

<template for> 属性は HTML 標準の一部ですが、まだすべてのブラウザでサポートされているわけではありません。

今後追加される可能性のある項目

今後追加される可能性のある機能としては、次のようなものが検討されています。

  • クライアントサイドのインクルード。たとえば、<template for="footer" src="/partials/footer.html"> や、パッチを適用していない <template src="/partials/footer.html"> などです。詳しくは、解説をご覧ください。これは chrome://flags/#enable-experimental-web-platform-features フラグの背後で使用できます。
  • 変更されないコンテンツの上書きを防ぐ。これは、コンテンツの改訂番号またはバージョン管理で実現できます。これにより、コンテンツをリセットするのではなく、ルートの変更やその他の更新の間で状態を維持できます。
  • パッチ適用中のサニタイズ。例: <template for=icon safe><svg id="from-untrusted-source">...</svg></template>

ポリフィル

Chrome チームは、他のブラウザにこの機能が実装される前からサイトでこの新機能をすぐに利用できるように、template-for-polyfill をリリースしました。npm で入手できます

ブラウザの HTML パーサーを直接更新できないなど、いくつかの制限がありますが、最も一般的なユースケースはカバーされています。サイトは他のブラウザでもテストする必要があります。

HTML の挿入とストリーミングの方法を更新

すべてのコンテンツを HTML として配信できるわけではありません。この分野で Chrome が行った作業の 2 つ目の部分は、JavaScript でコンテンツを簡単に更新できるようにすることを目的としています。

JavaScript を使用して既存のドキュメントに HTML を動的に挿入する方法はすでに複数あります。

  • setHTML
  • setHTMLUnsafe
  • innerHTMLouterHTML
  • createContextualFragment
  • insertAdjacentHTML

ただし、これらの関数はすべて動作がわずかに異なり、開発者が常に考慮するとは限らない微妙な違いがあります。

  • 新しいコンテンツは上書きされますか、それとも追加されますか?
  • 危険な可能性のある HTML をサニタイズしますか(たとえば、<script> タグをエスケープするなど)。
  • そうでない場合、<script> を実行する必要がありますか?
  • Trusted Types との連携

これらの API を見て、それぞれの質問に自信を持って答えられるデベロッパーはほとんどいません。

大きな制限事項として、事前に認識されている HTML の完全なセットに対してのみ使用できることが挙げられます。これは、HTML のストリーミングを許可する呼び出しが行われた場合です。実際には、HTML の強みの一つであるコンテンツの即時ストリーミング機能が使えなくなるため、コンテンツを挿入する前にコンテンツ全体をダウンロードする必要があります。ペイロードを分割したり、document.write などのハック的な非推奨メソッドを使用したりすることで、限定的に回避できますが、それぞれに問題が生じます。

静的 API とストリーミング API の新しいセット

Browser Support

  • Chrome: behind a flag.
  • Edge: behind a flag.
  • Firefox: not supported.
  • Safari: not supported.

Chrome は、この問題を解決し、ストリーミング機能を導入するために、一連の新しい API と既存の setHTMLsetHTMLUnsafe の拡張機能に取り組んできました。

Chrome 148 以降では chrome://flags/#enable-experimental-web-platform-features フラグを使用してデベロッパー テストを実施できます。Chrome 155 でリリースされる予定です。

設定または置換するメソッドと、既存の HTML の前後にコンテンツを挿入するメソッドがあります。各メソッドにはストリームの同等のメソッドがあります。

アクション 静的 ストリーミング
要素の HTML コンテンツを設定する setHTML(html, options); streamHTML(options);
要素全体をこの HTML に置き換えます replaceWithHTML(html, options); streamReplaceWithHTML(options);
要素の前に HTML を追加する beforeHTML(html, options); streamBeforeHTML(options);
HTML を要素の最初の子として追加する prependHTML(html, options); streamPrependHTML(options);
HTML を要素の最後の子として追加する appendHTML(html, options); streamAppendHTML(options);
要素の後に HTML を追加する afterHTML(html, options); streamAfterHTML(options);
新しい挿入メソッドとストリーミング メソッド

Unsafe バージョンもありますが、これについては後ほど説明します。特に Unsafe の同等のメソッドを追加すると、メソッドの数が多く見えるかもしれませんが、一貫した命名規則により、前述の関連性のないメソッドよりも、各メソッドの機能が明確になります。

静的バージョンでは、新しい HTML を DOM 文字列引数として受け取り、オプションのオプションも受け取ります。

const newHTML = "<p>This is a new paragraph</p>";
const contentElement = document.querySelector('#content-to-update');

contentElement.setHTML(newHTML);

ストリーミング バージョンは、getWriter() などの Streams API で動作します。

const contentElement = document.querySelector('#content-to-update');
const writer = contentElement.streamHTMLUnsafe().getWriter();

// Example stream of updating content
while (true) {
  await writer.write(`<p>${++i}</p>`);
  await new Promise((resolve) => setTimeout(resolve, 1000));
}

writer.close();

または、パイプチェーンを使用してフェッチ レスポンスから取得します。

const contentElement = document.querySelector('#content-to-update');
const response = await fetch('/api/content.html');

response.body
  .pipeThrough(new TextDecoderStream())
  .pipeTo(contentElement.streamHTMLUnsafe());

textStream() コンビニエンス メソッド

Browser Support

  • Chrome: 151.
  • Edge: 151.
  • Firefox: not supported.
  • Safari: not supported.

また、中間 TextDecoderStream() ステップを必要とせずに直接ストリーミングできる textStream コンビニエンス メソッドも追加されました。

const contentElement = document.querySelector('#content-to-update');
const response = await fetch('/api/content.html');

response.textStream().pipeTo(contentElement.streamHTMLUnsafe());

options

options 引数を使用すると、カスタム sanitizer を指定できます。デフォルトは defaultデフォルトのサニタイザー構成)です。次のように使用します。

const newHTML = '<p>This is a new paragraph</p>';
const contentElement = document.querySelector('#content-to-update');

// Only allows basic formatting
const basicFormattingSanitzer = new Sanitizer({ elements: ['em', 'i', 'b', 'strong'] });

contentElement.setHTML(newHTML, {sanitizer: basicFormattingSanitzer});

「安全でない」メソッド

各 API には「安全でない」バージョンもあります。

アクション 静的 ストリーミング
要素の HTML コンテンツを設定する setHTMLUnsafe(html,options); streamHTMLUnsafe(options);
要素全体をこの HTML に置き換えます replaceWithHTMLUnsafe(html, options); streamReplaceWithHTMLUnsafe(options);
要素の前に HTML を追加する beforeHTMLUnsafe(html, options); streamBeforeHTMLUnsafe(options);
HTML を要素の最初の子として追加する prependHTMLUnsafe(html, options); streamPrependHTMLUnsafe(options);
HTML を要素の最後の子として追加する appendHTMLUnsafe(html, options); streamAppendHTMLUnsafe(options);
要素の後に HTML を追加する afterHTMLUnsafe(html, options); streamAfterHTMLUnsafe(options);
「安全でない」挿入とストリーミングの方法

これらの「安全でない」メソッドは、デフォルトでサニタイザーをオフにします。必要に応じて、カスタム サニタイザーを指定できます。これらのメソッドでは、スクリプトをオプションの runScripts オプション(デフォルトは false)とともに実行することもできます。

setHTML と同様に、setHTMLUnsafe は既存のメソッドですが、スクリプト実行で使用できるように runScripts オプション パラメータが追加されています。

const newHTML = `<p>This is a new paragraph</p>
                 <script src=script.js></script>`;
const contentElement = document.querySelector('#content-to-update');

contentElement.setHTMLUnsafe(newHTML, {runScripts: true});

メソッドの「安全でない」という文言は、これらのメソッドを使用すべきではないという意味ではなく、潜在的なリスクと、スクリプトをサニタイズまたは制限する方法をデベロッパーに思い出させるためのものです。

この「安全でない」の程度は、入力の信頼性によって異なります。Unsafe 静的メソッドはすべて、html 引数として DOM 文字列または TrustedHTML の両方で動作し、サニタイザーの使用も許可します。ただし、runScript の目的はスクリプトを許可することであるため、デフォルトではサニタイザーは使用されません。

ユースケース

これらの新しい API により、デベロッパーは既存のページに HTML を簡単に追加し、一貫した名前とオプションで新しい API を追加できます。ストリーミング API を使用すると、新しいコンテンツがすべてプラットフォームで利用可能になるまで待つ必要がないため、パフォーマンス上のメリットが得られます。

次のようなユースケースがあります。

  • シングルページ アプリでの大規模なコンテンツ更新の動的ストリーミング。前述のように、現在の SPA アーキテクチャの大きな欠点は、初期 HTML ロードのストリーミングのメリットを享受できないことです。
  • HTML フッターなどの共通コンテンツを挿入する。JavaScript API を使用すると、パーシャルをプルしてページに挿入できます。これにより、送信されるすべてのページでパーシャルを繰り返すのではなく、キャッシュ保存のメリットを活かすことができます。ただし、実行に JavaScript が必要になるため、これは初期読み込みで表示されないコンテンツにのみ使用してください。

これらはほんの一例にすぎません。皆様のアイデアを楽しみにしています。

制限事項と注意点

これらの新しい API には、注意すべき制限事項と微妙な点もいくつかあります。

  • Trusted Types API とのストリーミングの統合には、新しい createParserOptions メソッドを使用する必要があります。このメソッドを使用すると、サニタイザーを任意の HTML 設定オペレーションに挿入できます。信頼できる型の統合の詳細については、解説をご覧ください
  • <template for> と同様に、ストリーミング中の要素を移動すると、予期しない結果やストリーム エラーが発生する可能性があります。
  • streamHTMLUnsafe は、<template for> 命令がメイン ドキュメントに追加されると処理し、defer スクリプトをストリームの最後まで遅延させるなど、多くの点でメイン パーサーのように動作します。

標準化のステータス

新しい挿入メソッドとストリーミング メソッドは HTML 標準に追加されるプロセスにありますが、まだすべてのブラウザでサポートされているわけではありません。

ポリフィル

Chrome チームは、他のブラウザにこの機能が実装される前からサイトでこの新機能をすぐに利用できるように、html-setters-polyfill をリリースしました。npm で入手できます

このポリフィルはストリーミングを行わず、完了時にバッファリングして適用します。これは、機能というよりも API の形状のポリフィルです。

また、安全なコンテンツの設定は setHTMLSanitizer API に依存しますが、Safari ではサポートされていません。

両方を併用する

これらは 2 つの別々の API ですが、組み合わせることで真価を発揮します。新しい <template for> 要素を HTML にストリーミングすることで、DOM への個別の JavaScript 参照でそれぞれを直接ターゲットにすることなく、コンテンツのさまざまな部分を動的に更新できます。

基本的な SPA スタイルのページ読み込みは、処理手順を含むアウトライン ページを読み込み、各新しいページのテンプレートを HTML の下部にストリーミングして、処理手順にスロットインすることで実装できます。

これらの API には、間違いなくさらなる可能性とユースケースが存在します。Google の(限られた)想像力に制限されることなく、部分的な更新を管理しやすくすることで、ボイラープレート コードを減らし、更新を容易にし、ウェブの新たな可能性を切り開くことができます。

Chrome では、宣言型部分更新プロジェクトの下で、他にもいくつかの API を開発していますが、まずはこの 2 つの API を皆様にお届けできることを楽しみにしています。新しい情報がわかり次第、随時お知らせいたします。