Go + HTMX

Streaming Server-Sent Events (SSE) with Go and HTMX: Building a Live System Dashboard

Master live server pushing. Learn how to stream metrics from Go to HTMX over Server-Sent Events (SSE) with zero client-side JavaScript.

Sachin Sharma
Sachin SharmaCreator
Jun 4, 2026
4 min read
Streaming Server-Sent Events (SSE) with Go and HTMX: Building a Live System Dashboard
Featured Resource
Quick Overview

Master live server pushing. Learn how to stream metrics from Go to HTMX over Server-Sent Events (SSE) with zero client-side JavaScript.

Streaming Server-Sent Events (SSE) with Go and HTMX: Building a Live System Dashboard

When building real-time dashboards (like system resource monitors, log viewers, or notification feeds), developers often default to WebSockets.

However, WebSockets are bidirectional and complex to manage. If your application only requires one-way server-to-client updates (server pushing metrics down to the UI), WebSockets are an over-engineered choice.

Server-Sent Events (SSE) is a lightweight, standard HTTP-based protocol that allows the server to keep a connection open and stream text updates to the client indefinitely.

By combining Go (with native http.Flusher support for streaming HTTP chunks) and HTMX's native SSE extension, you can build a dynamic, real-time system dashboard with zero client-side JavaScript.

In this guide, we'll design and build a live CPU and Memory resource monitor dashboard.


⚡ 1. The SSE + HTMX Streaming Flow

SSE operates over a standard HTTP connection.

  1. 2.
    Client Connection: The client browser opens an HTTP connection requesting the SSE endpoint (Content-Type: text/event-stream).
  2. 4.
    Go Server Push: The Go server starts a loop, collects CPU/Memory usage metrics, renders the metrics as an HTML fragment, and flushes the data down the open connection.
  3. 6.
    HTMX Dynamic Swap: When an event payload arrives, HTMX automatically intercepts the event, reads the HTML fragment, and swaps it into the targeted DOM element.
[HTMX Client Page] ──(Request: text/event-stream)──> [Go SSE Handler]
          │                                                  │
          │                                            (Collect Stats)
          │                                                  │
[Dynamic UI Update] <──(HTML Event: message) ── [Flush HTTP Connection Chunk]

🏗️ 2. Designing the HTML Layout with HTMX SSE

HTMX provides a dedicated ext/sse.js extension. We configure our parent container to listen to the SSE event source.

html
<!-- index.html --> <!DOCTYPE html> <html> <head> <title>Go + HTMX Live Monitor</title> <script src="https://unpkg.com/htmx.org@1.9.10"></script> <!-- Load HTMX SSE Extension --> <script src="https://unpkg.com/htmx.org@1.9.10/dist/ext/sse.js"></script> </head> <body> <div class="dashboard"> <h1>Server System Metrics</h1> <!-- 1. Open the SSE connection on the parent container --> <div hx-ext="sse" sse-connect="/stats/stream"> <div class="metrics-grid"> <!-- 2. Target these containers to swap incoming HTML segments from specific events --> <div id="cpu-gauge" sse-swap="cpu_update" class="metric-card"> <h3>CPU Usage</h3> <p>Awaiting stream...</p> </div> <div id="mem-gauge" sse-swap="mem_update" class="metric-card"> <h3>Memory Usage</h3> <p>Awaiting stream...</p> </div> </div> </div> </div> </body> </html>

💻 3. Coding the Go SSE Server

On the backend, we write a standard Go HTTP handler. To stream data, we set the headers to tell the browser not to cache, and assert that the response writer implements the http.Flusher interface.

go
// main.go package main import ( "fmt" "math/rand" "net/http" "time" ) func statsStreamHandler(w http.ResponseWriter, r *http.Request) { // 1. Configure SSE response headers w.Header().Set("Content-Type", "text/event-stream") w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Connection", "keep-alive") w.Header().Set("Access-Control-Allow-Origin", "*") // 2. Cast ResponseWriter to http.Flusher to enable chunked flushing flusher, ok := w.(http.Flusher) if !ok { http.Error(w, "Streaming unsupported", http.StatusInternalServerError) return } ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() for { select { case <-r.Context().Done(): // Client disconnected, exit goroutine cleanly return case <-ticker.C: // 3. Collect mock metrics (or read from sys/sigar) cpuPercent := rand.Intn(100) memPercent := rand.Intn(100) // 4. Format HTML fragment outputs for CPU and Memory cpuFragment := fmt.Sprintf( "<div id="cpu-gauge"><h3>CPU Usage</h3><p class="large">%d%%</p></div>", cpuPercent, ) memFragment := fmt.Sprintf( "<div id="mem-gauge"><h3>Memory Usage</h3><p class="large">%d%%</p></div>", memPercent, ) // 5. Write raw Server-Sent Event formatted chunks: // event: <name> data: <payload> fmt.Fprintf(w, "event: cpu_update data: %s ", cpuFragment) fmt.Fprintf(w, "event: mem_update data: %s ", memFragment) // 6. Push data down the TCP pipe instantly! flusher.Flush() } } } func main() { http.Handle("/", http.FileServer(http.Dir("./static"))) http.HandleFunc("/stats/stream", statsStreamHandler) fmt.Println("🚀 Dashboard server listening at http://localhost:8080") http.ListenAndServe(":8080", nil) }

📊 4. Network Optimization & Efficiency

Unlike WebSockets which create full duplex framing overhead, SSE runs over standard HTTP, allowing standard web tools (like reverse proxies, CDNs, HTTP/2 compression) to cache and compress headers automatically.

Additionally, browsers support native automatic reconnection out of the box. If the network drops, the browser automatically retries the connection in the background without custom JS reconnect handlers.


🏁 5. Conclusion

Server-Sent Events offer a lightweight, highly efficient protocol for server-to-client notifications. By streaming HTML fragments directly from Go concurrency channels and binding them to DOM structures using HTMX, you construct high-performance live dashboards while avoiding complex client-side state engines.

Sachin Sharma

Sachin Sharma

Software Developer

Building digital experiences at the intersection of design and code. Sharing weekly insights on engineering, productivity, and the future of tech.