ブログ記事

htmx実践入門 - ハイパーメディア駆動でシンプルなWeb開発

htmxは最小限のJavaScriptで高度なインタラクティブ機能を実現するライブラリです。SPAの複雑さに疲れた開発者に向けて、HTMLの拡張によるシンプルで保守しやすいWeb開発手法を解説します。

Web開発
htmx HTML JavaScript Web開発 フロントエンド
htmx実践入門 - ハイパーメディア駆動でシンプルなWeb開発のヒーロー画像

モダンな web 開発において、SPA フレームワークの複雑さに悩まされていませんか? htmx は「html を拡張する」というシンプルなアプローチで、複雑な javascript フレームワークなしに高度なインタラクティブ機能を実現します。 本記事では、htmx 2.0 の最新機能を含め、実践的な使い方を詳しく解説します。

この記事で学べること

  • htmx の基本概念とハイパーメディア駆動開発
  • 実践的な実装パターンとベストプラクティス
  • SPA との比較とコード量の削減効果
  • Alpine.js との組み合わせによる拡張
  • 実プロジェクトでの活用例とパフォーマンス

目次

  1. なぜ今 htmx なのか?SPA の複雑さからの解放
  2. ハイパーメディア駆動開発とは
  3. htmx の基礎 - 属性で Ajax を制御
  4. 実践的な実装パターン
  5. プログレッシブエンハンスメント
  6. サーバーサイドとの連携
  7. Alpine.js との組み合わせ
  8. パフォーマンスと SEO
  9. 実プロジェクトでの活用例
  10. まとめ - htmx が適している場面

なぜ今htmxなのか?SPAの複雑さからの解放

2025 年、多くの開発者が SPA フレームワークの複雑さに疲弊しています。 ビルド設定、状態管理、ルーティング、バンドルサイズ…本当にこれらすべてが必要でしょうか?

Web開発の複雑さの推移

チャートを読み込み中...

htmx が提供する価値:

  • コードベースを67%削減: react SPA と比較した実績
  • ビルドプロセス不要: html に直接記述
  • 学習曲線が緩やか: html 属性を覚えるだけ
  • SEOフレンドリー: サーバーサイドレンダリング

なぜ <a><form> だけが HTTP リクエストを送れるのか? この制約を取り除くことで、html は真のハイパーテキストになる。

Carson Gross htmx作者

ハイパーメディア駆動開発とは

ハイパーメディア駆動開発(Hypermedia-Driven Development)は、 アプリケーションの状態をサーバー側で管理し、html を通じてクライアントに伝える開発手法です。

// クライアント側で状態管理 const [users, setUsers] = useState([]); useEffect(() => { fetch('/api/users') .then(res => res.json()) .then(data => setUsers(data)); }, []); return ( <div> {users.map(user => ( <UserCard key={user.id} user={user} /> ))} </div> );
<!-- サーバーがHTMLを返す --> <div hx-get="/users" hx-trigger="load"> <!-- サーバーが生成したHTML --> </div> <!-- サーバー側のレスポンス --> <div id="users"> <div class="user-card">...</div> <div class="user-card">...</div> </div>
従来のSPAアプローチ
// クライアント側で状態管理 const [users, setUsers] = useState([]); useEffect(() => { fetch('/api/users') .then(res => res.json()) .then(data => setUsers(data)); }, []); return ( <div> {users.map(user => ( <UserCard key={user.id} user={user} /> ))} </div> );
htmxアプローチ
<!-- サーバーがHTMLを返す --> <div hx-get="/users" hx-trigger="load"> <!-- サーバーが生成したHTML --> </div> <!-- サーバー側のレスポンス --> <div id="users"> <div class="user-card">...</div> <div class="user-card">...</div> </div>

RESTからHATEOASへ

RESTとHATEOASのアプローチ比較
概念 REST API HATEOAS with htmx
データ形式 JSON HTML
状態管理 クライアント サーバー
次のアクション クライアントが決定 サーバーが提供
複雑さ 高い 低い

