10 ポイント 投稿者 GN⁺ 2025-10-11 | 2件のコメント | WhatsAppで共有
  • HTMX を使うことでコード量を約70%削減できた一方、UI間の同期の問題に加え、フロントエンドの状態管理の複雑さが増した
  • Datastar 導入後は、リアルタイムのマルチユーザーアプリ開発において WebSockets なしで簡潔なコード構成と保守のしやすさ を実現できた
  • HTMX が HTML属性を中心に動作ロジックを分散 させるのに対し、Datastar は サーバー主導の更新モデル によりロジックの 一貫性と保守性 を高める
  • Datastar API は属性が少なく、コードの 可読性と生産性 が向上すると感じた
  • Datastar は Server-Sent Events(SSE)Web ComponentsCSS View Transitions などの Webネイティブ技術を積極的に活用 し、リアルタイム協調と再利用可能なコンポーネント構造を可能にする

紹介と動機

  • 2022年、David Guillot が DjangoCon Europe で、ReactベースのSaaSを HTMX に移行してコード量を約70%削減し、機能改善も達成した事例を共有した
  • その後、多くのチームが単一ページアプリ(SPA)からマルチページの ハイパーメディアアプリ へ移行する中で、コード削減と 開発者体験・ユーザー体験 の両方の向上を経験した
  • 筆者自身も HTMX から Datastar へプロジェクトを移行し、コードがさらに短くなり、WebSocket や複雑な状態管理なしに リアルタイムのマルチユーザーアプリ を開発できることを確認した

移行のきっかけになった問題点

  • FlaskCon 2025 発表の準備中に、HTMX と AlpineJS を組み合わせて UI を同期しようとしたが、UI 同期の問題に直面した
    • 両ライブラリは異なる開発者が作った別個のツールであり、相互通信できない ため、開発者が自分で統合作業を担う必要があった
    • さまざまなタイミングでコンポーネントを初期化し、イベントを調整する過程で、想定以上のコード記述とデバッグ時間を要した
  • Datastar が2つのライブラリの機能を統合しつつ、11KB未満のサイズ で提供されている点に注目して試してみた
    • モバイル端末ユーザーのページ読み込み性能の改善にも有利

より優れた Datastar の API 設計

  • Datastar の API は HTMX より はるかに軽量な印象 があり、望む結果を得るために追加すべき属性(attribute)の数が少ない
  • HTMX はほとんどの相互作用で複数の属性を必要とする
    • URL 定義、ターゲット要素の指定、応答の処理方法などをそれぞれ別の属性で設定する
    • 通常は 2〜3個の属性 を毎回使い、時には継承チェーンをたどって属性の動作方式を確認しなければならない
    <a hx-target="#rebuild-bundle-status-button"  
       hx-select="#rebuild-bundle-status-button"  
       hx-swap="outerHTML"  
       hx-trigger="click"  
       hx-get="/rebuild/status-button"></a>  
    
  • Datastar は通常、たった1つの属性 で同じ機能を実装できる
    <a data-on-click="@get('/rebuild/status-button')"></a>  
    
    • 数か月後にコードを見直しても、動作方式を簡単に理解できる

動作原理の違い

  • HTMX はフロントエンドライブラリ として HTML 仕様の拡張を目指す一方、Datastar はサーバー主導ライブラリ として高性能な Web ネイティブのリアルタイム更新アプリケーション構築を目指す
  • HTMX はリクエストを発火する要素に属性を追加して動作を定義するため、ページ内の離れた要素を更新する場合でもロジックが複数レイヤーに分散する
  • Datastar は サーバーが何を変更するかを決定 し、すべての更新ロジックを1か所に集中させる
  • HTMX の例

    <div>  
      <div id="alert"></div>  
        <button hx-get="/info"   
                hx-select="#info-details"   
                hx-swap="outerHTML"  
                hx-select-oob="#alert">  
            Get Info!  
        </button>  
    </div>  
    
    • ボタンを押すと /info に GET リクエストを送り、応答内の info-details ID 要素でボタンを置き換え、さらに応答内の alert ID 要素でページ上の同じ ID の要素を置き換える
    • ボタン要素が知っておくべき情報が多すぎるうえ、サーバーが返す内容を事前に知る必要があるため、HTMX の「行動の局所性(locality of behavior)」 の原則が弱まる
  • Datastar の改善されたアプローチ

    <div>  
        <div id="alert"></div>  
        <button id="info-details"  
        data-on-click="@get('/info')">  
            Get Info!  
        </button>  
    </div>  
    
    • サーバーは同じ ID を持つ2つのルート要素を含む HTML 文字列を返す
      <p id="info-details">These are the details you are looking for…</p>  
      <div id="alert">Alert! This is a test.</div>  
      
    • シンプルで高性能な選択肢である

