htmx SSE拡張機能によるリアルタイムUX
ほとんどのWebアプリケーションは、どこかの時点でライブ更新を必要とします。通知バッジ、プログレスバー、ポーリングなしで更新されるダッシュボードなどです。一般的な解決策はWebSocketsや完全なSPAフレームワークです。しかし、すでにhtmxを使用している場合、よりシンプルな方法があります:htmx SSE拡張機能です。これは、ほとんどJavaScriptを使わずに、サーバー送信イベントを直接HTMLに接続します。
重要なポイント
- Server-Sent Events(SSE)は、標準HTTPを介したサーバーからクライアントへの一方向ストリーミングを提供します。通知、ダッシュボード、ライブフィードに最適です。
- htmx SSE拡張機能は、別パッケージ(
htmx-ext-sse)として提供され、カスタムJavaScriptを必要とせず、宣言的な属性を使用してSSEストリームをHTMLに接続します。 - 3つのコア属性 —
sse-connect、sse-swap、hx-trigger="sse:<event>"— で、ほとんどのリアルタイムUXパターンをカバーできます。 - SSEはWebSocketsよりもデプロイが簡単で、ポーリングの無駄なリクエストを排除しますが、サーバーからクライアントへの通信のみをサポートします。
Server-Sent Eventsとは何か
Server-Sent Events(SSE)は、標準HTTP接続を介したサーバーからクライアントへの一方向ストリーミングのためのブラウザネイティブプロトコルです。サーバーは接続を開いたままにし、何か伝えることがあるときにテキストイベントをプッシュします。ブラウザはEventSource APIを通じてそれらを受信します。
ワイヤーフォーマットはプレーンテキストです:
event: priceUpdate
data: <li>BTC — $62,400</li>
各イベントには、オプションの名前とdataペイロードがあります。複数のdata:行は連結されます。イベントは空行で区切られます。
SSEはHTTP上で動作するため、特別な設定なしでプロキシやファイアウォールを通過できます。ネイティブで自動再接続をサポートしています。トレードオフは方向性です:接続が開かれると、クライアントはメッセージを送り返すことができません。通知、ダッシュボード、ジョブの進行状況、ライブフィードには完璧に適しています。チャットや共同編集の場合は、代わりにWebSocketsを使用します。
htmx SSE拡張機能のインストール
SSEサポートはhtmxコアには組み込まれていません。別のhtmx-ext-sseパッケージに存在します。両方のスクリプトをロードし、コンテナ要素で拡張機能を有効化します:
<head>
<script src="https://cdn.jsdelivr.net/npm/htmx.org@latest/dist/htmx.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/htmx-ext-sse@latest"></script>
</head>
<body hx-ext="sse">
npmベースのビルドの場合、npm install htmx-ext-sseでインストールし、エントリーファイルでhtmx.orgとhtmx-ext-sseの両方をインポートします。
注意: 以前のhtmxバージョンの古い
hx-sse属性は非推奨です。代わりに専用拡張機能でhx-ext="sse"を使用してください。
htmxストリーミング更新のためのコア属性
3つの属性で、ほとんどのリアルタイムUXパターンをカバーできます:
| 属性 | 目的 |
|---|---|
sse-connect="<url>" | EventSource接続を開く |
sse-swap="<event-name>" | 受信したHTMLを要素にスワップする |
hx-trigger="sse:<event-name>" | イベント到着時にhtmxリクエストを発火する |
プッシュごとに自身のコンテンツを置き換えるライブフィード:
<div hx-ext="sse" sse-connect="/feed" sse-swap="message">
読み込み中…
</div>
サーバーがdata: <p>新しいアイテム</p>に続いて空行を含むイベントを送信すると、htmxはdivの内容を置き換えます — JavaScriptは不要です。
Discover how at OpenReplay.com.
複数イベントの処理とリクエストのトリガー
1つのsse-connectで複数の子要素にフィードでき、それぞれが異なるイベント名をリッスンします:
<div hx-ext="sse" sse-connect="/stream">
<div sse-swap="statsUpdate"></div>
<div sse-swap="alertBanner"></div>
</div>
また、SSEイベントを使用して、コンテンツを直接スワップするのではなく、フォローアップHTTPリクエストをトリガーすることもできます。これは、イベントが新しいデータが利用可能であることを通知するが、htmxに完全にレンダリングされたフラグメントを取得させたい場合に便利です:
<div hx-ext="sse" sse-connect="/events">
<div hx-get="/notifications" hx-trigger="sse:newNotification">
<!-- 各SSEイベントで更新される -->
</div>
</div>
サーバーが完了を通知したときにストリームを適切に閉じるには、sse-close="done"を追加します — doneという名前のイベントが到着すると接続が閉じます。
SSEがポーリングやWebSocketsに勝る場合
- ポーリングと比較: SSEは無駄なリクエストを排除します。サーバーは何か変更があったときのみプッシュします。
- WebSocketsと比較: SSEはデプロイが簡単で、HTTP/1.1とHTTP/2で動作し、特別なサーバーインフラストラクチャを必要としません。双方向通信が必要な場合のみWebSocketsを使用してください。
実用的な注意点:HTTP/1.1ブラウザは、ドメインごとの接続数を6に制限します。ユーザーが複数のタブを開くと、SSE接続がその制限を奪い合います。HTTP/2で提供すると、単一の接続上で複数のストリームを多重化することで、この制限をほぼ回避できます。
まとめ
htmx SSE拡張機能を使用すると、いくつかのHTML属性と接続を開いたままにする方法を知っているサーバーエンドポイントだけで、本物のHTML-over-the-wireリアルタイムUI — ライブダッシュボード、進行状況インジケーター、通知ストリーム — を追加できます。状態管理ライブラリ、クライアントサイドルーティング、ビルドパイプラインは不要です。サーバーがテキストをストリーミングできれば、UIをライブにできます。
よくある質問
いいえ。Server-Sent Eventsは厳密にサーバーからクライアントへの一方向です。データを送り返す必要がある場合は、hx-postまたはhx-putを使用した標準htmxリクエストとSSEストリームを組み合わせてください。リアルタイムチャットなどの完全な双方向通信には、WebSocketsがより良い選択です。
ブラウザの組み込みEventSource APIは、短い遅延の後に自動的に再接続を試みます。サーバーは、イベントストリームにretryフィールドを含めることで、再試行間隔を制御できます。htmx SSE拡張機能は、追加の設定なしでこの再接続動作を継承します。
はい、実際にHTTP/2が推奨されます。HTTP/1.1では、ブラウザはドメインごとの同時接続数を約6に制限するため、開いているSSEストリームを持つ複数のタブがその制限を使い果たす可能性があります。HTTP/2は単一の接続上でストリームを多重化し、実質的に上限を取り除きます。
コンテナ要素のsse-close属性値に一致する名前付きイベントを送信します。たとえば、sse-closeをdoneに設定した場合、doneという名前のイベントを送信すると、拡張機能はクライアント側でEventSource接続をクリーンに閉じます。
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.