Skip to content
Published on

HTMX & Hyperscript — 反-SPA陣営の2026年、Carson Grossが投げかけたハイパーメディア命題 (2026 深掘り)

Authors

プロローグ — SPA疲れ、そしてCarsonの一言

2026年5月、あるフロントエンド開発者の平均的な一日を描いてみる。

pnpm installの終わりを待ちながらコーヒーを淹れ直し、Vite devサーバを立ち上げ、React Queryのキャッシュ無効化方針を書き直す。昨日まで動いていたビルドが今日はModule not found: Can't resolve 'crypto'で落ちる。誰かがnext.config.jsにwebpackポリフィルを足したら、今度はTurbopackが怒る。フォーム1つ作るのに — useStateuseEffectuseMutationzodスキーマ、react-hook-formリゾルバ、楽観更新ロジック、エラーバウンダリ、ローディングスピナー — 7つの抽象が動員される。そのフォームが結局やるのはPOST /todosと画面の一部の更新だ。

この見慣れた風景に、一人の人物がナイフを突き立てた。モンゴメリ・カレッジ計算機科学科の教授、Carson Gross。彼は2020年にintercooler.jsを書き直したライブラリをHTMXと名付け、時代を一文で要約した。

「HTMLはすでにハイパーメディアだ。私たちがしたのは、その潜在能力を11 KBで解放しただけだ」

HTMXは新しいフレームワークではない。HTMLに4つの属性を追加しただけだ。hx-gethx-posthx-swaphx-target。フォームはもうJavaScriptハンドラを必要としない。ボタンが直接サーバにPOSTを送り、サーバはHTMLの断片を返し、その断片がページの一部を置き換える。それだけだ。

しかしその単純さは単なる構文以上の何かだ。Carsonはその上に一冊の書籍を載せた。『Hypermedia Systems』 — Roy FieldingのREST論文を読み直し、HATEOASを真剣に受け取り、「なぜ私たちはJSON APIを作り、その上にクライアントサイドルータを作り、その上に状態管理を作り、その上にキャッシュを作り、その上に最適化コンパイラを作ったのか」を問う書籍だ。答えは挑発的だ。「必要なかった」

本稿はHTMX陣営 — 反-SPA陣営、つまり「boring web(退屈なWeb)」ルネサンス — の2026年を整理する。HTMX 2.xのコア、_hyperscriptというCarsonの二作目、Datastarという次世代候補、Alpine.jsという妥協案、『Hypermedia Systems』が提示した思考フレーム、37signalsのような本番事例、そして — もっとも重要なこと — HTMXが間違うところまで。

これはHTMX擁護論ではない。いつHTMXが正しく、いつReactが正しいかを判断するために必要なすべての道具を一息で提供することが目的だ。


1章 · ハイパーメディア命題 — Roy Fieldingを読み直す

HTMXのすべての主張は1冊の博士論文から始まる。2000年、Roy Fieldingの「Architectural Styles and the Design of Network-based Software Architectures」 — 我々が略してRESTと呼ぶものだ。

FieldingはRESTに6つの制約を置いた。クライアント-サーバ、無状態、キャッシュ可能、統一されたインターフェース、階層化されたシステム、そして — HATEOAS: Hypermedia As The Engine Of Application State。

最後の制約が核だ。HATEOASは「クライアントは次の行動についての情報を、サーバが応答で返したハイパーメディアからのみ取得すべきだ」と述べる。つまり応答に「次に何ができるか」という情報がリンクとフォームの形で入っている必要があるということだ。

Carsonの診断はこうだ。

  • 今日我々がRESTと呼ぶものの99%はHATEOASを除く5つの制約のみに従う
  • JSON APIはデータだけを与え、行動を与えない。だからクライアントが「次に何ができるか」をコードで書いておかなければならない
  • その結果クライアントはドメインモデルの複製を持つことになり、サーバとクライアントが二度同期するシステムになる
  • 一方でHTMLはそれ自体がハイパーメディアだ。formタグは行動を含み、aタグは状態遷移を含む

HTMXの命題は故に単純だ。

「ブラウザはすでに優秀なハイパーメディアクライアントだ。HTMXはそのクライアントの語彙を拡張する」