htmxの基礎 - 属性でAJAXを制御

htmx は、html 要素に属性を追加するだけで Ajax 機能を実現します。 基本的な属性を理解すれば、すぐに使い始められます。

基本的な属性

<!-- htmx 2.0をCDNから読み込み -->
<script src="https://unpkg.com/htmx.org@2.0.4"></script>

<!-- 基本的な使い方 -->
<button hx-post="/clicked" 
        hx-target="#result"
        hx-swap="innerHTML">
  クリックしてください
</button>

<div id="result">
  <!-- ここにサーバーからのレスポンスが挿入される -->
</div>

主要な属性一覧

htmxの主要な属性
属性 説明 使用例
hx-get GET リクエストを送信 hx-get="/users"
hx-post POST リクエストを送信 hx-post="/users/new"
hx-trigger トリガーイベントを指定 hx-trigger="click"
hx-target 更新対象の要素を指定 hx-target="#content"
hx-swap 更新方法を指定 hx-swap="outerHTML"
hx-indicator ローディング表示 hx-indicator="#spinner"

トリガーの詳細設定

<!-- 様々なトリガーの例 -->

<!-- 入力の500ms後に検索 -->
<input type="text" 
       hx-get="/search" 
       hx-trigger="keyup changed delay:500ms"
       hx-target="#search-results">

<!-- スクロールで追加読み込み -->
<div hx-get="/more-content"
     hx-trigger="revealed"
     hx-swap="afterend">
  さらに表示...
</div>

<!-- 定期的な更新 -->
<div hx-get="/notifications"
     hx-trigger="every 30s"
     hx-swap="innerHTML">
  通知エリア
</div>

実践的な実装パターン

実際のアプリケーションでよく使用されるパターンを紹介します。

1. フォーム送信とバリデーション

<form hx-post="/contact" 
      hx-target="#form-container"
      hx-swap="outerHTML">
  
  <div class="form-group">
    <label>名前</label>
    <input type="text" name="name" required>
  </div>
  
  <div class="form-group">
    <label>メールアドレス</label>
    <input type="email" name="email" required>
  </div>
  
  <div class="form-group">
    <label>メッセージ</label>
    <textarea name="message" required></textarea>
  </div>
  
  <button type="submit">送信</button>
  
  <!-- ローディング表示 -->
  <div class="htmx-indicator" id="spinner">
    送信中...
  </div>
</form>

サーバー側のレスポンス例(エラー時):

<form hx-post="/contact" hx-target="#form-container">
  <div class="alert alert-error">
    メールアドレスが正しくありません
  </div>
  <!-- フォームの内容を保持して返す -->
</form>

2. 動的コンテンツ更新

<!-- 削除ボタン -->
<tr id="user-123">
  <td>山田太郎</td>
  <td>yamada@example.com</td>
  <td>
    <button hx-delete="/users/123"
            hx-target="#user-123"
            hx-swap="outerHTML swap:1s"
            hx-confirm="本当に削除しますか?">
      削除
    </button>
  </td>
</tr>
<!-- インライン編集 -->
<div id="user-name-123" class="editable">
  <span hx-get="/users/123/edit"
        hx-trigger="click"
        hx-target="#user-name-123">
    山田太郎
  </span>
</div>

<!-- 編集フォーム(サーバーから返される) -->
<div id="user-name-123">
  <input type="text" 
         value="山田太郎"
         hx-put="/users/123"
         hx-trigger="blur"
         hx-target="#user-name-123">
</div>
<!-- ドラッグ&ドロップで並び替え -->
<ul hx-post="/tasks/reorder" 
    hx-trigger="end"
    hx-vals='js:{order: getTaskOrder()}'>
  
  <li draggable="true" data-task-id="1">
    タスク1
  </li>
  <li draggable="true" data-task-id="2">
    タスク2
  </li>
  <li draggable="true" data-task-id="3">
    タスク3
  </li>