コンポーネントレベルで考える

  • より良いアプローチは、HTML を コンポーネントとして扱う こと
  • そのコンポーネントの本質を把握する
    • ユーザーが特定項目の追加情報を得る方法
    • ユーザーがボタンをクリックすると情報が表示されるか、情報がなければエラーがレンダリングされ、どちらの場合でもコンポーネントは静的状態になる
  • 状態ごとにコンポーネントを分離

    • プレースホルダー状態:
      <!-- info-component-placeholder.html -->  
      <div id="info-component">  
          <button data-on-click="@get('/product/{{product.id}}/info')">  
              Get Info!  
          </button>  
      </div>  
      
    • 情報表示状態:
      <!-- info-component-get.html -->  
      <div id="info-component">  
          {% if alert %}<div id="alert">{{ alert }}</div>{% endif %}  
          <p>{{product.additional_information}}</p>  
      </div>  
      
    • サーバーが HTML をレンダリングすると、Datastar がページを自動更新する
    • コンポーネントレベルで考えることで、誤った状態に入ったりユーザー状態を失ったりすることを防げる

複数コンポーネントの同時更新

  • David Guillot の発表で印象的だったのは、アプリがお気に入り件数を更新する際、変更されたコンポーネントだけでなく、そこから大きく離れたカウント要素も一緒に更新していたことだ
    • HTMX では JavaScript イベントを発火させ、それが再びリモートコンポーネントへの GET リクエスト発行をトリガーする
  • Datastar では 同期関数内でも複数コンポーネントを同時に更新 できる
  • ショッピングカートの例

    • カート追加コンポーネント:
      <form id="purchase-item"  
            data-on-submit="@post('/add-item', {contentType: 'form'})">"  
      >  
        <input type=hidden name="cart-id" value="{{cart.id}}">  
        <input type=hidden name="item-id" value="{{item.id}}">  
        <fieldset>  
          <button data-on-click="$quantity -= 1">-</button>  
          <label>Quantity  
            <input name=quantity type=number data-bind-quantity value=1>  
          </label>  
          <button data-on-click="$quantity += 1">+</button>  
        </fieldset>  
        <button type=submit>Add to cart</button>  
        {% if msg %}  
          <p class=message>{{msg}}</p>  
        {% endif %}  
      </form>  
      
    • カート件数表示コンポーネント:
      <div id="cart-count">  
          <svg viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg">  
              <use href="#shoppingCart">  
          </svg>  
          {{count}}  
      </div>  
      
    • Django で2つのコンポーネントを同じリクエストで更新:
      from datastar_py.consts import ElementPatchMode  
      from datastar_py.django import (  
          DatastarResponse,  
          ServerSentEventGenerator as SSE,  
      )  
      
      def add_item(request):  
          # 重要な状態更新は省略  
          return DatastarResponse([  
              SSE.patch_elements(  
                  render_to_string('purchase-item.html', context=dict(cart=cart, item=item, msg='Item added!'))  
              ),  
              SSE.patch_elements(  
                  render_to_string('cart-count.html', context=dict(count=item_count))  
              ),  
          ])  
      