素のHTMLは「リンククリック」または「フォーム送信」時のみサーバリクエストが起こる。HTMXはそれを拡張する。

  • どんなイベントもトリガーになりうる(hx-trigger)
  • どんなHTTP動詞も使える(hx-gethx-posthx-puthx-patchhx-delete)
  • 応答をページのどの部分にでもスワップできる(hx-targethx-swap)

これがHTMXのすべてだ。一行で要約すると — 「any element, any event, any HTTP verb, any swap target」


2章 · HTMX 2.xコア — 4つの属性で99%を作る

2024年1月、HTMX 2.0がGAになった。2025年に2.1、2026年初に2.2がリリースされ、現在の安定版はhtmx.org基準で11.4 KB(gzipped)。依存関係はゼロ。IE11サポートは1.x系に残し、2.x系はevergreenブラウザのみに単純化された。

2.1 インストール — scriptタグ一行

<script src="https://unpkg.com/htmx.org@2.0.4"></script>

これだけだ。npmパッケージもあるが、HTMXは<script>タグで始めることを明示的に推奨している。ビルドステップなし、バンドラなし、ツリーシェイキングの心配なし。

2.2 最初の例 — 検索フォーム

伝統的なSPAで「タイプするとライブで結果が更新される検索」を作るには — useStateuseDeferredValue、debounceフック、キャッシュ無効化、abortコントローラ、ローディング状態マシン — 最低でも50行のReactコードが必要だ。

HTMX版:

<input type="search" name="q"
       hx-get="/search"
       hx-trigger="input changed delay:300ms, search"
       hx-target="#results"
       hx-indicator=".spinner" />

<div id="results"></div>
<div class="spinner htmx-indicator">Searching...</div>

ここで起こることを一行ずつ読んでみる。

  • hx-get="/search" — トリガーされるとGET /search?q=...を送る。name="q"が自動でクエリパラメータになる
  • hx-trigger="input changed delay:300ms, search"inputイベントが発生し値が変わり、300ms他のイベントがないときトリガー。またはユーザが検索キーを押したとき
  • hx-target="#results" — 応答で#resultsの内容を置換
  • hx-indicator=".spinner" — リクエスト中に.spinnerhtmx-requestクラスを追加。CSSで表示/非表示

サーバは? Express、FastAPI、Django、Rails、Phoenix、Spring — HTMLの断片を返せるものなら何でも。

@app.get("/search")
def search(q: str):
    results = db.search(q)
    return render_template("results_fragment.html", results=results)

このパターンが持つ意味は深い。ローディング状態、debounce、abort、レース条件、キャッシング — このすべてをHTMXが処理する。 我々はビジネスロジックだけを書く。

2.3 コア属性13個

HTMXの表面積は小さい。13個のコア属性でほぼすべてが表現される。

属性役割
hx-get / hx-post / hx-put / hx-patch / hx-deleteHTTPリクエスト発射
hx-triggerどのイベントでトリガーするか(clickinputevery 2srevealed、...)
hx-target応答をどの要素に適用するか(CSSセレクタ)
hx-swapどう適用するか(innerHTMLouterHTMLbeforebeginafterend、...)
hx-valsリクエストに追加する値(JSONまたはJS関数)
hx-headersカスタムヘッダ
hx-indicatorリクエスト中インジケータ
hx-confirm送信前のconfirm()ダイアログ
hx-push-url応答を受けたときのブラウザURL更新
hx-boost通常のリンク/フォームを自動でAJAXにアップグレード

hx-boostだけでも興味深い。<body hx-boost="true">一行を追加すると、ページのすべてのaformが自動でAJAXリクエストを送り、応答からbody部分だけを置換する。TurboやInertiaと似た効果を1属性で

2.4 hx-swapの威力 — DOM操作が宣言的になる

hx-swapは応答HTMLをDOMに適用する方法を決める。

  • innerHTML(既定): ターゲットの内部を置換
  • outerHTML: ターゲット自体を置換
  • beforebegin / afterbegin / beforeend / afterend: 隣接位置に挿入
  • delete: ターゲット削除(応答無視)
  • none: DOM変更なし(サイドエフェクトのみ)

