各レシピは、短縮形・展開後の HTML・任意のマクロ・サーバレスポンス契約を
ドキュメント化しています。必要なもの列に記載のあるレシピは
@hypermedia-components/coreの
installX() を必要とします。それ以外(「—」)は純粋な htmx 属性だけです。
任意のレシピのスキャフォールドは
npx @hypermedia-components/cli add <name> で取得できます。コピーは
npx @hypermedia-components/cli validate で契約に対して機械検証できます。
各レシピページにはライブデモもあります。ほぼすべてのデモは、
このサイトの Worker が api/recipes/<name>/ 配下で実装する
各レシピのサーバ応答契約に対して、実際の htmx リクエストを送ります
(例外: コピー、
条件付きフィールド、
未保存ガードは
クライアントのみ、ストリーミング応答は
chat-messages のエンドポイントを共有)。
フォームの構築と送信 — バリデーション、ウィザード、サーバ往復を
必要とする入力たち。
| レシピ |
やりたいこと |
必要なもの |
| ミューテーションフォーム |
インライン 4xx エラー・成功時リダイレクト・二重送信ガードつきでフォームを送信する |
installFieldErrors() |
| フィールドエラー |
サーバが送るバリデーションエラーフラグメントを、名前の一致するフィールドへ振り分ける |
installFieldErrors() |
| 条件付きフィールド |
選択中のモードが読まないフィールドを、往復なしで宣言的に隠す |
installShowWhen() |
| マルチステップフォーム |
ウィザードを作る — ステップとドラフトはサーバが所有し、ステップ全体をスワップ |
— |
| インライン編集 |
値を表示モードと編集モードの間でその場でスワップする(クリックして編集) |
— |
| ファイルアップロード |
ライブ進捗バーと out-of-band フォームリセットつきでマルチパートアップロードする |
installUploadProgress() |
| 郵便番号住所補完 |
郵便番号から住所を補完したい — マスク付き入力、1 回のルックアップ GET、アウトオブバンドの自動入力 |
installMask() |
| マスタ参照 |
マスタを参照する — change で検証されるコード直接入力+検索ダイアログのピッカー。未解決コードは hidden の id を必ずクリア |
installRemoteDialog() + installCloseDialog() |
| カスケードセレクト |
階層選択を連鎖する(依存)select で行う — change のたびに次のレベルを読み込み |
— |
| トランスファー |
デュアルリストボックスの割り当て / 解除をサーバ往復で行う — フォーム全体のスワップ、カスタム JS ゼロ |
— |
作業中の内容を守る — ドラフト、ダーティ状態、セッション切れ、
落ちた接続、そして競合・重複する保存。
| レシピ |
やりたいこと |
必要なもの |
| 未保存ガード |
編集が失われる前に警告したい — data-dirty 状態、タブを閉じる際のプロンプト、boosted ナビの確認、保存でクリーン |
installDirtyGuard() |
| 自動保存 |
入力と同時にフォームをドラフトしたい — デバウンス送信、復元バナー、新規 JS ゼロ |
— |
| 編集競合 |
楽観ロックにしたい — 隠し version が毎回の保存に同乗し、古い保存は 409 の競合ダイアログに |
— |
| 冪等キー |
二重送信をサーバ側で無害化する — 描画フォームごとにキー 1 つ。リプレイには元のレスポンスを返し、エラーにしない |
— |
| セッション切れ再認証 |
401 をログインダイアログにして、中断されたリクエストを再実行したい |
installSessionExpiry() |
| ネットワーク再試行 |
応答がまったく来なかったリクエスト(オフライン / タイムアウト)を、動く Retry 付きで表面化する |
installNetworkRetry() |
要素ひとつ、リクエストひとつ — ボタン、ガード、小さな
クライアントサイドユーティリティ。
| レシピ |
やりたいこと |
必要なもの |
| リクエストアクション |
ビジー状態つきのボタンから htmx リクエストを発行する |
— |
| 確認アクション |
破壊的リクエストを確認ダイアログでガードする |
installConfirm() |
| 削除の取り消し |
即座に削除する(楽観的削除)— 猶予期間内ならトーストの Undo で行を復元 |
installToast() |
| クリップボードにコピー |
読み取り専用の値(URL、トークン、スニペット)をクリップボードへコピーする |
installCopy() |
| 並べ替えリスト |
サーバが順序を所有するリストをポインタ/キーボードで並べ替え、リクエスト 1 本で永続化する |
installSortable() |
業務レコードが通る契約 — 明細行、ライフサイクル、一括投入、
長時間処理。
| レシピ |
やりたいこと |
必要なもの |
| 明細行 |
受注・見積の明細行を編集する — 追加・削除・再計算はフォーム全体の往復。計算はすべてサーバが所有 |
— |
| ワークフローアクション |
レコードのライフサイクルを駆動する — サーバは合法な遷移だけを描画。コメント必須の遷移は 422、陳腐化した操作は現在の真実で 409 |
— |
| CSV インポート |
驚きなしの一括投入 — アップロード、検証レポート、トークン付き確定 |
— |
| 非同期ジョブ |
リクエストより長生きする処理を回す — 202 + 終端状態を列挙した自己ポーリングのジョブカード |
— |
hc-datagrid
コンポーネントを運用するためのすべて — ページング、ソート / フィルタ /
列のツーリング、インライン編集のエラーワイヤ、そして大きなグリッドを
正直に保つパターン。データグリッドページ
テンプレートが、これらすべてが載るレイアウトを組み立てます。
初めてなら、全体を構築順で地図化した
データグリッドガイドから
始めてください。
| レシピ |
やりたいこと |
必要なもの |
| データグリッドのページネーション |
データグリッドをサーバ駆動でページングする — out-of-band の行スワップつき |
installDatagrid() |
| データグリッドの並び替え |
適用中のソート集合を述べ、その場で編集させたい — 並べ替え可能な順序付きリスト。表示していない列のキーも扱える |
installSortList() |
| データグリッドの列フィルタ |
列ごとのフィルタポップオーバー — 名前空間付き f-col パラメータでグリッドを GET し、トリガーが data-filtered で戻る |
— |
| データグリッドの列選択 |
ユーザーごとにグリッドの列を選ばせたい — チューザーフォームが cols= の繰り返しパラメータ付きでグリッドを GET |
— |
| データグリッドの列プリファレンス |
ユーザーごとの列幅・列順を記憶 — ミラー入力がリサイズをオートセーブ。ソータブルなチューザーの cols= 順が列順 |
— |
| 行から詳細へ |
行が指すレコードを開いて同じ一覧に戻る — 同一性の列のリンク、戻りの #row-<id>、結果セットや選択を辿る |
installRowLink() |
| データグリッドのツリー |
グリッド内の階層 — aria-level の行が子を親の直後に遅延ロード |
— |
| データグリッド無限スクロール |
ページ番号なしの無限リスト — revealed の番兵行が次のカーソルバッチをスワップ |
— |
| データグリッドのスナップショットページャー |
ワークキューの母集合を検索時点で凍結する — フォームが行キーを運び、処理済み行は見え続け、消えた行はトゥームストーンに |
installDatagrid() |
| データグリッドの編集エラー |
グリッドの楽観的コミットのループを閉じる — 422 がマーク付きセル+エラー行つきでレコードを再描画 |
— |
| データグリッドの編集競合 |
行単位の楽観的ロック — 古いバージョンは 409 で上書き/破棄つきの競合行へ |
— |
| データグリッド一括操作 |
チェックボックスで行を選択し、1 つのアクションを全行に POST する |
installDatagridActions() |
| データグリッドの一括操作エラー |
大量エラーの一括操作 — best-effort と atomic、理由別集約、レポートから行へのリンク |
— |
| レシピ |
やりたいこと |
必要なもの |
| ライブ検索 |
入力に応じて検索する(タイプアヘッド)— デバウンスされた入力で結果をスワップ |
— |
| 検索結果の上限 |
1 回の検索が返せる件数を制限する — cap+1 検出、「cap+」件数、残り続ける切り詰めバナーまたはハードリジェクト |
— |
| フィルタポップオーバー |
フィルタフォームをポップオーバーに載せ、成功時に閉じる |
installClosePopover() |
| 保存ビュー |
現在のフィルタに名前を付けて、ただのリンクとして呼び戻したい — サーバー保存のクエリ文字列 |
— |
| レシピ |
やりたいこと |
必要なもの |
| レイジーパネル |
パネルの読み込みを、初めて表示されるまで遅延する |
— |
| レイジーツリー |
ツリーの枝の子を初回展開時に読み込む |
installTree() |
| データ領域 |
HX-Trigger の無効化を起点に領域を自己更新する |
— |
| レシピ |
やりたいこと |
必要なもの |
| SSE 更新 |
Server-Sent Events でサーバからフラグメント更新をプッシュする — 名前付きイベントと out-of-band プッシュ |
— |
| SSE トースト |
JSON ペイロードつき SSE イベントを許可リスト方式の DOM イベントへ変換する — トーストはそのまま動作 |
installSseDispatch() |
| チャットメッセージ |
コンポーザーの 1 POST でチャットのやり取り(ユーザーメッセージ + aria-busy のアシスタントプレースホルダー)を追記。422 経路はトランスクリプトを汚しません |
installChatScroll() |
| ストリーミングレスポンス |
アシスタントのプレースホルダーを SSE で埋める — aria-busy が読み上げを保留する間チャンクを追記し、done が最終メッセージに置換してストリームを閉じます |
— |
| レシピ |
やりたいこと |
必要なもの |
| リモートダイアログ |
ダイアログ(モーダル)の中身を、開くときにサーバから読み込む |
installRemoteDialog() + installCloseDialog() |
| トースト |
クライアントまたは HX-Trigger ヘッダーからトースト通知(スナックバー)を発火する |
installToast() |
| 未読バッジ |
アプリクロームの通知カウントを新鮮に保ち、ゼロでは正直に消し、自分の操作で即時補正する |
— |
| レシピ |
やりたいこと |
必要なもの |
| チャート |
サーバが送るデータテーブルを Observable Plot の SVG チャートへ昇格する |
installChart() + Plot |