Webネイティブ哲学

  • Datastar Discord コミュニティを通じて、Datastar が単なるヘルパースクリプトではなく、Web の基本プリミティブを活用してアプリを構築する哲学 であると理解した
  • HTMX が HTML 仕様を発展させようとする一方で、Datastar は Webネイティブ機能の採用促進 により関心を持っている
    • CSS view transitions
    • Server-Sent Events
    • Web Components など
  • 複雑な AlpineJS コンポーネントをリファクタリングして、シンプルな Web Components を抽出 し、複数箇所で再利用する大きな成果を得た
  • React のようなツールがなくても、カスタム HTML 要素の作成 によって高い行動の局所性と再利用性を実現できる優れたパターンだ

マルチユーザーアプリのためのリアルタイム更新

  • 協調機能を第一級の機能として備えたアプリは他と差別化され、Datastar はこの課題を解決する
  • ほとんどの HTMX 開発者は ポーリング方式 でサーバーから情報を取得するか、カスタム WebSocket コード を書いて複雑さを増やしている
  • Datastar は Server-Sent Events(SSE) というシンプルな Web 技術を使い、サーバーが接続されたクライアントに更新を「プッシュ」する
    • ユーザーがコメントを追加したり状態が変わったりすると、サーバーが即座にブラウザを更新し、追加コードは最小限で済む
    • カスタム JavaScript なしでも、リアルタイムダッシュボード、管理パネル、協調ツールを構築できる
  • クライアント接続が切れた場合でも、ブラウザが 自動的に再接続を試みる ため、追加コードは不要
    • サーバーに「最後に受信したイベント」を伝えることもできる

過剰な複雑さを避ける

  • Datastar Discord コミュニティは、Web アプリ制作に対する Datastar のビジョンを理解する助けになった
    • プッシュベースの UI 更新
    • 複雑性の低減
    • Web Components のようなツールを活用したローカルな複雑状況の処理
  • コミュニティは、新規ユーザーが過度に複雑なアプローチをしていることに気づかせてくれる

主なヒント

  • コンポーネント全体を再レンダリング して送ることを恐れないこと
    • そのほうが簡単で、性能への影響も大きくない
    • より良い圧縮率が得られ、ブラウザによる HTML 文字列のパースも非常に高速
  • サーバーが真実の状態 であり、ブラウザより強力である
    • 状態の大半はサーバーに処理させるべきで、思っているほどリアクティブシグナルは必要ないかもしれない
  • Web Components は、高い行動の局所性を持つカスタム要素にロジックをカプセル化するのに優れている
    • Datastar の Web サイト ヘッダーの星空アニメーションが良い例
    • <ds-starfield> 要素が星空アニメーションのすべてのコードをカプセル化し、内部状態を変更する3つの属性を公開している
    • Datastar は範囲入力が変化したり、マウスが要素上を動いたりしたときにその属性を駆動する

限界を超える可能性

  • Datastar が可能にする潜在力こそが最も興味深い
  • コミュニティは、他のツールを使う開発者が経験する限界をはるかに超えるプロジェクトを定期的に生み出している

注目すべき事例

  • サンプルページの データベース監視デモ
    • Hypermedia を活用し、JavaScript カンファレンスで発表されたデモの速度とメモリ使用量を大幅に改善した
  • Anders Murphy の 10億個のチェックボックス
    • 100万個のチェックボックス実験がサーバー容量を超えたため、Datastar を使って安価なサーバー上で10億個を実現した
  • 米国中のすべてのレーダーステーションデータを表示する Web アプリ
    • レーダー信号が変化すると、UI 上の該当ポイントが 100ミリ秒以内 に変化する
    • 毎秒80万件以上のポイントが更新 され、ユーザーは最大1時間前までスクラブ可能(遅延は700ミリ秒未満)
    • これが Hypermedia アプリとして実現できること自体が、Datastar の可能性を示している