ここにhx-swap="outerHTML swap:1s settle:1s scroll:#top"のようなmodifierを付けると — swap前に1秒待機(アニメーション)、settleフェーズ1秒(遷移クラス適用)、完了後#topにスクロール — このすべてが一行に表現される。

2.5 hx-triggerの威力 — イベントDSL

hx-triggerは小さなDSLだ。

<!-- 5秒ごとに自動更新 -->
<div hx-get="/notifications" hx-trigger="every 5s"></div>

<!-- ビューポートに入ったら一度だけトリガー(lazy load) -->
<div hx-get="/heavy-section" hx-trigger="revealed"></div>

<!-- 別要素のイベントでトリガー -->
<div hx-get="/related" hx-trigger="updated from:#article"></div>

<!-- キーボードショートカット -->
<form hx-post="/save" hx-trigger="keyup[ctrlKey&&key=='s'] from:body"></form>

every Nsでポーリングが一行になる。revealedで無限スクロールが一行になる。from:#selectorで別要素のイベントをlistenする。WebSocketなしでもポーリングで十分な領域 — 通知、株価、ライブカウンタ — でHTMXは即座に答えを与える。


3章 · Hyperscript — Carsonの二番目の挑発

HTMXはサーバ往復で処理できることを扱う。しかし「ボタンをクリックすると隣のdivのクラスをトグルする」のような純粋なクライアントサイド動作も我々はよく必要とする。ここでReactを引っ張ってくるのは過剰だ。jQueryはあまりに古臭く感じる。

Carsonはこの空白に**_hyperscript**(アンダースコア始まり、略称_hs)を置いた。英語の文のように読めるイベントハンドラDSLだ。

<button _="on click toggle .active on #panel">Toggle</button>

文として読んでみる。「on click, toggle the class .active on #panel」。そのまま英語だ。パーサはこれをそのまま受け取る。HyperTalk(HyperCardのスクリプト言語)からインスピレーションを得ている。

3.1 もう少し複雑な例

<input _="on input
            if my value's length < 3
              hide #suggestions
            else
              show #suggestions
              put 'Searching...' into #suggestions
            end" />

これは何か。入力の長さが3未満なら#suggestionsを隠し、そうでなければ表示し、テキストを「Searching...」で埋める。コードはそのまま英語だ。

3.2 fetchが一行で非同期

<button _="on click
            fetch /api/heavy
            put it into #result">
  Load
</button>

fetch /api/heavyの次行のitは応答を指す。put it into #result — 応答を#resultに入れろ。JavaScriptのPromiseやasync/awaitの違和感なく自然に流れる。

3.3 よく使われるパターン

  • クラスのトグル: on click toggle .open on .menu
  • 確認: on click if confirm('Sure?') trigger submit on me
  • タイマー: on load wait 3s then add .visible to #toast
  • ドラッグ: on mousedown set $dragging to me on mousemove if $dragging then ...
  • イベント委譲: on click from .row in me ...

3.4 Hyperscript採用の現実 — 2026年の答え

正直になろう。HyperscriptはHTMXほどに採用されていない。GitHubスターはHTMXの1/10程度だ。理由は単純だ。

  • VS Codeのシンタックスハイライトが弱い — クォート内の文字列として認識されて色がつかない
  • エラーメッセージが薄い — 英語の文なのでどこで間違ったかの追跡が難しい
  • TypeScriptと統合されない — 型安全性なし
  • Alpineの方が馴染みやすい — JavaScript風の式表現なのでReact/Vue出身者の適応が速い

そのため大多数のHTMXユーザはHyperscriptではなくAlpine.jsを併用する。それが次の章だ。


4章 · Alpine.js — リアクティビティを欲しがる者への橋

Caleb Porzioが作ったAlpine.jsは「Vue 3のリアクティビティ、jQueryの直接性、Tailwindのインラインアプローチ」を合わせたライブラリだ。15 KB。依存関係ゼロ。HTMXとの最良の補完として定着した。

4.1 基本

<div x-data="{ open: false }">
  <button @click="open = !open">Toggle</button>
  <div x-show="open" x-transition>Hello!</div>
