htmx 2026年版:Reactが不要なとき(そして絶対に必要なとき)
フロントエンドコミュニティでhtmx vs React論争が2年も続いていますが、議論の大半はポイントがズレています。どちらの技術が悪いわけじゃなくて、同じ問題を解く競合として比べている時点で間違っているんです。
htmxがGitHubスター47,000超え。Reactは依然として数百万のプロダクションアプリを動かしている。2025年のState of JS調査(2026年2月公開)では、htmxは使ったことのある開発者の間で「最も好感度の高い技術」を維持し、Reactは使用率83.6%にもかかわらず満足度が過去最低を記録しました。何かが確実に変わっていますが、具体的に何が変わっているのか。
これは「htmx最高、Reactダメ」系の記事じゃありません。意思決定フレームワークです。読み終わる頃には、htmxが正解な場面、Reactが依然として代替不可能な場面、そして「両方使う」が答えな場面を明確に判断できるようになります。
核心的なアーキテクチャの違い
機能比較の前に、根本的な違いを押さえておく必要があります。htmxとReactはAPIが違うだけじゃない。アプリケーションの状態がどこにあるべきかという哲学そのものが違うんです。
React:クライアントサイドアプリケーションモデル
Reactはブラウザをアプリケーションランタイムとして扱います。サーバーはJSONを返すAPI。クライアントが状態管理、ルーティング、UIレンダリング、副作用処理をすべて担当する構造です。
[サーバー] → JSON → [Reactアプリ] → DOM
↑
状態管理
ルーティング
副作用
エラーバウンダリ
このモデルは強力です。オフラインファーストアプリ、楽観的更新、複雑なアニメーション、リアルタイムコラボレーション、リッチなインタラクションを実現できる。でも、その分だけ複雑なんです。2026年の典型的なReactアプリの構成を見ると:
- メタフレームワーク(Next.js、Remixなど)
- 状態管理(React Context、Zustand、Jotai、またはServer Components)
- データフェッチング(TanStack Query、SWR、またはフレームワークネイティブローダー)
- ビルドパイプライン(Vite、Turbopack、Webpack)
- TypeScript設定
- テストインフラ(Vitest、Playwright、Testing Library)
ビジネスロジック1行書く前にツーリングが6層。これが2026年のReactの現実です。
htmx:ハイパーメディアモデル
htmxはブラウザをドキュメントビューアとして扱います。ただし、ドキュメントの一部を差し替えられるビューア。サーバーがHTMLフラグメントを返し、クライアントにはアプリケーション状態も、ルーティングロジックも、ビルドステップもありません。
[サーバー] → HTML → [ブラウザ + htmx] → DOM(swap対象)
サーバーがアプリケーションそのもの。ブラウザはレンダラー。htmxはページリロードなしで一部を更新する橋渡し役です。
<!-- ページリロードなしで結果を更新する検索フォーム --> <input type="search" name="q" hx-get="/search" hx-trigger="input changed delay:300ms" hx-target="#results"> <div id="results"> <!-- サーバーがここにHTMLフラグメントを返す --> </div>
これだけ。JavaScriptファイルなし。ビルドステップなし。状態管理なし。サーバーが検索クエリを処理して結果をHTMLでレンダリングしたら、htmxが#resultsのdivをswapする。インタラクション全体がHTML属性だけで完結するんです。
htmxが勝つ領域:Webアプリの80%
React支持者が聞きたくない不都合な真実があります。Webアプリの大半は、フォーム、テーブル、フィルター、ナビゲーションで構成されるCRUDインターフェースなんです。クライアントサイドのアプリケーションランタイムは必要ない。サーバーがHTMLをレンダリングして、ページの一部をリロードなしで更新できれば十分。
1. 管理画面と社内ツール
htmxが圧倒的に有利な最大カテゴリ。管理パネルというのは本質的にこういうものの集合です:
- ソート、フィルター、ページネーション付きテーブル
- レコード作成・編集フォーム
- 確認用モーダルダイアログ
- フィードバック用トースト通知
htmxなら、各機能はHTMLフラグメントを返すサーバーラウンドトリップ1回で終わり:
<!-- ソート可能なテーブルヘッダー --> <th hx-get="/users?sort=name&dir=asc" hx-target="#user-table-body" hx-indicator="#loading"> 名前 ↕ </th> <!-- 確認付き削除 --> <button hx-delete="/users/42" hx-confirm="このユーザーを削除しますか?" hx-target="closest tr" hx-swap="outerHTML swap:500ms"> 削除 </button> <!-- インライン編集 --> <td hx-get="/users/42/edit-name" hx-trigger="dblclick" hx-swap="innerHTML"> 田中太郎 </td>
同じ機能をReactで実装すると:
// ソート状態管理 const [sortConfig, setSortConfig] = useState({ key: 'name', dir: 'asc' }); // TanStack Queryでデータフェッチング const { data, isLoading, refetch } = useQuery({ queryKey: ['users', sortConfig, filters, page], queryFn: () => fetchUsers({ ...sortConfig, ...filters, page }), }); // 楽観的更新付き削除ミューテーション const deleteMutation = useMutation({ mutationFn: deleteUser, onMutate: async (userId) => { await queryClient.cancelQueries(['users']); const previous = queryClient.getQueryData(['users']); queryClient.setQueryData(['users'], (old) => old.filter(u => u.id !== userId) ); return { previous }; }, onError: (err, userId, context) => { queryClient.setQueryData(['users'], context.previous); }, onSettled: () => queryClient.invalidateQueries(['users']), }); // 確認ダイアログの状態 const [deleteTarget, setDeleteTarget] = useState(null); const [showConfirm, setShowConfirm] = useState(false);
htmx版はHTML属性8行。React版はJSXに触れる前にJavaScript 25行以上。しかもReact版の方が壊れるポイントが多い。staleキャッシュ、楽観的ロールバックのバグ、クエリ間のレースコンディション、サブスクリプションからのメモリリーク。こういうのが全部乗ってくるわけです。
2. マルチステップフォームとウィザード
htmxは部分バリデーションとマルチステップフローを自然に処理できます。サーバーが状態を制御するので:
<!-- ステップ1:サーバーがバリデーション後にステップ2を返す --> <form hx-post="/onboarding/step-1" hx-target="#wizard-container" hx-swap="innerHTML"> <input name="email" type="email" required> <input name="company" required> <button type="submit">次へ</button> </form>
サーバーがステップ1をバリデーション→セッションに保存→ステップ2のHTMLを返す。たったこれだけ。クライアントサイドのフォーム状態管理?なし。Formik?不要。React Hook Form?不要。Zodスキーマをコンポーネントに繋ぐ作業?当然なし。
3. 動的セクションがあるコンテンツサイト
ブログ、ドキュメントサイト、ECの商品ページなど、コアコンテンツはサーバーレンダリングだけど動的インタラクション(カート追加、ライブ検索、コメント)が必要な場合。
<!-- リロードなしのカート追加 --> <button hx-post="/cart/add" hx-vals='{"productId": "abc123", "qty": 1}' hx-target="#cart-count" hx-swap="innerHTML"> カートに追加 </button> <!-- ライブコメントセクション --> <div hx-get="/posts/42/comments" hx-trigger="load, every 30s" hx-swap="innerHTML"> <!-- コメントがここに読み込まれる --> </div>
パフォーマンス比較
サーバー駆動アプリにおいて、htmxは重要なパフォーマンス指標すべてで勝ちます:
| 指標 | htmx | React SPA |
|---|---|---|
| JavaScriptバンドル | 約14 KB(htmx本体) | 150-400 KB(フレームワーク+アプリ) |
| Time to Interactive | ほぼ即時(ハイドレーションなし) | 遅延(ハイドレーション必要) |
| INPスコア | 優秀(最小限のJS実行) | 可変(コンポーネントツリー次第) |
| サーバー負荷 | 高い(HTMLレンダリング) | 低い(JSON返却) |
| CDNキャッシュ適性 | 優秀(HTMLフラグメントはキャッシュしやすい) | 低い(動的JSONレスポンス) |
Reactが勝つ唯一の領域がサーバー負荷。サーバーサイドでHTMLをレンダリングするのはJSON直列化よりCPUを食う。ただ2026年現在、エッジコンピューティングとストリーミングHTMLが普及したことで、このトレードオフは大多数のアプリでhtmx有利に傾いてきています。
Reactが依然として代替不可能な領域
反対側の話もしないとフェアじゃない。htmxが本当に歯が立たない領域があるんです。
1. リッチテキストエディタと複雑なインタラクティブコンポーネント
Notion、Google Docs、コラボレーションホワイトボードのようなものを作るには、マイクロ秒単位で複雑なローカル状態を管理する必要があります。キーストロク1つ、カーソル位置、選択範囲、フォーマット操作のすべてをクライアントサイドで処理しなければならない。文字1つにサーバーラウンドトリップ?使い物にならないレイテンシになります。
htmxではこれは無理です。キーストロクごとに200msのサーバーラウンドトリップは実用的じゃない。ここはクライアントアプリの領域です。
2. オフラインファーストアプリ
インターネットなしで動く必要があるアプリ——フィールドサービスアプリ、モバイルメモアプリ、POSシステムなど——では、htmxはアーキテクチャ的に互換性がありません。htmxはすべてのインタラクションにサーバーが必要。サーバーなし=インタラクションなし。
3. リアルタイムコラボレーション機能
Google Docsスタイルの同時編集、マルチプレイヤーゲーム、コラボレーションデザインツールなど、複数ユーザーが高頻度で共有状態を変更するケース。htmxのリクエスト・レスポンスモデルはこのパターンに合いません。
4. 高頻度UIインタラクション
ドラッグ&ドロップ、インタラクティブなデータビジュアライゼーション(D3.js)、アニメーション重めのUI、キャンバスベースのエディタ、ゲーム的なインターフェース。クライアントサイドのフレームレート応答性が求められます。
意思決定マトリクス
| ユースケース | htmx | React | 両方 |
|---|---|---|---|
| 管理画面 | ✅ 最適 | ⚠️ オーバーキル | — |
| CRUDアプリ | ✅ 最適 | ⚠️ オーバーキル | — |
| マルチステップフォーム | ✅ 良い | ✅ 良い | — |
| EC | ✅ 良い | ✅ 良い | ✅ 最適 |
| インタラクション付きコンテンツサイト | ✅ 最適 | ⚠️ オーバーキル | — |
| リッチテキストエディタ | ❌ 不可 | ✅ 必須 | — |
| オフラインファーストアプリ | ❌ 不可 | ✅ 必須 | — |
| リアルタイムコラボ | ❌ 不向き | ✅ 必須 | — |
| データ可視化ダッシュボード | ⚠️ 限定的 | ✅ 最適 | ✅ 良い |
| D&Dビルダー | ❌ 不可 | ✅ 必須 | — |
「両方使う」パターン:インタラクティビティアイランド
2026年の最も現実的な答えは「両方使う」であることが多いです。インタラクティビティアイランドというアーキテクチャで、htmxがページ構造、ナビゲーション、標準CRUDを処理し、複雑なインタラクティブな部分だけをReact(またはPreact/Svelte)コンポーネントで処理します。
仕組み
<!-- htmxが管理するページシェル --> <main> <!-- htmxがナビゲーション処理 --> <nav hx-boost="true"> <a href="/dashboard">ダッシュボード</a> <a href="/analytics">分析</a> <a href="/settings">設定</a> </nav> <!-- htmxがテーブル/CRUD処理 --> <section hx-get="/dashboard/table" hx-trigger="load" hx-target="this"> 読み込み中... </section> <!-- インタラクティブチャートはReactアイランド --> <div id="analytics-chart" data-config='{"range": "30d", "metrics": ["revenue", "users"]}'> </div> <script type="module"> import { renderChart } from './chart-island.js'; renderChart(document.getElementById('analytics-chart')); </script> </main>
GitHubが数年間このパターンを使ってきました。GitHubの大部分はサーバーレンダリングHTMLで、コードエディタ、ファイルツリー、ActionsワークフロービルダーなどのインタラクティブコンポーネントにだけJavaScriptが入っています。
マイグレーション:React SPAからのhtmx抽出
React SPAからhtmxへの移行を検討中なら、ビッグバンリライトはやめましょう。Strangler Figパターンを使います。最もシンプルなページから1つずつ置き換えていく方法です。
ステップ1:低インタラクションページの特定
Reactアプリを監査してください。本質的にフィルター付きデータテーブル、設定フォーム、詳細ビュー、リスト/グリッドビューであるページを探します。全ページ数の60-80%を占めていても、インタラクティブな複雑さは20%未満のはずです。
ステップ2:サーバーサイドルート
マイグレーション候補ごとに、完全なHTMLを返すサーバーサイドルートを作成します:
# Djangoの例 def user_list(request): users = User.objects.filter(active=True) sort = request.GET.get('sort', 'name') if request.headers.get('HX-Request'): # htmxリクエスト:テーブルボディだけ返す return render(request, 'users/_table_body.html', {'users': users}) # フルページリクエスト:ページ全体を返す return render(request, 'users/list.html', {'users': users})
HX-Requestヘッダーチェックがキーパターン。同じルート、同じロジックですが、htmxはHTMLフラグメントを受け取り、直接アクセスはフルページを受け取ります。
ステップ3:Reactは必要な場所に残す
インタラクティブコンポーネントまで無理に移行する必要はなし。リッチテキストエディタ、ドラッグ&ドロップのカンバンボード、リアルタイムチャットウィジェットはReactのまま残して、サーバーレンダリングページに差し込む独立アイランドとして扱えばOKです。
マイグレーションのROI
CRUD中心のReact SPAからhtmxに移行したチームが共通して報告する数字:
- フロントエンドコードの40-60%削減
- 標準CRUD画面の機能開発スピード向上(クライアント状態管理の配線が不要)
- Core Web Vitalsの改善(ハイドレーション排除によるLCP・INP向上)
- デバッグの簡素化(サーバーログに全ストーリーがある。クライアント状態の再現不要)
バックエンドファクター:htmxが輝くスタック
htmxの過小評価されている側面が1つあります。バックエンドチームのアンロックです。チームが主にGo、Python(Django/Flask)、Ruby(Rails)、Rust、PHP(Laravel)、Java(Spring)の開発者なら、htmxはReactを学ばなくてもインタラクティブなWebアプリを構築できるようにしてくれます。
htmx + バックエンド言語エコシステム
| バックエンド | htmx統合 | テンプレートエンジン | プロダクション事例 |
|---|---|---|---|
| Go | 優秀(templ、標準ライブラリ) | templ、html/template | インフラダッシュボード |
| Python/Django | 優秀(django-htmx) | Djangoテンプレート | 管理パネル、CMS |
| Ruby/Rails | 優秀(Turbo + htmx) | ERB、Haml | SaaS管理インターフェース |
| Rust/Axum | 成長中(askama、maud) | askama、maud | 高性能ダッシュボード |
| PHP/Laravel | 優秀(ネイティブBlade) | Blade | EC管理、CRM |
| Java/Spring | 良好(Thymeleaf) | Thymeleaf | エンタープライズCRUD |
htmxでやりがちなミス(と回避法)
1. htmxをクライアントフレームワークのように使う
最大のミスは、クライアントで状態を管理すること。どのタブがアクティブか、どのフィルターが適用されているかをJavaScriptで追跡しているなら、htmxのアーキテクチャと戦っています。
ダメなパターン:
let activeFilters = []; document.querySelectorAll('.filter-checkbox').forEach(cb => { cb.addEventListener('change', () => { if (cb.checked) activeFilters.push(cb.value); else activeFilters = activeFilters.filter(f => f !== cb.value); htmx.ajax('GET', `/items?filters=${activeFilters.join(',')}`, '#results'); }); });
良いパターン:
<!-- フィルター状態はサーバーに任せる --> <form hx-get="/items" hx-target="#results" hx-trigger="change"> <label><input type="checkbox" name="filter" value="active"> アクティブ</label> <label><input type="checkbox" name="filter" value="premium"> プレミアム</label> <label><input type="checkbox" name="filter" value="new"> 新規</label> </form>
2. リクエストを飛ばしすぎる
htmxはサーバーリクエストを簡単にしてくれる反面、油断するとおしゃべりなアプリになりがち。hx-triggerのモディファイアでデバウンス・スロットルを入れておきましょう:
<input hx-get="/search" hx-trigger="input changed delay:300ms" hx-target="#results" name="q">
3. ローディング状態を忘れる
htmxはローディング状態のためのhx-indicatorパターンを提供しています。必ず使いましょう:
<button hx-post="/process" hx-indicator="#spinner"> 送信 <img id="spinner" class="htmx-indicator" src="/spinner.svg"> </button>
2026年のリアリティチェック
業界が収束しつつある現実的なポイント:
-
SPAは死んでいない。 複雑でインタラクティブなアプリには依然としてクライアントフレームワークが必要です。React、Vue、Svelteはどこにも行きません。
-
SPAは使われすぎ。 本質的にはサーバー駆動CRUDなのに、クライアントSPAとして作られているアプリが多すぎる。「今はこうするもんだから」って理由だけで。htmxはそのオーバーエンジニアリングを炙り出しているわけです。
-
最良のアーキテクチャは問題に合ったアーキテクチャ。 NotionクローンにはReactが必要。管理画面にはhtmxが必要。SaaS製品には両方必要かもしれない。
-
htmxは「過去に戻る」ではない。 PHP時代の開発じゃありません。HTTPセマンティクス、モダンブラウザ、そして大半のインタラクションがリクエスト・レスポンスパターンであるという事実を活用する現代的なアプローチです。2026年のサーバーレンダリングインフラ(エッジ関数、ストリーミングHTML、CDNキャッシュフラグメント)がこの方式をかつてないほど高速でスケーラブルにしています。
-
JavaScriptエコシステムの疲弊は本物。 htmxの成長は純粋に技術的な利点だけが理由じゃない。200MBの
node_modulesを維持し、webpackの設定をデバッグし、半年ごとに新しい状態管理ライブラリを学ぶことに疲れた開発者の声でもあるんです。
アプリケーションが本当に何を必要としているか。そこだけを見てアーキテクチャを選んでください。Twitterのトレンドじゃなくて。大半のWebアプリにとって、答えはJavaScriptを増やすことじゃない。減らすことです。
関連ツールを見る
Pockitの無料開発者ツールを試してみましょう