Serving Datastar Streams
Datastar leaves the server-side implementation open-ended. Datastar provides SDKs for various languages, but those SDKs only cover the protocol on top of SSE that the Datastar JavaScript client expects. To manage Datastar connections from the server, you need two things:
- Sending the SSE events Datastar expects.
- Getting state changes from one of your servers to the others.
The second is the harder one if you haven’t worked with streaming architectures. The creator of Datastar uses NATS, but any centralized store or pub-sub queue where servers can publish and read changes is enough to get started. Server languages that handle concurrency poorly will hit limits on scaling SSE connections sooner.
Streaming Flow
Here’s the flow for any SSE setup with Datastar. (For a refresher on the client side, see Datastar Basics.)
- Client requests a server-rendered page that isn’t SSE.
- Server returns a page with an element that opens a Datastar SSE endpoint, like
data-on:load="@get('/stream')". That can be the same path, if the server reads theDatastar-Request: trueheader to tell that Datastar initiated the request. This example uses a separate endpoint for clarity. - Client requests the endpoint that provides the SSE stream.
- Server responds with headers indicating an SSE stream, leaving the connection open.
- Server sends the current page’s contents (again). This should be the full state of the page.
- Server sends deltas as needed. To get started, it’s fine to send the entire page’s contents again.
- The client may send mutations via request actions. Those changes come back on the stream, not in the response to the action.
Is #5 important? It’s what covers reconnects, which clients do arbitrarily.
SSE connections are browser-controlled: the browser may cut the connection at any point, and reconnect at any point. So whenever a connection is established, assume a disconnect preceded it and send the latest state. Steps 3-5 should repeat every time the browser reconnects.
A browser will typically drop an SSE connection when:
- The user switches away from the tab using it
- The browser is backgrounded (on mobile devices)
- The connection idles (Datastar can keep-alive the response as a best effort)
This changes from browser version to browser version, as each vendor decides what’s optimal for battery life.
Sending the full state initially also handles a patch-elements edge case: navigating to a new page and then going back to the SSE-enabled one. Some browsers restore the HTML from the initial page load rather than the state Datastar patched in. Re-sending the state on connect resolves that too.
You can replace patch elements with patch signals, as long as signals cover every state change you’d otherwise make through SSE.
Backend Implementation Needs
SSE holds a connection open for a long time, so use a framework or language that supports IO concurrency (node, go, rust, etc.).
The traditional options for server-to-server communication all apply:
- NATS
- Redis PUB/SUB
- Postgres LISTEN/NOTIFY
- Any cloud provider pub/sub (Google Cloud Pub/Sub, AWS SNS, etc.)
- P2P libs like iroh
Whatever writes you make should publish the entity ID or page identifier that changed. A server receiving an SSE stream request then subscribes to those entities, and gets notified when changes occur on that page.
Use a modern compression for the SSE stream (zstd, brotli, etc.). The Datastar SDKs typically do this for you, but remember to if you’re implementing it yourself. This one change dramatically reduces the redundant data you send down the stream — including the full page updates you send (because we’re being lazy).
Some Other Uses for SSE Streaming
Some fun ideas that you can use the above process for:
Render Above the Fold, Stream in the Rest
The initial page render and the SSE stream don’t have to return the same results. The server can return only what’s above the fold — by limiting how many items a list returns, or by marking the elements to load later with an HTML element the server recognizes.
If the page doesn’t need real-time streaming, the server can close the stream once it has written the full page contents.
Multiplayer Collaboration
There is nothing more to do here! If you’ve implemented the design above, this already works.
Throttling
Updates are controlled by the server, so any updates can be rate limited by the server.