</div>
  • x-data — コンポーネントスコープを開きreactive state オブジェクトを定義
  • @click(またはx-on:click) — イベントハンドラ
  • x-show — 条件付きレンダリング(displayトグル)
  • x-transition — 自動トランジション

この5つのdirectiveで90%が表現される。追加でx-textx-htmlx-bindx-modelx-forx-ifなどがある。

4.2 HTMX + Alpine — 黄金の組み合わせ

HTMXはサーバ往復、Alpineはローカル UI 状態。両者は衝突せず自然に合わさる。

<div x-data="{ mode: 'list' }">
  <button @click="mode = 'list'" :class="{ 'active': mode === 'list' }">List</button>
  <button @click="mode = 'grid'" :class="{ 'active': mode === 'grid' }">Grid</button>

  <div hx-get="/items" hx-trigger="load" hx-target="this">
    <!-- サーバから受け取ったHTMLが入る -->
  </div>
</div>

ビューモード切替(クライアント状態)はAlpine、アイテムロード(サーバ状態)はHTMX。各自が得意なことをする。

4.3 alpine-ajax — コインの裏側

興味深いことにAlpine陣営にはalpine-ajaxというサードパーティプラグインがある。Alpineの上でHTMX的なサーバ通信をする。この道具の存在自体がメッセージだ。HTMX命題とAlpine命題は同じものを異なる入口から見た結果だ。


5章 · Datastar — 次世代ハイパーメディアフレームワーク

Delaney Gillilanが作ったDatastarはHTMX + Alpineを1ライブラリに合わせる挑発的試みだ。2024年に1.0が出て、2026年現在1.3が安定版。サイズは約10 KB。

5.1 一つの核心的差異 — SSE優先

HTMXは通常のHTTP応答を受けてDOMを置換する。DatastarはServer-Sent Events(SSE)を1級市民に置く。つまり1つのリクエストが複数のfragment更新をストリームとして受ける。

<button data-on-click="@get('/calculate')">
  Calculate
</button>

<div id="step1"></div>
<div id="step2"></div>
<div id="final"></div>

サーバは単一のリクエストを受けてSSEで応答する。

event: datastar-fragment
data: selector #step1
data: fragments <div id="step1">Step 1 done</div>

event: datastar-fragment
data: selector #step2
data: fragments <div id="step2">Step 2 done</div>

event: datastar-fragment
data: selector #final
data: fragments <div id="final">Result: 42</div>

LLMストリーミング、進行状況表示、ライブダッシュボード — Datastarの本領はこういう場所だ。HTMXでもhtmx-ext-sse拡張で似たことができるが、Datastarは最初からSSEを前提に設計されている。

5.2 式表現 — Alpineスタイル

<input data-bind-name />
<div data-text="$name"></div>
<button data-on-click="$count++">+</button>
<p data-text="$count"></p>

data-bind-*data-textdata-on-* — Alpineとほぼ互換の語彙。シグナルベースのリアクティビティを採用し、すべての状態は$でアクセスする。

5.3 Datastar vs HTMX — どう選ぶか

  • LLMストリーミング/リアルタイムダッシュボード中心 → Datastar(SSE一級)
  • 伝統的なCRUD/フォーム中心 → HTMX(エコシステム成熟、互換性優位)
  • クライアントサイド状態が少なくない → Datastar(Alpine機能内蔵)
  • サーババックエンドと統合されたライブラリが豊富 → HTMX(Django、Rails、FastAPI、Express全てにヘルパあり)

2026年現在HTMXが圧倒的多数だ。Datastarは「SSEネイティブ」という強みをLLM時代に持ち込んだが、採用はまだアーリーアダプタ段階だ。


6章 · Hypermedia Systems — 一冊が作った思想体系

Carson Gross、Adam Stepinski、Deniz Akşimşekの3人が書いた『Hypermedia Systems』は2023年に出版され、GitHubでオープンアクセスで読める。副題は「HTML, HTTP, and the Hypermedia Architecture」。

6.1 書籍が答える問い

  • なぜ我々はJSON API + JavaScriptクライアントのパターンを既定値として受け入れたのか?
  • HATEOASは本当に実用的か、それとも学術的好奇心に過ぎないか?
  • モバイルでハイパーメディアが通用するか?(書籍は「Hyperview」というAndroid/iOSハイパーメディアクライアントも扱う)
  • ハイパーメディアで作ったシステムはどうスケールするか?

