Webフロントエンドの世界では、目立つ新機能よりも「どう壊れずに長く使えるか」のほうが難しいことがあります。今回の htmx 4.0.0 は、まさにその難しさに正面から向き合ったリリースです。見た目の派手さだけでなく、内部実装、イベント名、履歴まわりの扱いまで手を入れていて、使っている人ほど影響を受けます。しかも開発チームは、これは単なる機能追加ではなく、長期運用を見据えた設計の組み直しだとかなりはっきり言っています。
htmx チームは 2026-08-28 に htmx 4.0.0 の公開を告知した。開発期間は8か月にわたり、途中でゲームも作った、と少し肩の力を抜いた書き方をしているが、内容はかなり本気だ。出発点は、作者が fixi という別プロジェクトを作る過程で fetch() API と非同期 JavaScript に慣れたことだった。htmx はこれまで互換性の都合から XMLHttpRequest を使っていたが、fetch() へ移すことで内部がすっきりするのではないか、という発想が出発点になったという。
さらに、Christian からストリーミング HTML に関するアイデアをもらい、Michael と Alex も加わって開発が進んだ。実際には fixi の移植と htmx のテストスイートを走らせながら、2.x の挙動を少しずつ再現していった形だ。結果として、2.x と 4.x の違いはユーザー視点では小さい場面も多いが、差が出るところでは「どういう選択が 100年使える Web サービスに向いているか」を基準に決めた、と説明している。
ただし、NPM では 4.0 を latest にしない。非バージョン指定の CDN URL を使っている利用者を強制的に巻き込まないためで、しばらくは 2.x が latest、4.0 系は next のまま、早ければ2027年初頭までその状態が続く。サイト上の案内だけは 4.0 を参照するようになる。これは、最新を押し付けるより、運用中のサイトを守ることを優先した判断だと読める。
変更点の中で一番大きいのは属性継承の扱いだ。htmx 2 では、親要素に書いた属性が子要素にも自動で効くことが多かった。たとえば hx-confirm を親に置くと、配下の削除ボタンにも確認ダイアログが効く。これは便利だが、CSS のように「効くときは効く、でもどこまで効くか分かりづらい」という問題を生みやすかった。htmx 4 ではこの継承は既定では行われず、継承させたい属性名の後ろに :inherited を付ける。hx-confirm:inherited のように明示する形だ。アップグレード負担はここが最大で、どこに inherited を付けるべきかを探すコマンドラインツールも用意された。逆に、hx-disinherit のような旧来の補助属性は不要になり、削除すべきだとしている。
イベント名も整理された。htmx 2 では歴史的経緯でイベントが増え、名前の規則もばらばらだった。4.0 では htmx:phase:action[:sub-action] という形に統一され、htmx:beforeRequest は htmx:before:request、htmx:afterSwap は htmx:after:swap になる。エラー系もまとめ直され、htmx:xhr:* は fetch() 化に伴って消え、htmx:validation:* はブラウザ標準のフォーム検証へ寄せられた。履歴サポートも変わり、localStorage にページのスナップショットを保存する方式をやめた。戻る操作ではページを再取得して body か [hx-history-elt] に差し込む。3rd party JavaScript が DOM を書き換えたまま整合性が崩れる問題を避ける狙いだ。代わりに、sessionStorage を使う hx-history-cache という拡張が用意されている。
新機能もある。ひとつは morphing swap の標準搭載だ。これは差し替えのたびに要素を雑に置き換えるのではなく、既存 DOM をうまく「変形」させながら更新する方式で、以前から存在した idiomorph の発想を取り込んでいる。もうひとつは <hx-partial> タグで、単純な部分更新よりも意図が分かりやすい形で、別の場所へメッセージを追加したり、数値だけ差し替えたりできる。拡張群も増えていて、hx-preload、hx-download、hx-alpine-compat、hx-history-cache に加え、hx-sse、hx-ws、hx-multipart のようなストリーミング系、そして hx-live という軽いフロントエンドスクリプトの仕組みまで入った。配布物には htmax.js というバンドルもあり、どれを組み合わせるか迷いたくない人向けのまとめパッケージとして用意されている。
アップグレード支援もかなり手厚い。npx htmx.org@4.0.0 upgrade-check -- ./templates のようなコマンドで、HTML や JS、TS、Jinja、ERB などを走査し、継承属性不足、名前変更、削除された API、古いイベント名まで指摘する。説明例では、hx-headers に :inherited が要ること、hx-disable が hx-ignore に変わること、hx-vars が消えて hx-vals と js: を使うこと、hx-prompt は拡張として読むこと、htmx:afterRequest は新名に直すこと、htmx.addClass() は element.classList.add() に置き換えることが示されている。インストールは npm で htmx.org@4.0.0 を指定するか、CDN の https://unpkg.com/htmx.org@4.0.0/dist/htmx.min.js を読む形になる。
このリリースで一番印象に残るのは、派手な新機能より「暗黙を減らす」方向に舵を切ったことだ。属性継承を明示に変えたのは、一見すると書く量が増えるので面倒に見える。しかし、htmx のように HTML 属性でふるまいを積み上げる道具では、親に何を書いたかが子までどこまで影響するかを追えないほうが、後からはるかに苦しい。hx-confirm の例は分かりやすい。便利だが、画面が大きくなるほど「誰がどこでこの挙動を注入したのか」が見えにくくなる。明示化はその不透明さを減らす。
履歴を localStorage に保存しない、という変更も重要だ。これはキャッシュ速度の話に見えるが、実際には「他の JavaScript が触ったページを、どの状態で戻すか」という整合性の問題に近い。Web アプリは htmx だけで閉じていないことが多く、Alpine.js のような補助ライブラリや独自スクリプトが普通に混ざる。スナップショット復元は速い反面、その時点の DOM だけを凍結してしまうので、動いていたはずのロジックが戻らない。再取得して差し込む方式は少し遠回りに見えるが、現実のフロントエンドにはこちらのほうが自然だと思う。
htmx:beforeRequest のような古い名前を htmx:before:request に寄せたのは、単なる表記統一以上の意味がある。イベント名は実装の履歴をそのまま背負いやすく、放っておくと「どれが新しい流儀なのか」が分からなくなる。今回の整理は、古い API を切る痛みと引き換えに、今後の拡張の余地を買った形だろう。しかも upgrade-check がイベント名まで機械的に見つけるので、破壊的変更を「手作業で頑張る」のではなく、ツールで吸収する前提になっている。このあたりに、htmx が単なる小技のライブラリから、移行手順まで面倒を見る基盤に変わってきた感じがある。
新機能としては morphing swap や <hx-partial> のほうが目を引くが、今回の本質はそこだけではない。fetch() への移行、履歴の再設計、イベントの命名整理、拡張の再編成がまとまって入っていることに意味がある。htmx はもともと「JavaScript を減らし、HTML を中心にしたい」人に刺さる道具だった。その道具が、内部の都合で少しずつ歪んでいくと、結局は使いにくくなる。今回の 4.0.0 は、その歪みを直しながら、次の数年を見据えて作り直した版に見える。派手な話ではないが、こういうリリースのほうが実際には寿命を伸ばす。htmx を本気で使っている人ほど、その価値は大きいはずだと思う。