Webフロントエンドの在り方をかえるかもしれない?htmx入門

htmxという技術をご存知ですか?
XHTMLやXMLの仲間?いえいえ、名前から何となく似ている気がしますが、全く別物です。

私自身つい最近知った技術なのですが、Webフロントエンドの開発においてかなり便利な技術だと感じています。日本では今のところ知名度はそれほど高くないようですが、もしかすると今後トレンドになる可能性のある技術なのでは?と思っています。

今回はフロントエンドの技術であるhtmxについて紹介したいと思います。

htmxとは

名前から何となくイメージできる方もいるかと思いますが、htmxはHTMLを拡張するための技術です。以下に概要をまとめます。

  • 実態はJavaScriptで作られたライブラリ
  • htmxを導入するとHTML要素を拡張することができ、JavaScriptのコードを書かずともモダンなWebフロントエンドを作ることができる
  • HTMLタグにhx-から始まる専用の属性を付けることで、HTMLの機能を拡張する

具体的にHTMLがどのように拡張されるのかというと、例えば以下のようなことができるようになります。

  • 任意の要素からHTTPリクエストを発行できる
    • aタグとformタグ以外からもリクエストを発行できる
  • 任意のイベントでリクエストを発行できる
    • クリック以外の動作でリクエストを発行できる
  • PUT, PATCH, DELETEなどのHTTPリクエストが発行できる
    • GET, POST 以外のHTTPリクエストを発行できる
  • レスポンスされたHTMLを任意の箇所に挿入・置換できる
    • 画面遷移してWebページ全体を置き換える必要がない

以下は、htmxを使って、HTML標準のbutton要素からDELETEメソッドでHTTPリクエストを発行するサンプルコードです。

<button hx-delete="/item/10"
		hx-target="body"
		hx-push-url="true"
		hx-confirm="本当に削除しますか?"
		class="delete-btn"
>
  削除
</button>

こちらのコードの詳細は後述しますが、このようにhx-から始まる属性を追加することによって、JavaScriptのコードを書かずとも様々なことを実現できるようにするライブラリがhtmxです。

SPA・MPAとhtmxの関係

現状、個人的にhtmxは画期的なライブラリだなと感じているのですが、既にSPA(Single Page Application)で構成されているWebアプリであれば、途中からhtmxを導入するメリットは少ないかもしれません。

なぜなら、SPA構成で作られたWebアプリの場合、先に上げたhtmxで実現できること(任意の要素で、任意のイベントで、任意のHTTPリクエストを発行し、任意のHTML要素を置き換える、など。)は、JavaScriptで簡単に実現可能だからです。
また、リッチなUIを実現しようと思うと、htmxだけでは実現が難しいケースもあります。そのようなケースでは、SPAがアプリケーションの構成として選ばれる場面はまだまだありそうです。

一方で、SPA構成のWebアプリケーションは良いことばかりではありません。
開発においてはJavaScriptやTypeScript、SPAを実現するためのフレームワーク(Vue.jsやReact.js)における高度な知識が必要になる場合もあります。
SPA構成のアプリケーションではフロントエンドとバックエンドで分かれるため、アプリケーションを稼働させるためのインフラ構成も複雑化する傾向があります。また、SPAのWebサイトは性質上SEO対策がしにくいというデメリットもあります。
SPAのいくつかの弱点を補うためにSSR(Server-Side Rendering)を実現する技術(Next.jsやNuxt.jsなど)もありますが、開発やインフラ構成が複雑化しやすい点ではSPAとあまり変わりません。

新しくWebアプリケーションを開発する場合、SPAを選択することがかなり一般的にはなっていますが、要件としてリッチなUIを求められないのであれば、MPA(Multi-Page Application。リクエストのたびに画面遷移してページ全体を置き換えるようなWebアプリケーション)構成が選ばれるケースは現代においても少なくありません。そして、MPA構成のアプリでは、htmxを導入することで多くのメリットがあります。

htmxのメリット

私が思うhtmxを導入することのメリットは大きく2つです。

  • 要素の部分的な置き換えが簡単にできる
  • CRUD処理の設計をきれいにできる

要素の部分的な置き換えが簡単にできる

