HTML over the wire is back — the WebSocket part is optional
Server-rendered fragments beat shipping a framework, but SSE gets you most of the benefit with less ops pain.
A post making the rounds this week pitches a familiar idea with fresh energy: build your "SPA" by streaming server-rendered HTML fragments over a WebSocket, and ship almost no JavaScript. The browser keeps a persistent connection open, the server pushes <div>s instead of JSON, and a tiny client runtime swaps them into the DOM. No React, no client-side state store, no API layer to design and version.
The pattern works. It's been in production for years under half a dozen names. But if you're evaluating it in 2026, the interesting question isn't whether HTML-over-the-wire is legitimate — it clearly is — it's whether the WebSocket part is actually the right transport. Increasingly, the answer is no.
This isn't new — that's the point
Every major backend ecosystem independently converged on this architecture, which is about the strongest signal you can get that it solves a real problem. Phoenix LiveView shipped it for Elixir back in 2019, riding the BEAM's absurd capacity for cheap persistent connections — the Phoenix team famously demonstrated two million WebSocket connections on a single server years before LiveView existed. Basecamp extracted Hotwire from HEY in late 2020 and made "HTML over the wire" a slogan. Microsoft's Blazor Server does the same dance over SignalR. Laravel got there with Livewire 3 plus Reverb, its first-party WebSocket server. Django has a small cottage industry of implementations, including the post author's own Django LiveView.
And the lineage runs deeper than any of these. Rails was rendering server-side partials and splicing them into pages with RJS in 2006; PJAX and Turbolinks refined the idea a decade ago. The JSON-API-plus-client-framework SPA was the detour. This is the web's default architecture reasserting itself with a push channel bolted on.
What you're actually buying
The concrete win is deleting an entire layer of your stack. No REST or GraphQL API to design, document, and keep backward-compatible with your own frontend. No client-side data store mirroring your database. One rendering path, on the server, in the language your team already writes, with your existing auth and your template engine's XSS escaping. For a small team building a dashboard, admin panel, or internal tool with live-updating data, that's weeks of work that simply doesn't exist.
The client payload collapses too. htmx is around 14 kB gzipped; LiveView's and Turbo's runtimes are similarly small. Compare that with a typical React + state management + data-fetching bundle before you've written a feature. And latency per interaction genuinely improves: one persistent connection, no per-request TLS and connection setup, HTML that's ready to paint the moment it arrives.
The HN discussion around the post — 90-plus comments — largely conceded all of this. The pushback landed somewhere else.
The socket is the weak link
What you give up is the stateless request/response model that fifty years of web infrastructure is optimized for. Every connected user now holds open state on a specific server. That means sticky sessions or a shared pub/sub backplane (in practice, Redis) the moment you run a second instance. It means load balancers and corporate proxies that idle-timeout or outright block WebSocket upgrades. It means autoscaling gets ugly, because draining a server now disconnects thousands of users mid-session, and reconnection logic has to rebuild whatever state the server was holding. Blazor Server's circuit-lost error screen is the canonical failure mode; every framework in this family has its own version.
Here's the thing: most apps in this pattern's sweet spot don't need bidirectional streaming. They need the server to push updates and the client to send occasional actions. Plain HTTP handles the actions; Server-Sent Events handle the push — over ordinary HTTP, with automatic reconnection and Last-Event-ID resume built into the browser. That's why the most interesting recent entrant, Datastar, is SSE-first rather than WebSocket-first, and why Turbo Streams was designed to work over SSE and plain form responses, not just Action Cable. Even the original post's lone code sample is htmx's SSE extension, not a socket.
The author reports his own site handling peaks of 600 concurrent readers on minimal hardware — plausible, though it's a single-source anecdote about a mostly-read-only site, which is exactly the workload where SSE would do the same job with less machinery.
Where this should land in your stack
If your team is backend-heavy and you're building something server-authoritative — dashboards, admin tools, CRUD apps with live data, notifications, presence — this pattern is production-ready and has been for years. Pick the implementation native to your stack: LiveView on Elixir (still the best-engineered version, because the runtime was built for exactly this), Hotwire on Rails, Livewire on Laravel, htmx or Datastar anywhere else. Start with SSE and plain HTTP; reach for WebSockets only when you actually need low-latency client-to-server streaming, like cursors or collaborative editing.
If you need offline support, optimistic UI, or serious client-side interactivity — canvas tools, editors, anything where the interesting state lives in the browser — this pattern fights you the whole way, and a round trip per keystroke will never feel native. Physics doesn't negotiate: a user 150 ms from your server feels every one of those milliseconds on every interaction.
The honest framing is that this architecture didn't eliminate complexity; it moved it from the client bundle to server memory and your ops runbook. For a lot of teams that's a fantastic trade — servers are easier to observe and reason about than a thousand browser environments. Just be clear that you're making a trade, not getting something for free. The "barely any JavaScript" pitch is real. The "barely any operational complexity" pitch, when WebSockets are involved, is not.
Sources & further reading
- HTML over WebSockets: real-time SPAs with barely any JavaScript — en.andros.dev
- HTML over WebSockets: real-time SPAs with barely any JavaScript (discussion) — news.ycombinator.com
- HTML Over WebSockets — testdriven.io
Lenn writes about cloud platforms, Kubernetes internals, and the infrastructure decisions that quietly make or break engineering organizations. Based in Berlin's vibrant tech scene, they have a talent for turning dense platform-engineering topics into prose that people actually finish reading.
Discussion 0
No comments yet
Be the first to weigh in.