HTML Over WebSockets: The Counterintuitive Approach to Real-Time Web Apps
The dominant pattern for building interactive web applications today is clear: build a JSON API, ship a JavaScript-heavy frontend, and manage state on the client. But a recent post on Hacker News (132 points, 99 comments) reminded the community that there's an alternative that's been quietly gaining traction: HTML over WebSockets, where the server sends rendered HTML fragments instead of JSON, and the client simply swaps DOM nodes.
It sounds backwards. Why would you send HTML — verbose, heavier than JSON — over a WebSocket? But when you look at the trade-offs, the approach has some surprising advantages.
How It Works
The pattern is straightforward:
- Client establishes a WebSocket connection to the server
- Client sends user interactions (clicks, form submissions, input changes) as simple messages
- Server processes the request, renders the relevant HTML fragment, and sends it back
- Client replaces the relevant DOM node with the new HTML
That's it. No client-side state management. No JSON parsing. No reconciliation. No virtual DOM. Just: "here's the new HTML for that section of the page."
The Advantages
1. Dramatically Less JavaScript
Modern SPAs often ship 200KB+ of JavaScript just for state management, routing, and rendering. With HTML over WebSockets, your client-side JavaScript can be as little as a few lines:
const ws = new WebSocket('wss://yourserver.com/socket');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.target && data.html) {
document.querySelector(data.target).innerHTML = data.html;
}
};
// Send interactions
document.addEventListener('click', (e) => {
if (e.target.matches('[data-action]')) {
ws.send(JSON.stringify({
action: e.target.dataset.action,
value: e.target.value
}));
}
});
That's the entire client. No React, no Vue, no state store, no build step.
2. Server-Side State Is Simpler
When state lives on the server, you don't need to sync it with the client. There's no "stale closure" problem, no race conditions between API calls, no optimistic UI updates that need to be rolled back. The server knows the truth at all times, and the client is always a reflection of that truth.
3. Better for Low-End Devices
On low-end phones and slow connections, JavaScript-heavy SPAs can be painfully slow to load and interact with. HTML over WebSockets front-loads almost nothing — the initial page load is minimal, and subsequent updates are just HTML fragments.
4. SEO and Initial Load
The initial page load can be server-rendered HTML, meaning search engines see a fully rendered page. No client-side rendering delay, no hydration mismatch, no "blank page then content appears" experience.
The Trade-offs
HTML Is Larger Than JSON
This is the obvious objection. Sending rendered HTML is larger than sending raw JSON. But in practice, the difference is often negligible — especially with gzip compression, which reduces HTML dramatically. And you're saving the client from needing a rendering framework to turn that JSON into HTML anyway.
No Offline Support
Since all rendering happens on the server, there's no offline mode. If the WebSocket disconnects, the app stops working. For some applications (dashboards, admin panels, real-time tools), this is fine. For others (email clients, document editors), it's a dealbreaker.
Server Scaling
Every connected client holds an open WebSocket connection. This means your server needs to handle concurrent connections efficiently. Tools like Phoenix Channels, Go's goroutines, and Node.js's event loop make this manageable, but it's a different scaling challenge than stateless HTTP.
Who's Using This Pattern?
This approach has been championed by several frameworks:
- Phoenix LiveView (Elixir) — the most well-known implementation, used in production by many companies
- Hotwire/Turbo (Ruby on Rails) — a hybrid approach that uses WebSockets for partial page updates
- Livewire (PHP/Laravel) — similar pattern for the PHP ecosystem
- htmx — a lightweight library that extends HTML with AJAX and WebSocket attributes
The fact that this pattern has been adopted across multiple language ecosystems suggests it's solving a real problem. Developers are tired of the complexity of client-side state management, and HTML over WebSockets offers a genuinely simpler alternative.
When to Use It
HTML over WebSockets shines for:
- Internal tools and dashboards — where low latency matters more than offline support
- Real-time applications — chat, notifications, live data feeds
- Forms-heavy applications — where server-side validation is critical
- Applications on low-end devices — where JavaScript performance is poor
It's less suitable for:
- Offline-first applications — PWA-style apps that need to work without connectivity
- Highly interactive UIs — drag-and-drop interfaces, complex animations, canvas/WebGL
- Applications with complex client-side state — anything that needs optimistic updates or local computation
The Takeaway
The web development community has spent the last decade making applications more complex — more JavaScript, more state management, more build tooling. HTML over WebSockets is a reminder that sometimes the simpler approach is the better one. Not every application needs a full SPA architecture. Sometimes, a WebSocket and some HTML fragments are all you need.
Sources: HTML over WebSockets: real-time SPAs with barely any JavaScript
Top comments (0)