MPA構成のWebアプリケーションでは、リクエストの度に画面遷移をすることになるため、画面上の要素を部分的に置換することが難しくなります。
例えば、MPAの場合、何かしらのリソースに対する一覧画面(例えば、ユーザーの一覧画面など)で、検索機能やページング機能を実装した場合、「検索ボタン」や「次ページ」「前ページ」リンクをクリック時には画面全体が切り替わるのが一般的です。一方でSPA構成のWebアプリの場合、一覧の中身のデータだけが置き換わるのが一般的です。Webアプリ利用者の観点では、画面全体が置き換わるよりも、SPAのように変更がある部分だけが置き換わる方が使いやすいです。

もちろん、MPA構成のアプリケーションでも、JavaScriptを駆使することで要素の中身だけを入れ替えることは可能です。しかし、MPAのアプリケーションでリッチなUIを実現しようとするとどうしてもJavaScriptのコード量が増え、長期的にメンテナンスしやすいコードを保つのは難しくなってしまいます。

htmxを使うと、MPAのWebアプリでもJavaScriptを書くことなく、検索やページング処理で中身だけを入れ替えるなどのUIが簡単に実現可能になり、開発者にとって低コストでユーザーにやさしいUIを作りやすくなります。

CRUD処理の設計をきれいにできる

CRUDとはCreate, Read, Update, Deleteの頭文字を取った言葉で、データベースを使用するようなアプリケーションでリソースに対する操作の総称として使われることが多い言葉です。

一般的な業務アプリなどでは、管理するリソース(ユーザーや商品など)に対してCRUD処理を実装することが多いかと思います。MPA構成のWebアプリケーションの場合、HTML標準のformタグを使ってリクエストを送信する場合が多いですが、formタグが標準でサポートしているHTTPメソッドはGETとPOSTしかありません。そのため、更新や削除の処理もPOSTメソッドで対応するケースが多くなってしまいます。
しかし、HTTPではPUTやPATCH、DELETEなどのHTTPメソッドが標準で定められているため、操作の内容に応じて適切なHTTPメソッドを使う方がWebアプリケーションの設計としては適切です。

こちらに関してももちろん、JavaScriptからリクエストを送るようにすればPUTやDELETEでリクエストを送信することは可能ですが、基本的に画面遷移することを前提とするMAPのWebアプリケーションではCRUD処理をJavaScriptで実装することは相性があまり良くありません。

htmxを用いると、HTML要素に属性を付与するだけでPUT, PATH, DELETEなどのHTTPメソッドが使えるようになるので、アプリケーション全体としての設計がより洗練されたものにできます。

サンプルコード

DELETEメソッドによる削除の実装

ここからは具体的なコードを確認しながらhtmxについて、できることを簡単に解説します。
まずは、先にも紹介した削除ボタンのコードから。

<button hx-delete="/item/10"
		hx-target="body"
		hx-confirm="本当に削除しますか?"
		class="delete-btn"
>
  削除
</button>

hx-deleteは、DELETEメソッドでHTTPリクエストを送信するための属性です。値にはリクエストのURLを指定します。同じようにhx-put, hx-patchなどがあります。(もちろんGETとPOSTもあります。)このように属性によって、GETとPSOT以外のHTTPメソッドが送信できるようになります。

hx-targetは、レスポンスをどの要素に置き換えるかを指定するものです。ここではbodyを指定しているため、画面全体が置き換わり、実質画面遷移しているのと同じ動作になります。

hx-confirmは、リクエスト送信前に確認ダイアログを出すための属性です。削除のような、不可逆になる操作では、誤ってボタンを押してしまった場合の対処として、一度確認を挟むのが一般的です。こういった処理を実現しようと思うと、一度JavaScriptの処理を挟む必要がありますが、htmxではこのような処理も属性一つで対応可能です。
ただし、この場合JavaScriptの標準の確認ダイアログ(window.confirmと同じもの)しか出ません。自作のモーダルウィンドウを用意し、リッチなUIを実現しようと思うと、多少JavaScriptのコードが必要になります。とはいえ、簡単な確認だけで良いのであれば、これだけで十分なケースも多いので、実装がとても簡単になります。

この例では、サンプルコードを簡潔にするために、リクエストURLを/item/10としています。この10はリソースのIDを想定しており、実際のアプリケーションのコードではデータに応じて動的になってほしいです。
その場合は、使用しているWebアプリケーションフレームワークのテンプレートエンジンを使って値を埋め込みます。
例えば、Thymeleaf(SpringBootのテンプレートエンジン)を使う場合、以下のような書き方でid部分を動的にすることができます。

th:hx-delete="@{/item/{id}(id=${item.id})}"

検索とページングの実装

