DEV Community

Charles
Charles

Posted on

HTML Over WebSockets: Real-Time SPAs With Barely Any JavaScript

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:

  1. Client establishes a WebSocket connection to the server
  2. Client sends user interactions (clicks, form submissions, input changes) as simple messages
  3. Server processes the request, renders the relevant HTML fragment, and sends it back
  4. 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
        }));
    }
});
Enter fullscreen mode Exit fullscreen mode

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)