{
  "id": 19419668,
  "title": "Rendering in batches with glimmer and Datastar",
  "url": "https://urgent.news/2026/11/10/rendering-in-batches-with-glimmer-and-datastar",
  "topic": "tech",
  "section": "Tech",
  "published": "2026-11-10T22:54:17.000Z",
  "source": {
    "name": "Lobsters",
    "slug": "lobsters",
    "url": "https://yogthos.net/posts/2026-11-10-glimmer-datastar.html"
  },
  "original_language": "en",
  "account": "Anders Murphy recently discussed [faster streaming HTML with batches and aggregates](https://andersmurphy.com/2026/09/29/faster-streaming-html-with-batches-and-aggregates.html). The concept involves collecting changes and rendering them once for the entire batch when the server streams HTML to a page. This approach also prevents rendering the same thing twice for two distinct pages, thereby avoiding resending data that the client already possesses. The author was developing glimmer and glimmer-datastar, which enable reactive server-side rendering for Jolt, when they encountered comparable issues. This article outlines the modifications made to these libraries and how they are utilized in the newsroom app.\n\nGlimmer is a Reagent-style library wherein state resides in ratoms, which UI components subscribe to. Although the original target was native GUIs like glimmer-gtk and glimmer-tui, the reactive core is toolkit-agnostic. Intrigued by applying the same model on the server using Datastar on the frontend, the author observed that each open page maintains an SSE stream to the server. When the page is rendered, any ratom the page accesses is recorded, and the stream subscribes to it. Subsequently, whenever one of those ratoms changes, it triggers the stream to render the page anew. Consequently, when a swap! occurs on a ratom, it transmits a datastar-patch-elements event featuring the updated HTML to every page rendering that element.\n\nInitially, the approach awakened every stream that reads the ratom upon a write to execute a render. While this works adequately for a few pages with occasional writes, it becomes unscalable with increased load, as the cost escalates to writes * pages. The solution involves decoupling writes from render requests. A write merely needs to label the streams that read the ratom as \"dirty,\" followed by a separate render loop executing on a predefined timer set by :tick-ms. Every dirty stream undergoes rendering exactly once upon waking up, incorporating all modifications that have accumulated since the prior rendering. The loop pauses for the remainder of the tick before rendering, enabling every incoming change to be incorporated into the same batch. Meanwhile, an idle loop simply waits on a channel without incurring any additional overhead. Consequently, a surge of ten open streams and a hundred swap! operations results in a significant improvement as compared to the previous scenario.",
  "summary": null,
  "key_points": [],
  "editors_take": null,
  "illustration": null,
  "coverage": {
    "outlets": 1,
    "also_reported_by": []
  },
  "ai_generated": true,
  "disclaimer": "Summaries, key points and the editor’s take are written by software from other outlets’ reporting and may contain errors — always check the linked original."
}