HTMX 4.0がやってくる!「The fetch()ening」完全解説 — 2.xからのマイグレーションと新機能ガイド
「HTMX 3.0は出ないって言ってたじゃないか!」
そう思った方も多いかもしれません。実際、HTMXの作者であるCarson Gross氏は「HTMX 3.0は出ない」と明言していました。しかし彼は2025年11月に「4.0が出ないとは言っていない」というジョークとともに、HTMX 4.0 のアルファ版を発表し、現在はベータ版(v4.0.0-beta2)まで進んでいます。
今回の4.0アップデートは単なる機能追加にとどまらず、内部アーキテクチャの根本的な刷新を伴う非常に大きな変更です。公式はこれを "The fetch()ening" と呼んでいます。
本記事では、公式ドキュメントとマイグレーションガイドをもとに、HTMX 4.0の全貌を日本語で解説します。記法の変更例・設定項目一覧・コマンド集・Ubuntu環境でのパフォーマンス比較まで、できるだけ網羅的にまとめました。
1. なぜ「4.0」なのか? ─ "The fetch()ening" の意味
HTMX 4.0の最大の変更点は、通信のコアが長年使われてきた XMLHttpRequest(XHR)から、モダンな fetch() API へと完全に置き換えられたことです。
All requests use the native fetch() API. This cannot be reverted.
─ HTMX 4.0 Migration Guide
なぜ今 fetch() に移行するのか?
HTMX 1.0時代からのレガシーであるIEサポートのために残されていた XMLHttpRequest ですが、fetch() に移行することで以下のメリットが得られます。
1. Readable Streams のサポート
単一のレスポンスを待つのではなく、コンテンツのストリームをDOMに順次スワップすることが可能になります。AIチャットのような「少しずつ表示される」UIをJavaScriptなしで実現できます。
2. SSE(Server-Sent Events)のコア統合
HTMX 2.0では拡張機能(Extension)に分離されていたSSEが、ストリーミング対応の恩恵を受けて再びコア機能として統合されました。
3. コードベースの大幅な簡素化
複雑な非同期処理が整理され、ライブラリ全体のサイズが大幅に削減されました。
2. Ubuntu環境でのパフォーマンス比較
「内部が刷新されたなら、パフォーマンスはどう変わったの?」という疑問に答えるため、Ubuntu 22.04環境(Node.js v22.13.0)で実際に計測しました。
ファイルサイズの大幅な削減
fetch() への移行とコードの整理により、ファイルサイズが劇的に小さくなっています。
| バージョン | 生サイズ (Raw) | Gzip圧縮時 | 削減率 |
|---|---|---|---|
| HTMX 2.x (v2.0.9) | 50.1 KB | 16.2 KB | ─ |
| HTMX 4.0 (beta2) | 33.3 KB | 11.6 KB | ▼33.5% |
初期ロード時のネットワーク転送量が約33%削減されます。特にモバイル回線では体感できるレベルの改善です。
JavaScriptパース時間の改善
ファイルサイズが小さくなったことで、V8エンジンがJavaScriptをパースして実行可能になるまでの時間も短縮されています(100回計測、ウォームアップあり)。
| バージョン | 平均 (Avg) | 中央値 (p50) | p95 |
|---|---|---|---|
| HTMX 2.x | 0.024 ms | 0.006 ms | 0.080 ms |
| HTMX 4.0 | 0.020 ms | 0.005 ms | 0.032 ms |
p95(遅いケース)において約60%の改善が見られました。
XHR vs Fetch API のスループット比較
ローカルサーバーに対して並列リクエスト(並列数10、合計500リクエスト)を行った結果です。
| APIの種類 | 処理時間 | スループット |
|---|---|---|
| XHR(htmx 2.x相当) | 1036 ms | 483 req/s |
| Fetch(htmx 4.0相当) | 1025 ms | 488 req/s |
並列処理では両者ほぼ同等でした。なお、今回の計測はNode.js環境(undiciベースのfetch)であり、実際のブラウザ環境ではfetch APIの方が最適化されているケースが多いため、体感パフォーマンスはさらに向上すると期待できます。
3. インストール方法
CDN(最速)
<!-- 推奨: minified版 -->
<script src="https://cdn.jsdelivr.net/npm/htmx.org@4.0.0-beta2"
integrity="sha384-v+EMKtNUAo5enmQxBqgoU/FWvVvvZHvITNzurHSl4kzvCs94wdlgHUci1lliKWKz"
crossorigin="anonymous"></script>
<!-- ES Module版 -->
<script type="module"
src="https://cdn.jsdelivr.net/npm/htmx.org@4.0.0-beta2/dist/htmx.esm.min.js"
integrity="sha384-imtDMsKpIb5KDnuaceZPNUtGHemw6nZSQTAcVNpfegk47oSVnMbe0dp6civ/SA4s"
crossorigin="anonymous"></script>
npm
npm install htmx.org@4.0.0-beta2
import 'htmx.org';
// または名前付きインポート
import htmx from 'htmx.org';
htmax.js(新登場!バンドル版)
HTMX 4.0では htmax.js という新しいバンドルファイルが登場しました。以下の人気拡張機能をすべて含んだオールインワンファイルです。
- SSE(Server-Sent Events)
- WebSockets
- preload
- browser-indicator
- download
- optimistic
- targets
<!-- 拡張機能の属性をそのまま使える -->
<script src="/js/htmax.min.js"></script>
<div hx-sse:connect="/events">...</div>
4. 記法の変更点(Breaking Changes)
HTMX 4.0では、より予測可能で堅牢な動作を目指して、いくつかの破壊的変更が行われています。
① 属性の継承が「明示的」に変わった
HTMX 2.0の最大の罠とも言えた「暗黙の属性継承」が廃止されました。4.0では、親要素から属性を継承させたい場合、:inherited 修飾子を明示的につける必要があります。
<!-- HTMX 2.x: 暗黙の継承(下のbuttonもhx-confirmを引き継ぐ) -->
<div hx-confirm="本当に削除しますか?">
<button hx-delete="/item/1">削除</button>
</div>
<!-- HTMX 4.0: 明示的な継承が必要 -->
<div hx-confirm:inherited="本当に削除しますか?">
<button hx-delete="/item/1">削除</button>
</div>
:inherited はすべての属性に使えます(hx-boost:inherited、hx-target:inherited、hx-headers:inherited など)。
さらに、継承した値に追記したい場合は :append も使えます。
<div hx-include:inherited="#global-fields">
<!-- .extra を追記して継承 -->
<form hx-include:inherited:append=".extra">...</form>
</div>
2.xの挙動に戻したい場合は設定で対応できます。
htmx.config.implicitInheritance = true;
② エラーレスポンス(4xx, 5xx)もスワップされるように
HTMX 2.0では、サーバーが 400 や 500 のエラーステータスを返した場合、デフォルトではDOMのスワップが行われませんでした。HTMX 4.0では、204 と 304 以外のすべてのレスポンスがスワップされます。
<!-- 4.0: 422が返ってきたらエラーメッセージをスワップ -->
<form hx-post="/submit" hx-target="#result">
...
</form>
<div id="result"></div>
サーバー側でエラーメッセージを含むHTMLを返す設計が基本となります。2.xの挙動に戻す場合は以下の設定を使います。
htmx.config.noSwap = [204, 304, '4xx', '5xx'];
③ hx-delete がフォームデータを含まなくなった
hx-get と同様に、hx-delete は囲んでいるフォームの入力値を含まなくなりました。
<!-- 2.x: フォームデータが自動的に含まれていた -->
<!-- 4.0: 明示的に指定が必要 -->
<button hx-delete="/item/1" hx-include="closest form">削除</button>
④ 履歴キャッシュ(localStorage)の廃止
「戻る」ボタンを押した際の挙動が変わります。2.0ではDOMのスナップショットを localStorage に保存していましたが、状態の不整合やセキュリティの懸念から廃止されました。4.0では、履歴ナビゲーション時にネットワークリクエストを発行してコンテンツを再取得します。
// フルリロードにしたい場合
htmx.config.history = "reload";
// 履歴機能を無効化したい場合
htmx.config.history = false;
⑤ OOBスワップの順序が変わった
HTMX 2.0では hx-swap-oob 要素がメインコンテンツより先にスワップされていましたが、4.0ではメインコンテンツが先にスワップされ、OOBと <hx-partial> はその後(ドキュメント順)にスワップされます。
⑥ デフォルトタイムアウトが60秒に
HTMX 2.0ではタイムアウトが無制限(0)でしたが、4.0ではデフォルトで 60秒 に設定されました。
// 2.xの挙動(タイムアウトなし)に戻す場合
htmx.config.defaultTimeout = 0;
5. 属性名・イベント名の変更一覧
廃止・リネームされた属性
| 2.x の属性 | 4.0 での対応 |
|---|---|
hx-disable(処理スキップ) |
hx-ignore に改名 |
hx-disabled-elt(要素無効化) |
hx-disable に改名 |
hx-vars |
hx-vals の js: プレフィックスを使用 |
hx-prompt |
hx-confirm の js: プレフィックスを使用 |
hx-ext |
拡張機能スクリプトを直接読み込む |
hx-disinherit |
継承が明示的になったため不要 |
hx-inherit |
継承が明示的になったため不要 |
hx-request |
hx-config を使用 |
hx-history |
廃止(localStorage廃止のため) |
hx-history-elt |
廃止 |
hx-params |
htmx:config:request イベントを使用 |
イベント名の変更(主要なもの)
すべてのイベントが htmx:phase:action[:sub-action] の形式に統一されました。また、すべてのエラーイベントが htmx:error に統合されました。
| 2.x のイベント名 | 4.0 のイベント名 |
|---|---|
htmx:afterOnLoad |
htmx:after:init |
htmx:afterRequest |
htmx:after:request |
htmx:afterSwap |
htmx:after:swap |
htmx:beforeRequest |
htmx:before:request |
htmx:beforeSwap |
htmx:before:swap |
htmx:configRequest |
htmx:config:request |
htmx:responseError |
htmx:error |
htmx:sendError |
htmx:error |
htmx:timeout |
htmx:error |
htmx:pushedIntoHistory |
htmx:after:history:push |
リクエスト/レスポンスヘッダーの変更
| 2.x | 4.0 | 備考 |
|---|---|---|
HX-Trigger |
HX-Source |
フォーマットが tagName#id に変更 |
HX-Target |
HX-Target |
フォーマットが tagName#id に変更 |
HX-Trigger-Name |
廃止 |
HX-Source を使用 |
HX-Prompt |
廃止 |
hx-confirm の js: プレフィックスを使用 |
| (新規) | HX-Request-Type |
"full" または "partial"
|
| (新規) | Accept |
text/html を明示的に送信 |
6. 新機能の紹介
hx-swap に追加された新しいスワップスタイル
HTMX 4.0では hx-swap に新しいスタイルが追加されました。
innerMorph / outerMorph(DOMモーフィング)
これまで idiomorph 拡張機能として提供されていた賢いDOM差分更新アルゴリズムが、コア機能として組み込まれました。フォームの入力値やフォーカス状態を保持したままDOMを更新できます。
<!-- 内部をモーフィング(フォームの入力値を保持) -->
<div hx-get="/refresh" hx-swap="innerMorph">
<input type="text" value="この値が保持される">
</div>
<!-- 要素全体をモーフィング -->
<div hx-get="/refresh" hx-swap="outerMorph">...</div>
textContent(HTMLインジェクション防止)
レスポンスをHTMLとしてパースせず、テキストとして挿入します。
<span hx-get="/count" hx-swap="textContent">0</span>
upsert(IDベースの更新)
既存のIDを持つ要素を更新し、新しい要素を挿入します(拡張機能として提供)。
新属性 hx-status ─ ステータスコード別の挙動制御
4.0で追加された非常に強力な属性です。HTTPステータスコードごとにスワップの挙動を細かく制御できます。
<!-- バリデーションエラーを専用コンテナに表示 -->
<form hx-post="/submit"
hx-status:422="target:#errors select:#validation-errors">
...
</form>
<!-- サーバーエラー時はスワップしない -->
<button hx-post="/save" hx-status:5xx="swap:none">保存</button>
<!-- 201 Created 時にURLをプッシュ -->
<form hx-post="/items" hx-status:201="push:/items/new">...</form>
<!-- ワイルドカードも使える(50xは502,503など、5xxは全5xx系) -->
<div hx-get="/data"
hx-status:404="select:#not-found"
hx-status:50x="select:#bad-gateway"
hx-status:5xx="swap:none">
</div>
新属性 hx-optimistic ─ オプティミスティックUI
リクエストが完了する前に、楽観的な(成功を前提とした)UIを即座に表示できます。リクエストが失敗した場合は元の状態に戻ります。
<div hx-post="/like" hx-optimistic="#liked-state">
<div id="unliked-state">♡ いいね</div>
</div>
<template id="liked-state">
<div>♥ いいね済み!</div>
</template>
新タグ <htmx-partial> ― 柔軟な部分更新
これまで hx-swap-oob を使って複雑な部分更新を行っていましたが、より直感的な <htmx-partial> タグが導入されました。
<!-- サーバーからのレスポンス例 -->
<div id="main-content">
メインの更新内容
</div>
<!-- この部分は #cart-count に対してスワップされる -->
<htmx-partial hx-target="#cart-count" hx-swap="innerHTML">
5
</htmx-partial>
View Transitions API のサポート
ブラウザネイティブの View Transitions API を使ったスムーズなページ遷移が標準サポートされました。
<!-- 個別のスワップに適用 -->
<form hx-post="..." hx-swap="outerHTML transition:true"></form>
<!-- グローバルに有効化 -->
<script>
htmx.config.transitions = true;
</script>
7. 設定項目(htmx.config)の変更まとめ
リネームされた設定項目
| 2.x の設定名 | 4.0 の設定名 |
|---|---|
defaultSwapStyle |
defaultSwap |
globalViewTransitions |
transitions |
historyEnabled |
history |
includeIndicatorStyles |
includeIndicatorCSS |
timeout |
defaultTimeout |
デフォルト値が変わった設定項目
| 設定項目 | 2.x のデフォルト | 4.0 のデフォルト |
|---|---|---|
defaultTimeout |
0(タイムアウトなし) |
60000(60秒) |
defaultSettleDelay |
20 ms |
1 ms |
廃止された設定項目
以下の設定項目は4.0で削除されました。代替手段も記載します。
| 廃止された設定 | 代替 |
|---|---|
allowEval |
廃止 |
allowScriptTags |
廃止 |
disableSelector |
hx-ignore 属性を使用 |
getCacheBusterParam |
廃止 |
historyCacheSize |
廃止(localStorage廃止のため) |
ignoreTitle |
hx-swap="... ignoreTitle:true" を使用 |
refreshOnHistoryMiss |
廃止 |
responseHandling |
hx-status と noSwap を使用 |
scrollBehavior |
廃止 |
scrollIntoViewOnBoost |
廃止 |
selfRequestsOnly |
htmx.config.mode を使用 |
withCredentials |
hx-config を使用 |
wsBinaryType |
廃止 |
wsReconnectDelay |
廃止 |
8. 2.xからのマイグレーション方法
ステップ1: アップグレードチェッカーを実行
HTMX 4.0には、プロジェクト内のテンプレートファイルをスキャンして修正が必要な箇所を指摘してくれるCLIツールが同梱されています(Python 3が必要)。
# プロジェクトルートで実行
npx htmx.org@next upgrade-check -- ./path/to/project/root
# Vueファイルなど追加の拡張子もスキャンする場合
npx htmx.org@next upgrade-check --ext .vue ./path/to/project/root
# デフォルトでスキャンされるファイル拡張子:
# .html, .php, .js, .ts, .jinja, .jinja2, .j2, .erb, .hbs
出力は ファイル:行番号 形式で、ほとんどのエディタでクリックして該当箇所に移動できます。
ステップ2: 互換性モードで動かす
まずは互換性設定を入れて2.xの挙動のまま4.0に移行し、その後段階的に修正するのがおすすめです。
<!-- 方法1: 設定で2.xの挙動を復元 -->
<script>
htmx.config.implicitInheritance = true;
htmx.config.noSwap = [204, 304, '4xx', '5xx'];
</script>
<script src="https://cdn.jsdelivr.net/npm/htmx.org@4.0.0-beta2"></script>
<!-- 方法2: 互換性拡張機能を使用(イベント名も含めて2.x互換に) -->
<script src="/path/to/htmx.js"></script>
<script src="/path/to/ext/htmx-2-compat.js"></script>
ステップ3: 段階的に移行する
互換性モードで動作確認後、以下の順で移行を進めます。
-
hx-disable→hx-ignore、hx-disabled-elt→hx-disableに変更(必ず先に行う) - 廃止された属性を代替に置き換える(
hx-vars→hx-vals、hx-ext→ スクリプト直接読み込みなど) -
hx-confirm:inheritedなどの明示的継承に書き換える - イベントリスナーのイベント名を更新する
- 設定項目名を更新する
拡張機能の読み込み方法の変更
2.xでは hx-ext 属性で拡張機能を指定していましたが、4.0ではスクリプトを直接読み込みます。
<!-- 2.x: hx-ext属性で指定 -->
<body hx-ext="sse">
<div hx-sse:connect="/events">...</div>
</body>
<!-- 4.0: スクリプトを直接読み込む -->
<script src="/path/to/htmx.js"></script>
<script src="/path/to/ext/sse.js"></script>
<!-- hx-ext属性は不要 -->
<div hx-sse:connect="/events">...</div>
特定の拡張機能のみ許可したい場合は htmx-config メタタグで制限できます。
<meta name="htmx-config" content='{"extensions": "sse, ws"}'>
9. まとめ
HTMX 4.0 "The fetch()ening" は、レガシーな XMLHttpRequest と決別し、モダンなWeb標準である fetch() APIへと舵を切った野心的なアップデートです。
主な変更点を振り返ると:
- ファイルサイズが約33%削減(50.1KB → 33.3KB)
-
DOMモーフィング(
innerMorph/outerMorph)が標準搭載 -
hx-statusでステータスコード別の挙動を細かく制御可能 -
hx-optimisticでオプティミスティックUIが簡単に実装可能 -
<htmx-partial>でより柔軟な部分更新が可能 - 属性の継承が明示的になり、予測可能な動作に
- エラーレスポンスもスワップされるようになり、エラーUIの実装が容易に
破壊的変更が多く、最初は戸惑う部分もありますが、長期的にはより予測可能でバグの少ない開発体験をもたらしてくれるはずです。
現在はベータ版ですが、今後の正式リリースに向けて、まずは upgrade-check コマンドで自分のプロジェクトへの影響を確認してみてください!
Discussion