Datastar in Clojure
A series in 8 parts

Building hypermedia apps with Datastar and Clojure

Signals, SSE, server-shaped routes, and a native shell that replaces a React Native app

Datastar is a hypermedia framework, and the whole pitch fits on an index card: one script tag, a set of data-* attributes, and a server that answers requests with HTML and signal patches over Server-Sent Events. That's it. That's the framework. This series teaches it from Clojure, with no SDK. You write the wire format yourself in part 2 (it's a handful of small functions, so don't get nervous), and once you have, nothing the browser does is a mystery.

Everything builds toward one small app, Brew Board: a coffee counter where customers configure drinks and place orders, and a barista moves them along a live board. Along the way you'll decide what belongs in a signal and what doesn't (spoiler: less than you'd think), shape the backend's routes around commands and one long-lived stream, and finally serve the very same routes to a thin native shell that takes the place of a React Native app. Yes, really. Parts 1–6 are the core. Parts 7 and 8 are optional: part 7 is for building a mobile app, and part 8 weighs the alternative architectures.

It's a toy, but a toy in the useful sense, the way a model railway is useful if you want to understand switching. State is an atom, there is no auth and no database, and nothing is tuned. Each part has a standalone lesson in clojure/ with a narrated demo, an exercise track with failing tests, interactive figures to step through, and recall cards. And wherever a usage decision depends on how Datastar works inside (how it reads the stream, how signals track dependencies, how the morph matches elements), the post opens the hood and explains that piece from the source.

How this series was made. It was written with Claude Code, Anthropic's coding agent, on top of Learning Kit, Dan Sutton's toolkit for teaching a topic by building a toy version of it. Learning Kit supplies the site shell, the predict-then-reveal figures and recall cards, the exercise track, and the CI that checks the code, the tests and every quoted excerpt. Thank you, Dan.

    Practice

    Reading about it isn't the same as building it. A few things help it stick:

    The code

    Each part has a standalone lesson in clojure/src/lessonNN and go/lessonNN with a runnable demo; the capstone puts everything together.

    And here's where it all comes together. The capstone is the whole of Brew Board in clojure/src/brewboard: the hand-written SSE writer, the menu and pure handle-command, whole-view renders for the customer and the board, the broadcast hub, the reitit routes, and the native-shell routes. Every piece you built, in one place. Start it and open the two pages side by side, in two tabs of one browser. That's safe because part 6 gave every tab its own id, so the tabs don't step on each other's drafts:

    cd clojure && clojure -M -m brewboard.main      # PORT env var, default 8080
    # customer: http://localhost:8080/      board: http://localhost:8080/board

    Add a medium oat latte as Ada, place the order, advance it twice on the board, and mark oat milk sold out. Then just watch: every open page follows along, and the customer's drop-down shows oat as sold out without a reload. It's a small thing, and it's oddly thrilling the first time. clojure -M -m brewboard.demo runs the same session headless over real HTTP and prints what each stream received, for those of us who like to see the receipts.

    One more thing. native/ios and native/android hold small reference shells, a WKWebView app and a WebView app, that load the same server, apply the served /native/path-config rules, and implement the BrewShell bridge from part 7. They aren't built in CI (Xcode in CI is its own adventure); native/README.md says what was checked and how to run them against localhost.

    Thanks to Fabian Merkle, who spoke about Datastar at Clojure/Conj 2026, for reviewing the series and for feedback that changed it. Part 8's indicator trade-off and its command endpoint, the error handling in part 6's broadcast, part 6's "Where this goes next" and the half-open connections in part 7 all came out of his notes. Any mistakes that survived are mine.