6.2 LoB — Locality of Behavior 原則

書籍が導入した核心原則の一つが**Locality of Behavior(LoB、行動の局所性)**だ。一文で — 「要素の動作はその要素を見れば分かるべきだ」。

Reactは動作をコンポーネントツリーのどこかにあるハンドラに分離する。CSSは外部ファイルに置く。JSイベント委譲も動作を別の場所に置く。これが良いと我々は教わってきたが(Separation of Concerns)、Carsonは別の原則を提案する。分離された関心ではなく、集まった行動。

<!-- 一箇所で動作が見える -->
<button hx-post="/like"
        hx-target="#count"
        _="on click toggle .liked on me">
  Like
</button>

このボタンが何をするか — POSTリクエスト、カウント更新、クラストグル — 別のファイルを開く必要がない。デザイナーも、バックエンド開発者も、AIアシスタントも、この一行を見て理解する。

6.3 SoC vs LoB — どちらが正しいか

答えは両方正しく、文脈が決める。大きなReactアプリではコンポーネント抽象がLoBの欠如を補償する。HTMXアプリでは画面が小さくLoBがそのまま通用する。ユーザ5人の社内ツールではLoBに従い、1億人を目指すSaaSではコンポーネント抽象が必要かもしれない。

核心は — LoBは既定値となるべきで、抽象は正当化されなければならないということだ。


7章 · 本番事例 — 誰が実際に使っているか

HTMXはおもちゃではない。2026年現在、次の事例が公開されている。

  • GitHub — 新しいPRページの一部、イシューコメント — Turbo中心だがHTMX的パターンが多い
  • NASA JPL — 内部ツールの多くがHTMXベース(公開カンファレンスでの発表あり)
  • Contexte — フランスの政治ニュースサイト、HTMXで全面リニューアル
  • Replicant.au — オーストラリアのインディSaaS、HTMX + Hyperscriptを全面採用
  • David Heinemeier Hansson37signals — Hotwire(Turbo + Stimulus)がメインだが、「フロントエンド単純化」陣営の代表として HTMX陣営と思想を共有
  • Bunny.net — CDN会社、管理ダッシュボードがHTMXベース
  • Quanta Magazine — 一部のインタラクティブコンテンツ

DHH(David Heinemeier Hansson)はHTMXユーザではないが、「複雑さはファッションではない」キャンペーンの最大の声だ。彼のHotwireはRails陣営のHTMXで、思想はほぼ同じ。2024-2025年の彼の一連のトーク — 「The One Person Framework」、「What is Modern Web Development?」 — はHTMX陣営の資料としてもよく引用される。

7.1 会社規模はどこまで可能か

小規模SaaS、社内ツール、コンテンツサイト、管理パネル、CMS — ここまではHTMXが明らかに強い。モバイルアプリのように動作すべきPWA、オフラインが1級市民である協業ツール、WebGLキャンバスをいくつも立ち上げるクライアント重めのゲーム — こういう場所ではHTMXは弱い。

37signalsのBasecampは両者の間のどこかにある。協業ツールだがリアルタイム同期のような重いクライアント状態は少ない。


8章 · 正直な弱点 — HTMXが間違うところ

擁護論ではないと言った。HTMXは次の領域で弱い、または合わない。

8.1 豊富なクライアントサイド状態

  • Figmaのような協業ホワイトボード — 多数のオブジェクト状態をクライアントで追跡しなければならない。サーバ往復は非現実的だ
  • Notionのようなブロックエディタ — キー入力ごとにサーバに行くのは不可能だ
  • VS Code Web — エディタ状態、シンタックスツリー、LSP応答 — すべてクライアントで処理されなければならない

こういう場所ではReact/Vue/Solidが正しい。

8.2 オフラインファースト

PWA、モバイル優先、飛行機でも動作すべきアプリ。HTMXはサーバ応答を仮定するのでオフラインが壊れる。Service Worker + IndexedDB + 同期キュー — この組み合わせはSPAの領域だ。