キーワード検索とページングを実現するユーザー一覧画面を参考にhtmxを解説します。
画面は以下のイメージです。

ユーザー一覧画面

ソースコードは以下になります。
ここではテンプレートエンジンとしてThymeleafを使用しています。
※CSSとバックエンドのコードは省略します。

<!DOCTYPE html>  
<html lang="ja"  
      xmlns:th="http://www.thymeleaf.org"
>  
<head>  
	<meta charset="UTF-8">
	<title>ユーザー一覧</title>  
	<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.min.js" integrity="sha384-/TgkGk7p307TH7EXJDuUlgG3Ce1UVolAOFopFekQkkXihi5u/6OCvVKyz1W+idaz" crossorigin="anonymous"></script>
</head>  
<body>  
<div >  
	<h1>ユーザー一覧</h1>  
    <div id="search">  
	    <label for="keyword">キーワード検索</label>  
	    <input id="keyword" type="text" name="q" placeholder="キーワード検索">  
	    <button 
		    hx-get="/users"  
		    hx-include="#search"  
		    hx-push-url="true" 
		    hx-target="#main"   
		    hx-swap="outerHTML"  
		    hx-select="#main"  
        >	
        Search  
        </button>  
    </div>    
    <div id="main">  
		<table>
			<thead>
				<tr>
				    <th>ID</th>  
				    <th>Name</th>  
			        <th>Email</th>  
		        </tr>
			</thead>
			<tbody>        
				<tr th:each="user : ${users}">  
		          <td th:text="${user.id}"></td>  
		          <td th:text="${user.name}"></td>  
		          <td th:text="${user.email}"></td>  
		        </tr>     
		    </tbody> 
		</table>      
		<div>        
			<a th:if="${page > 1}"  
	           th:href="@{/users(q=${search}, page=${page - 1})}"  
	           th:hx-get="@{/users(q=${search}, page=${page - 1})}"  
	           hx-push-url="true"  
	           hx-swap="outerHTML"  
	           hx-select="#main"  
	           hx-target="#main"  
            >
            前ページ
	        </a>  
	        <a th:if="${#lists.size(users) == 10}"  
	           th:href="@{/users(q=${search}, page=${page + 1})}"  
	           th:hx-get="@{/users(q=${search}, page=${page + 1})}"  
	           hx-push-url="true"  
	           hx-swap="outerHTML"  
	           hx-select="#main"  
	           hx-target="#main"  
            >
            次ページ
	        </a>  
	    </div>    
	</div>
</div>  
</body>  
</html>

まず、head要素内で、htmxをscriptタグで読み込んでいます。これにより、hx-からはじまるhtmxの属性を使用できるようになります。

続いてキーワード検索の部分です。

<div id="search">  
	<label for="keyword">キーワード検索</label>  
	<input id="keyword" type="text" name="q" placeholder="キーワード検索">  
	<button 
		hx-get="/users"  
		hx-include="#search"  
		hx-push-url="true" 
		hx-target="#main"   
		hx-swap="outerHTML"  
		hx-select="#main"  
>	
	Search  
	</button>  
</div>

通常のHTMLでは、リクエストにパラメータを含めたい場合、form要素内にinput要素を含め、submitボタンでリクエストを送信する必要があります。
htmxでは、hx-include属性で要素を指定すると、指定した要素内に含まれるinput要素を自動的にリクエストに含めてくれます。hx-getでURLを指定することで、formタグやaタグがなくてもGETリクエストが送信可能になります。

hx-push-urlは、ブラウザのhistoryに追加するかどうかの設定です。trueにしておくと、ブラウザの戻るボタンを押すと、前のページに戻ります。falseにすると、ブラウザのhistoryには追加されずに、要素の置き換えだけが行われます。

このサンプルでは、バックエンドでは検索結果を含むHTML全体がレスポンスされることを想定しています。ページ全体が置き換わった場合、従来型のMPAのページと変わりはありません。しかし、htmxを使えば、SPAで作成したWebページのように、画面の一部のみを置き換えることが可能になります。
hx-targetは、要素の置き換え先です。hx-swapは、要素の差し替え方式です。デフォルトではinnerHTMLで、ターゲット要素のHTMLの内側を置き換えます。outerHTMLを指定すると、ターゲット要素全体をレスポンスで置き換えます。hx-selectでは、レスポンスで返ってきたHTMLから、どの要素を取得するかを指定できます。
つまり、今回のサンプルでは、レスポンスで返ってきたHTMLからid属性がmain要素を取得し、それを#mainの要素に置き換えます。
その結果、一覧に表示される結果のみが置き換わり、画面遷移せずとも要素の一部が置き換わる動作になります。