</ul>

3. 無限スクロール

<div id="posts">
  <!-- 最初の投稿リスト -->
  <article>投稿1</article>
  <article>投稿2</article>
  <!-- ... -->
  
  <!-- 次のページを読み込むトリガー -->
  <div hx-get="/posts?page=2"
       hx-trigger="revealed"
       hx-swap="outerHTML"
       hx-indicator="#loading-spinner">
    <div id="loading-spinner" class="htmx-indicator">
      読み込み中...
    </div>
  </div>
</div>

プログレッシブエンハンスメント

htmx の大きな利点は、javascript が無効でも基本機能が動作することです。

<!-- JavaScriptが無効でも動作するフォーム -->
<form method="post" action="/search"
      hx-post="/search"
      hx-target="#results"
      hx-push-url="true">
  
  <input type="search" name="q" placeholder="検索...">
  <button type="submit">検索</button>
</form>

<div id="results">
  <!-- 検索結果がここに表示される -->
</div>

プログレッシブエンハンスメントのベストプラクティス

  1. 基本的な html 機能を先に実装
  2. htmx 属性で段階的に機能を拡張
  3. 重要な機能はサーバーサイドで処理
  4. クライアントサイドは ux の向上に集中

サーバーサイドとの連携

htmx はサーバーサイドの言語やフレームワークを選びません。 どんなバックエンドとも連携できます。

レスポンスヘッダーの活用

# Python/Flask の例
from flask import Flask, render_template, make_response

@app.route('/users/<int:id>', methods=['DELETE'])
def delete_user(id):
    # ユーザーを削除
    User.delete(id)
    
    # htmx用のレスポンスヘッダー
    response = make_response("", 200)
    response.headers['HX-Trigger'] = 'userDeleted'
    response.headers['HX-Redirect'] = '/users'
    
    return response

イベントの連携

<!-- クライアント側でイベントを受信 -->
<script>
  document.body.addEventListener('userDeleted', function(evt) {
    // トースト通知を表示
    showToast('ユーザーが削除されました');
  });
</script>

Alpine.jsとの組み合わせ

htmx と Alpine.js を組み合わせることで、必要最小限のクライアントサイドロジックを追加できます。

<!-- Alpine.jsでUI状態を管理 -->
<div x-data="{ open: false, count: 0 }">
  <button @click="open = !open">
    メニューを開く
  </button>
  
  <div x-show="open" x-transition>
    <ul hx-get="/menu-items" 
        hx-trigger="revealed once">
      <!-- メニュー項目がサーバーから読み込まれる -->
    </ul>
  </div>
  
  <!-- カウンターはクライアント側で管理 -->
  <button @click="count++" 
          hx-post="/track-click"
          hx-vals='js:{count: count}'>
    クリック数: <span x-text="count"></span>
  </button>
</div>

htmx + Alpine.jsの利点

  • htmx: サーバーとの通信と DOM 更新
  • Alpine.js: ui の状態管理とインタラクション
  • 合計でも 20KB 以下の軽量な構成
  • 学習コストが非常に低い

パフォーマンスとSEO

htmx を使用したアプリケーションのパフォーマンス特性を見てみましょう。

ページロード速度の比較

htmx (初回: 0.8秒) 100 %
完了
React SPA (初回: 2.3秒) 35 %
Next.js SSR (初回: 1.4秒) 60 %

同じ機能を持つダッシュボードアプリケーションでの比較

バンドルサイズの比較

JavaScriptバンドルサイズ(KB)

チャートを読み込み中...

SEOの利点

  1. サーバーサイドレンダリング: 常に完全な html を返す
  2. クローラビリティ: javascript に依存しない
  3. メタデータ管理: サーバー側で完全制御
  4. パフォーマンススコア: Core web Vitals で高得点

実プロジェクトでの活用例

実際のプロジェクトで htmx を活用した例を紹介します。

管理画面ダッシュボード