8.3 複雑なクライアントサイドアニメーション

  • 60fpsのキャンバス描画
  • 物理シミュレーション
  • 3Dインタラクション
  • タイムラインベースのビデオ編集

こういう場所でHTMXは役に立つことがほぼない。

8.4 サーバ負荷

HTMXはインタラクションごとにサーバを叩く。トラフィックパターンが静的アセット中心から動的HTML生成中心に変わる。サーバサイドキャッシング、エッジレンダリング、CDN戦略が必須だ。SPAではこの負担の一部がCDNとクライアントに移っていたが、HTMXではサーバが再び引き受ける。

8.5 モバイルネットワーク

応答時間が200ms以上に延びるとユーザ体験が崩壊する。Optimistic updateパターンが難しく、「ボタンを押したのに何も変わらない」という印象を与えやすい。

回避策はある。hx-disabled-elthx-indicator、ローカルAlpine状態で即時UI更新→サーバ応答で確定 — しかし「既定で高速な」SPAよりは気を使う必要がある。


9章 · 判断フレームワーク — いつHTMXか

次の表を基準線にする。全項目を合計して点数が0より大きければHTMX、0より小さければSPA。

質問HTMX点SPA点
フォーム中心か?+20
コンテンツ中心か?+20
社内ツール/管理か?+20
チームが5人以下か?+10
サーバ言語が強いか(Django/Rails/Phoenix)?+20
SEOが重要か?+1-1
オフラインが必要か?-2+2
クライアントサイド状態が多いか?-2+2
60fpsアニメーションが核か?-2+2
モバイルアプリとコード共有が必要か?-1+2
5万人以上同時ユーザか?-1+1
デザイナーがHTML/CSS優先か?+10
既存コードベースがReactか?-2+1

この点数表は絶対値ではない。しかし出発点にはなる。点数が0近くなら — 両パターンを混ぜることができる。HTMXで大きな領域を作り、本物のSPAが必要な部分(エディタ、ホワイトボード)だけReact島に閉じ込めるパターンが増えている。

9.1 マイグレーション戦略

既存のReactアプリがあってHTMXに移したいなら — 全面書き直しはほぼ常に間違った答えだ。Strangler Figパターンを使おう。

  1. 新ページからHTMXで書く — 新規管理ページ、新モジュール
  2. CRUDが単純な画面を標的移行 — フォームページ、リストページ
  3. エディタ/ダッシュボードなどクライアント重い画面はReactで維持 — 島として隔離
  4. 共有ヘッダ/ナビゲーションはサーバサイドレンダリング — HTMX hx-boostで遷移を加速

10章 · コード一ページでHTMX陣営を見る

10.1 HTMX — インライン編集フォーム

<!-- 表示モード -->
<div hx-target="this" hx-swap="outerHTML">
  <p>タイトル: こんにちは、世界</p>
  <button hx-get="/posts/1/edit">編集</button>
</div>

<!-- サーバ応答: 編集モード -->
<form hx-put="/posts/1" hx-target="this" hx-swap="outerHTML">
  <input name="title" value="こんにちは、世界" />
  <button type="submit">保存</button>
  <button hx-get="/posts/1">キャンセル</button>
</form>

サーバ側擬似コード(Python/FastAPI):

@app.get("/posts/{id}/edit")
def edit_form(id: int):
    post = db.get_post(id)
    return templates.TemplateResponse("post_edit.html", {"post": post})

@app.put("/posts/{id}")
def update_post(id: int, title: str = Form(...)):
    db.update_post(id, title=title)
    post = db.get_post(id)
    return templates.TemplateResponse("post_display.html", {"post": post})

画面遷移ロジック、状態マシン、フォームライブラリ — すべて消えた。

10.2 Hyperscript — モーダルを閉じる一行

<div id="modal" class="hidden">
  <div class="overlay" _="on click hide #modal"></div>
  <div class="content">
    <button _="on click hide #modal">閉じる</button>
    <p>Escでも閉じます。</p>
  </div>
</div>

<script>
  document.addEventListener('keydown', e => {
    if (e.key === 'Escape') document.getElementById('modal').classList.add('hidden')
  })
</script>

上のJavaScriptもHyperscriptで一行に書ける。