ページングの処理も考え方は同じです。ページ番号をリクエストに含めることで、対象のページを取得し、#mainの要素だけを置き換えることができます。

この検索とページング処理における開発時のメリットは、バックエンドの開発において、フロントがhtmxであることを意識する必要がないという点です。従来通りのMPAとしてページ全体のHTMLを返すような作りをしていても、htmxによってレスポンスから要素の一部を取得して置き換えることが可能になっているため、バックエンドでhtmxに合わせた形で開発する必要は特にありません。
※どうしてもhtmxに合わせた実装をしたい場合は、htmxからのリクエストはHX-Requestというヘッダーが付くので、その値の有無によって分岐させることは可能です。

その他にできること

ここではhtmxの機能のほんの一部を紹介しました。
実際にはまだまだできることがたくさんあります。

興味がある方は是非調べてみてください。

また、今回サンプルコードを作成するにあたって、以下の書籍を参考にしました。ここで紹介した内容はほんの一部であり、書籍を読んでいると、「こんなことまでJavaScriptなしで実現できるのか!」と驚かされます。
htmxの知識だけでなく、そもそものWebの在り方から色々と考えさせられます。

ハイパーメディアシステム

ハイパーメディアシステム | 技術評論社

近年急速に注目を集めるフロントエンドライブラリ「htmx」について、その作者自身らが執筆した解説書です。 htmxの魅力はそのシンプルさにあります。そのシンプルさは、30年にわたりウェブを支えてきたハイパーメディアの力を再発見し、HTMLそのものを拡張するという発想から生まれました。 本書の第1部では、ハイパーメディアの歴史としくみを紐とき、ウェブの根幹でありながら現代では誤解されがちなRESTの本来の概念を丁寧に解説します。そして、Web 1.0スタイルのアプリケーションを作成しながら、ハイパーメディアの中核的なコンセプトをおさらいします。 第2部では、ハイパーメディアの力を最大限に引き出すhtmxのしくみと使い方を紹介します。「あらゆる要素からHTTPリクエストを発行できる」「任意のイベントでリクエストをトリガできる」といったhtmxの基本概念を、実際にウェブアプリケーションを作成しながら学びます。 第3部では、モバイル版のhtmxともいえる「Hyperview」を紹介します。モバイルアプリにもハイパーメディアの概念を取り入れることで、htmxと同様の強力さとシンプルさを兼ね備えたモバイルアプリケーションを作成できることを示します。 複雑化した現代のフロントエンド開発に疲れてしまった方は、本書を手にぜひ一度、htmxにチャレンジし、ハイパーメディアシステムとしてのウェブの本来の力を再発見してください。こんなにシンプルで軽やかなウェブ開発の方法があったのかと、きっと驚かれるはずです。

https://gihyo.jp/book/2025/978-4-297-14945-1

まとめ

日本だとhtmxに関する書籍もまだ少なく、htmxが今後Webアプリの開発においてどれだけ浸透していくのかは正直なところ分かりません。情報が少ない段階では、AIを使った開発で学習データが少なくて不利になる可能性もありそうです。

ですが、学んでいる中で個人的には将来性のある技術なのではと密かに期待しています。新しいWebアプリやWebサイト開発の案件に携わる方は、技術選定の中で候補の1つとして頭の片隅にとどめておいて損はないかもしれません。

余談

私がhtmxを知ったのは、書店に行った時にたまたま先に紹介したハイパーメディアシステムの書籍を目にした時でした。htmxが聞いたことがない技術であったこと、そして表紙に書かれていた 「React 疲れ」していませんか?の一言が妙に目を引きました。

とはいえ、htmxに関する書籍が他になかったので、その時点ではまだ日本ではあまり浸透してない技術なんだろうと思いました。ソフトウェアの世界は時代の変化が速く、たとえ一度流行ったとしても長く使われる技術かどうかは分かりません。そういうことを色々と考えた結果、結局買わずに書店を出てしまいました。

しかし、後日htmxのことが気になってAIに質問みると、面白そうな技術だなと思い、興味が沸いてきて結局電子書籍で購入しました。技術書は紙で読む方が好きなので、書店に行ったときに買っておけばよかったなと少し後悔しています。気になる本は出合ったときにすぐ買っておくべきですね。