<div class="dashboard">
  <!-- サイドバー -->
  <nav class="sidebar">
    <a href="/dashboard" 
       hx-get="/dashboard/overview"
       hx-target="#main-content"
       hx-push-url="true">
      概要
    </a>
    <a href="/dashboard/users"
       hx-get="/dashboard/users"
       hx-target="#main-content"
       hx-push-url="true">
      ユーザー管理
    </a>
    <a href="/dashboard/settings"
       hx-get="/dashboard/settings"
       hx-target="#main-content"
       hx-push-url="true">
      設定
    </a>
  </nav>
  
  <!-- メインコンテンツ -->
  <main id="main-content">
    <div hx-get="/dashboard/overview" 
         hx-trigger="load">
      <!-- 初期コンテンツ -->
    </div>
  </main>
  
  <!-- リアルタイム通知 -->
  <div hx-ext="sse" 
       sse-connect="/notifications"
       hx-trigger="sse:notification">
    <div id="notification-area"></div>
  </div>
</div>

ECサイトの商品フィルタリング

<div class="product-listing">
  <!-- フィルター -->
  <aside class="filters">
    <form hx-get="/products"
          hx-target="#product-grid"
          hx-trigger="change">
      
      <h3>カテゴリー</h3>
      <label>
        <input type="checkbox" name="category" value="electronics">
        電化製品
      </label>
      <label>
        <input type="checkbox" name="category" value="clothing">
        衣類
      </label>
      
      <h3>価格帯</h3>
      <input type="range" 
             name="max_price" 
             min="0" 
             max="100000"
             hx-trigger="change throttle:500ms">
      
      <h3>並び順</h3>
      <select name="sort">
        <option value="newest">新着順</option>
        <option value="price_asc">価格が安い順</option>
        <option value="price_desc">価格が高い順</option>
      </select>
    </form>
  </aside>
  
  <!-- 商品グリッド -->
  <div id="product-grid" class="products">
    <!-- 商品一覧 -->
  </div>
</div>

React SPA版

450行のJavaScript、3つの状態管理

htmx版

50行のHTML属性、状態管理不要

89%のコード削減

保守性も大幅向上

まとめ - htmxが適している場面

htmx は万能ではありません。適している場面と適さない場面を理解して使用しましょう。

htmxが適している場面

htmxの適合度
用途 適合度 理由
管理画面・ダッシュボード ★★★★★ CRUD操作が中心でSEO不要
ECサイト ★★★★★ SEO重要、サーバー側で商品管理
ブログ・CMS ★★★★★ コンテンツ中心、高速表示
社内ツール ★★★★★ 開発速度重視、複雑なUI不要
フォーム中心のアプリ ★★★★☆ バリデーションをサーバー側で統一

htmxが適さない場面

  • リアルタイムコラボレーション: Google Docs のような複雑な同期
  • オフライン対応必須: PWA でのオフライン機能
  • 複雑なアニメーション: ゲームやインタラクティブな可視化
  • クライアント側の計算処理: 画像編集や 3D レンダリング

選定のポイント

「アプリケーションの状態をどこで管理すべきか」を考えましょう。 サーバー側で管理できる場合、htmx は素晴らしい選択肢になります。

導入のロードマップ

  1. 小規模な機能から開始: 検索フォームやページネーション
  2. 徐々に拡大: CRUD 操作、動的フォーム
  3. 必要に応じてAlpine.js追加: ui 状態の管理
  4. パフォーマンス測定: 実際の改善効果を確認

htmx は「シンプルさ」と「パワフルさ」を両立する、新しい web 開発のパラダイムです。 SPA の複雑さに疲れたら、ぜひ htmx を試してみてください。

htmx を採用してから、新機能の実装速度が 3 倍になりました。 コードベースもシンプルになり、新しいメンバーもすぐに開発に参加できます。

開発チーム Daily Hack編集部

この記事は役に立ちましたか?

Daily Hackでは、開発者の皆様に役立つ情報を毎日発信しています。