現在の使用体験

  • まだ Datastar を探求している段階だが、標準的な HTMX 機能にあたる UI 更新の AJAX 処理をすばやく簡単に実装できている
  • Datastar を使ってさらに多くを達成するためのさまざまなパターンを学び、実験している
  • 数十年にわたり、リアルタイム更新によってより良いユーザー体験を提供する方法に関心を持ってきたが、Datastar が 同期コードでもプッシュベース更新 を可能にしてくれる点が気に入っている
  • HTMX を使い始めたときには大きな喜びを感じたが、Datastar に移行してから失ったものは何もなく、むしろ はるかに多くを得た と感じている
  • HTMX を使って喜びを感じたことがあるなら、Datastar でも同じ飛躍を再び感じるだろう。それは Web が本来あるべき姿を再発見すること に近い

2件のコメント

 
GN⁺ 2025-10-11
Hacker News のコメント
  • Chris が自分の安全圏を出て挑戦し、その経験を私たちと共有してくれた点に感謝している。私は htmx で 4 年間 Web アプリを作ってきたので少しバイアスがあるかもしれないが、Datastar と htmx の主要なアーキテクチャの違いがよく表れていると思う。htmx は HTML 主導、Datastar はサーバー主導だ。クライアント側 API がシンプルなのは事実だが、その分サーバー側ロジックが複雑になる。たとえば、HTML 要素がサーバーから返された fragment をどこに挿入するかの情報を持っていないなら、その情報をサーバー側で保持する必要があるので、どちらか一方に複雑さは必ず存在する。アーキテクチャの選択は好みの問題のように思える。例での「less attributes」(属性を減らす)という論理は、htmx では任意で付けられる属性まで例に含めているので、100% 公平とは言いにくい。たとえば hx-trigger="click" を外せば属性は 20% 減る。それに、<span> の代わりに <button> を使うなど、よりアクセシブルに HTML を書けば信頼性も高まると思う。結局のところ Datastar の強みは、Alpine や Stimulus の機能が組み込まれた状態で提供されているように見える点で、これは本当に印象的だ
    • Datastar を使えば、ページの別の部分をリアルタイムで更新するために eventing を別途実装しなくても、一度にすべてを配信して更新できるので、複雑さがかなり減るのではないかと思う。もちろん状況によっては、イベントベースの方式や後から読み込む方式のほうが良い場合もあると思う
    • 「Alpine や Stimulus の機能が HTMX に標準搭載されたようなもの」という意見を見て、HTMX で個人プロジェクトをやろうかと考えている。AlpineJS や Stimulus のような追加ライブラリも必要だと説明している資料があるのか気になる
    • HTML 要素に fragment をどこへ挿入するかの情報がなければサーバーが知る必要がある、という議論があったが、この場合はフロントエンドのほうがより軽くて速いのではないか、とも思った。特に要素が大量にあるならなおさらではないか?
    • この構造は Pharo の Seaside フレームワークに少し似ている。うちの会社で Pharo で B2B アプリを作ったときは、UI 状態をバックエンドで管理していたので、フロントエンドとバックエンドの往復が多かった。リアルタイム性や低遅延が重要でない B2B なら問題ないが、スケーラビリティの高い B2C アプリには向かない
  • Datastar と HTMX を実際に使った立場として、Datastar でアプリを書くときにどんな大きな違いが出るのか、まだよく分からない。FastAPI、HTMX、Alpine.js、そして SSE を組み合わせて、ログのリアルタイム表示やデプロイ状況の更新などをやっている。Datastar の例を見ても、この構成よりどこがより簡単になるのかがあまり見えない。(コード参照: devpush SSE partial)。Web Components も以前 Basecoat を開発していたときに試したが、スタイルの問題や状態管理などさまざまな理由で、結局は従来の HTML/CSS/JS に戻った。devpu.sh, basecoatui.com
    • HTMX でも Datastar のように機能的な拡張性を考えると、リスト全体を丸ごと更新するほうがシンプルになるケースは多い。個別のデプロイ状態を更新しようとするより、リスト全体を更新したほうがページネーションのようなエッジケースも気にしなくてよくなるので、ずっと単純でコードも軽くなる
  • Datastar がリアルタイム/共同編集/マルチプレイには不十分だと思っている人には、PRO 機能なしでも動作し、しかも 5 ドルの VPS で HN のトップに載った 3 つのデモを紹介したい。Datastar がどれだけよくできた技術かを示している: Checkboxes, Cells, Game of Life Example。Checkboxes と Cells の例はビューのレンダリングが動的に動作するため、かなりズームアウトでき、仮想スクロールにはバックプレッシャー機能もある
    • 私がコード構造を正しく理解しているなら、実際には datastar で推奨されている差分パッチ(diff/patch)方式は使っておらず、毎回ページ全体を再レンダリングしているようだ。実際、このメンタルモデルは、サーバーがクライアント状態を精密に追跡する例よりずっと単純に見えるので、むしろ惹かれる。一般的な複雑なアプリもこういう方式で構築できるのか気になる。ユーザーが別々のページに移動するときに、さまざまなウィジェットの状態まで追跡しながら即時に再レンダリングするにはどうすればいいのか、参考になるテキストがあれば知りたい
    • Checkboxes / Cells の例で「ズームアウトできる」とあったが、具体的にどうやるのか気になる。それと、その座標(x=123&y=456 など)に現在のビューの URL を自動更新する data-replace-url のようなオプションがあれば、もっと良かったと思う
    • PRO 機能への言及を見て、オープンコアモデル(一部はオープンソース、残りは有料、299 ドルのライセンス)だと知った。自分は見送りたい
  • 最近こういう記事(htmx, datastar, greedy developer)を読んだが、Datastar の優れた中核機能が有料(Pro)へ移されたと聞いた。オープンソースでも有料でも、フレームワークを金銭的に支援したい気持ちはあるが、こういう前例は少し不安だ
    • 私も Datastar を数か月追い続けて 1.0.0 リリースを待っていたが、今では期待が完全にしぼんでしまった。「オープンソースだけど実際はそうではない」ケースに何度も当たってきたので、余計に信頼できない
    • 実は Datastar のことをあまり好きではないと書いていた立場だったが、今回は Datastar を少し擁護したくなった。フレームワークの作者が自分のコードを MIT ライセンスで無料公開したのだから、過去に無料公開されていた機能も引き続き MIT で使える。貢献せず使ってきただけの立場なら、過去バージョンに依存するのは本人の選択だ。これから有料モデルに切り替えるのはプロダクトオーナーの自由で、必要なら単に Fork すればよいと思う
    • PRO ライセンス 299 ドルを一度払って購入したが、まだ PRO 機能を実際に使ったことはない。Google スプレッドシート風クローンを作ろうとしたが、PRO なしでも十分実装できた。(Cells デモ参照
    • Datastar 側が HTMX の Discord で Datastar がどれだけ良いかをずっと宣伝しているのを超えて、少し攻撃的に感じたことがある。reddit でも「必要な機能が揃っているなら forever beta を使えばいい、オープンソースは誰にも借りがない」というニュアンスのコメントを残していたのを見たことがある
    • Datastar を見ていると、昔の Meteor.js の事例をすぐ思い出した。(Meteor.js HN 議論
  • この投稿に出てくるサンプルコードがよく理解できない。たとえば次のような htmx の例が <span hx-target="#rebuild-bundle-status-button" hx-select="#rebuild-bundle-status-button" hx-swap="outerHTML" hx-trigger="click" hx-get="/rebuild/status-button"></span> こういう datastar のコードに変わるようだが: <span data-on-click="@get('/rebuild/status-button')"></span> さらに他の例はもっと混乱する。結局、なぜ htmx から Datastar へ移るのか理由が分からない
    • 基本的に HTMX は「この span をクリックしたら /rebuild/status-button から HTML を受け取り、返ってきた HTML から #rebuild-bundle-status-button 要素を抽出して既存要素を置き換える」という意味だ。一方 Datastar は「span をクリックしたら /rebuild/status-button の命令にそのまま従う」となる。サーバーが複数の ID 付き要素を返せば、Datastar はそれらを自動で認識してすべて置き換えてくれる。つまり target、select、swap を全部書かなくても、ID さえ付いていれば意図どおり動く
    • Datastar の構造は、ロジックをバックエンドに寄せたものだ。昔ながらの HTML のように、リクエストすると HTML を受け取ってブラウザがすぐレンダリングする形だが、Datastar は PWA のように一度ページを読み込んだ後、インタラクションのたびにバックエンドへリクエストを送り、変更分だけ受け取って反映する。フロントエンドにロジックを置かず、バックエンドがすべての状態を管理する SPA(シングルページアプリ)の逆方向の構造だ。本質的にはバックエンド/フロントエンドでどちらにロジックを配分するかという問題が繰り返されているだけで、Datastar はサーバー(バックエンド)にロジックを集中させても動的なインターフェースを維持できるようにしてくれる
    • でも、なぜクリック用に span タグを使うのかは疑問だ。button やリンクタグのほうが適切ではないかと思う
  • 皮肉なことに、この記事はハイパーメディアがテーマなのに Datastar 公式サイトへのリンクがない。公式サイトはこちら: https://data-star.dev/
  • HTMX の大きな利点のひとつは、クライアントがサーバーから返されるデータ構造を知らなくてもよい点だが、もしクライアントが個々の要素の ID や意味まで知っている必要があるなら、その約束は崩れるように思える。もっとも、実際には多くのプロジェクトで OOB(Out of Band)が使われているくらいなので、完全に構造を分離するのは現実から少し離れているとも思う。両方の世界の長所を取れる方法が出てくるといい
    • 実際のところ、クライアントは何も知らなくてもよい。クライアントでアクションを実行すると、サーバーはページ全体のビューを返せる。クライアントはそれを即座に全体レンダリングする。ビデオゲームの immediate mode モデルに似ている
  • Datastar がこのようにサーバーから HTML をパッチ適用する構造は、関心の分離(Separation of concerns)の観点ではあまり良くなく、アプリの規模が大きくなるほど、サーバーから HTML の一部を注入し続けるのは管理がかなり面倒になりそうだ
    • だからといって、JS があちこちから fragment で HTML を注入するようにすればより良くなるわけでもない
    • サーバーから HTML の断片を返すエンドポイントを設計する方式には、なんとなく抵抗がある
  • Datastar は htmx より完成度が高い印象だ。htmx でいくつかのプロジェクトを成功させたが、イベント処理のために JS のグルーコード(特に AlpineJS など)を追加で書かなければならない部分がいつも惜しかった。Datastar がこの必要性まで減らしてくれるなら本当に期待できる
    • Datastar サイトの grugs around the fire エッセイを読むのを勧める
  • 私はハイパーメディアのトレンドに比較的遅れて参加したが、最初は Datastar を使い、最近は HTMX に移った。Datastar の API のほうが少し良いが、htmx 2.0 で OOB(Out-Of-Band)アップデートがサポートされたので、それ以降は大半のケースで htmx に軍配を上げている
    • 「ハイパーメディアに遅れた」という言い方が印象的だ。Ted Nelson が 1965 年にこの用語を初めて使い、当時「hyperfilm— a browsable or vari-sequenced movie— is only one of the possible hypermedia that require our attention」と書いていたことを考えると興味深い。関連論文を見る
    • HTMX で OOB 要素を扱うときの不満点: 1. OOB なら htmx-swap-oob="true" が必須で、この属性がないと期待どおりに動かない 2. 逆に OOB でないなら htmx-swap-oob="true" があると無視されるか誤動作する。このせいで同じコンポーネントを OOB/非 OOB で再利用するとき、サーバーから isOob フラグを毎回渡さなければならず、かなり面倒だ
    • 私は alpine-ajax の API のほうが好きだ。複数ターゲットを指定するだけで、JS なしでも各要素を一貫して置き換えられる。Datastar の signal/state 概念は、むしろ複雑さを増していてあまり好みではない