<body _="on keydown[key=='Escape'] from window hide #modal">

10.3 Datastar — LLMストリーミング

<button data-on-click="@post('/chat', { messages: $messages })">
  Send
</button>

<div id="response"></div>

サーバはSSEでトークン単位のfragmentを送る。

event: datastar-fragment
data: merge inner
data: selector #response
data: fragments Hello

event: datastar-fragment
data: merge inner
data: selector #response
data: fragments Hello,

event: datastar-fragment
data: merge inner
data: selector #response
data: fragments Hello, world!

Datastarはこの使用事例を最初から前提する。HTMXではhtmx-ext-sse拡張で似たことが可能だが1級市民ではない。


エピローグ — 退屈なWebのルネサンス

Webの最初の10年は単純だった。HTMLがあり、フォームがあり、リンクがあり、サーバがあった。そしてjQueryが来て、Ajaxが来て、BackboneとAngularが来て、Reactが来て — 我々が作るものすべてが急に二度描かれ始めた。一度はサーバで、一度はクライアントで。一度はPythonで、一度はJavaScriptで。そしてその二つを合わせることに、次第に大きな割合のエネルギーを使うようになった。

Carson Grossはどこかの時点で立ち止まり、問うた。「我々は本当にこのすべてを必要としていたのか?」

答えは — ある場合はそうだし、ある場合は違う。HTMXはすべての場所で正しいわけではない。しかし我々がReactを使った場所の半分はReactが正しくなかった。その半分のさらに半分 — 管理、CRUD、社内ツール、コンテンツサイト — はHTMX一行で十分だった。

2026年にHTMX陣営が強くなる理由は単純だ。AI時代に単純さはより価値ある資源になる。 LLMはReactコンポーネントツリーの中の状態の流れを追うのに弱いが、<button hx-post="/like">が何をするかは即座に分かる。AIアシスタントと協業するコードベースはLoBが優位だ。

退屈なWebは終わったのではない。ただしばらく忘れられていただけだ。

チェックリスト — 次のプロジェクトを始める前に

  • フォーム中心か、データ中心か? フォーム中心ならHTMX先行
  • サーバ言語と強く統合されるか? Django/Rails/PhoenixならHTMXが自然
  • オフラインが核要件か? それならSPAまたはPWA
  • 60fpsキャンバス/3Dが核か? それならSPA
  • デザイナーがHTML/CSSで直接作業するか? HTMXは彼らに馴染む
  • チーム規模が5人以下か? 単純さのROIが最も高い
  • AIアシスタントとペアプロするか? LoBコードがより理解されやすい

アンチパターン — してはいけないこと

  • HTMXでSPA真似事 — ページ遷移をすべてhx-boostだけで処理しクライアントルータを作らない。URLが一貫して動作するようサーバ側ルーティングを維持
  • すべてのインタラクションでサーバ往復 — クラストグルのような純粋UI動作はHyperscript/Alpineで処理。サーバを呼ぶ必要のない場所で呼ばない
  • HTMLのfragmentが巨大化 — 応答fragmentは小さくあるべき。毎回ページの半分を更新するならSoCが崩れているサイン
  • HyperscriptとAlpineを同時使用 — 両者は似た領域を扱う。一つだけ選ぶ
  • テスト不在 — HTMXはサーバをより頻繁に叩く。統合テスト(Playwright風)がより重要
  • ハイパーメディア可能性を無視したJSON API設計 — REST APIをJSONで設計しその上にHTMXページを乗せると、二度同期するSPAと同じになる。ハイパーメディアを1級応答に置く
  • HTMX拡張を知らずに始めるhtmx-ext-ssehtmx-ext-wshtmx-ext-response-targetsなどを覚える。コアが小さい分、拡張が頻繁に必要

次回予告

次回はHTMX + Django/FastAPI/Railsバックエンドスタックの実戦構成 — テンプレートfragment管理戦略、認証フロー、ファイルアップロード、フォーム検証、CSRF、そして「HTMXフレンドリーなコントローラパターン」 — を扱う。その次にはHTMXとReact島を一ページに混ぜるハイブリッドパターンで — Notion風エディタをReactで立てその周りをHTMXで包む実戦例を持ってくる。


参考 / References