1.1 Datastar and Common Lisp for web development
================================================
2025-08-17
So you're living under the gun
Circumstances have you on the run
A doctor frowns, you feel bad
Take this, you've just been had!
Don't you lose it, now listen to us
Everything's going to be all right
Take a break, take some time
Everything's going to be all right
Don't you lose it, remember to take
Time out for fun.
-- _Devo, Time Out for Fun, 1982_
Doing front-end development with state in the back-end, using
whichever language one prefers, and specifically Common Lisp, while
being reactive and scalable: if doing this is wrong, I don't want to be
right, and Datastar seems almost too good to be true.
1.1.1 The demo
--------------
Let's start by going directly to what this is all about: Datastar (1) is
an 'hypermedia framework' that uses server-side rendering along with a
(very light) front-end library. There are several SDKs, including
Clojure, but I wanted to see if it worked with Common Lisp, and long
story short, it does.
This small demo is the outcome of my curiosity: a minimal example of
a dynamic website made with Datastar and a Common Lisp backend (2),
based on the 'click this button' example in the Datastart guide.
(Codeberg repository (3))
[Datastar and Common Lisp ]
Datastar and Common Lisp (4)
Read the Datastar guide (5) for more information, this is nothing
more than an example of how it works when using Common Lisp, with a
couple of functions/macros that can be thought as an initial SDK.
1.1.2 What and why
------------------
As I work on See Budotree (and there's a lot of new ideas floating
around to make it bigger and better), I've continued my descent into
'frontend development'. I've started with vanilla JavaScript, and it
was fun enough, and then I started looking into the more established
ecosystem. I never had to actually worry about it before (that I wrote
a Texinfo blogging solution from scratch: Texiblog is a good indication
of of this...), but I tried things with an open mind, like I often do...
So much so that I now have hundreds of lines of code using React, MUI,
Vite, and some other things I can't remember, and truth be told that
from a user perspective, it looks OK.
[The Budō Lineage Tree React version]
The Budō Lineage Tree React version
It's not fun though.
I've had more fun in the couple of hours I've spent in this small
demo with Datastar then when doing the above. This is largely my fault
and a consequence of my own limitations and preferences, but even the
filesystem organisation bothers me, everything seems to be optimised for
complexity, and the urge to just ask some LLM to do most of the stuff
ends up being overwhelming - and that's the first step to completely
lose the plot and start a read-only project.
Yes, I know there are alternatives. Yes, I've tried them. No, it
didn't make a difference.
1.1.3 Datastar and hypermedia
-----------------------------
One of the good things of having everything in the client is not even
technical: it's cost. I can share my static content and leverage the
browser. This is very compelling and has been tremendously useful.
However, as I try to add more features, it starts to be insufficient,
and once that happens, a new world of additional options needs to be
navigated: edgeless functions, SSR, application servers. There's
nothing wrong with that, of course, but it does open a door for
reevaluating my options.
This is how I found Datastar. With the assumption that I would need
to have a back-end, why not try to actually enjoy the process? I had
stumbled on Alpine.js before (and Preact, and Solid.js, and...), and had
read a bit on HTMX, but I got to Datastar since I found it while
searching for web development with Common Lisp: the creator of Datastar
mentioned Lisp (6) a couple of times, and catching that was enough to
pick my interest. (7) This video on real-time hypermedia (8) by
Delaney is a very interesting watch and sets some of the major
principles in which Datastar rests.
Datastar already has a Clojure SDK. Clojure is not new to me:
Clojure-MQTT, but if I really want to have fun, well, it's not Common
Lisp. That Datastar had Clojure support (and from what I gathered, a
significant one) was however not irrelevant, since Clojure is a Lisp (9)
1.1.4 What I did, and what was used
-----------------------------------
This demo implements the button with SSE events, and in reality the
Datastar specific parts are the smallest parts of it: we just need to
send SSE events, which means that the bulk of the work (although still
quite simple) is done at the web server level, specifying 'send an
event-stream reply with the appropriate format, headers, and using this
format as payload'.
For that, I've used this macro, which actually does a bit more than
it should (the ‘let’ sets Spinneret options to keep the HTML output in a
single line, something that is not related with SSE directly).
(defmacro with-sse-response ((stream-var) &body body)
"Set up SSE headers and bind STREAM-VAR to a UTF-8 SSE stream. Based on
https://github.com/edicl/hunchentoot/issues/175#issuecomment-585040829,
but without detaching the socket."
(let ((*html-style* :tree)
(*print-pretty* nil)
(*always-quote* t))
(setf (content-type*) "text/event-stream; charset=utf-8")
(setf (header-out "Cache-Control") "no-cache")
(setf (header-out "Access-Control-Allow-Origin") "*")
(let* ((raw-stream (send-headers))
(,stream-var (flex:make-flexi-stream raw-stream :external-format :utf-8)))
,@body)))
The main components used are:
“Hunchentoot (
https://edicl.github.io/hunchentoot/)”
One of the most popular Common Lisp webservers. There are options,
but I wanted to keep this as simple as possible, so Im not using
Clack or any additional layer.
“Spinneret (10)”
An HTML generator that created human-readable HTML from lists.
“css-lite (11)”
Does for CSS what Spinneret does for HTML.
“SBCL (12)”
Common Lisp compiler; one of several possible choices since I'm not
using anything that depends on non-CL libraries.
The code itself is in a single file (not counting the package
definitions) on purpose, I personally find it easier to learn new things
when I don't have to visit lots of different files. For a more robust
usage I will almost surely split things in different ways, with handlers
separated from webserver start, with the HTML and CSS in different
files, etc.
Deployment-wise, I'm not expecting issues with this, I've tested it
with thousands of concurrent users and it didn't made a dent. There's
something to be said about the real needs of websites and the idea that
most things need a 'service' that 'scales': serving HTML is not that
hard for a server to do.
1.1.5 How it works
------------------
1.1.6 Where to go from here
---------------------------
By itself, this example doesn't do a lot, but for me it opened the door
to a different way to create a reactive website. I'll likely add more
and more as I explore it, and as I do I've found that there's a lot of
things that I can go 'back to basics' on: I've had some difficulties in
completely understanding the entire CSS stack used, especially when
things like Tailwind and then used to build more 'semantic' frameworks,
different things piled on top of each other. The realities of
reactive/mobile-first design is one of the reasons behind the apparent
complexity, but using Datastar has allowed me to peel at least one of
the layers.
Give it a try: even if you're not using Common Lisp, it is a polished
framework, done by some pretty smart people that have poured their
experience and opinion on it, and for me that's a big advantage.
---------- Footnotes ----------
(1) Datastar (
https://data-star.dev/)
(2) a minimal example of a dynamic website made with Datastar and a
Common Lisp backend (
https://datastar.interlaye.red/)
(3) Codeberg repository (
https://codeberg.org/fsm/cl-datastar-demo)
(4) Datastar and Common Lisp (
https://datastar.interlaye.red/)
(5) Datastar guide (
https://data-star.dev/guide/getting_started)
(6) mentioned Lisp
(
https://x.com/DelaneyGillilan/status/1935673128384110756)
(7) This is an important point because my choice has not been
primarily motivated by benchmarks or anything of the sort, but more due
to having landed on it while looking for a different way to do things,
having read the documentation, seeing a couple of videos, and feeling it
made sense. The author, Delaney Gillilan, is outspoken about why he
thinks Datastar makes sense, and why the framework works the way it
does, and so far it's been pretty convincing.
(8) video on real-time hypermedia
(
https://www.youtube.com/watch?v=0K71AyAF6E4)
(9) There's an entire discussion about what this means that I will
sidestep. Clojure has been very useful in opening up the Java
ecosystem, enabling the use of a member of the Lisp family instead of
Java itself, and that is not an insignificant benefit.
(10) Spinneret (
https://github.com/ruricolist/spinneret)
(11) css-lite (
https://github.com/paddymul/css-lite)
(12) SBCL (
https://www.sbcl.org/)