Back to Blog
Engineering

Building a Real-Time Dashboard with Server-Sent Events

Traxo Team|February 18, 20268 min read

When we set out to build real-time updates for Traxo's monitoring dashboard, WebSockets seemed like the obvious choice. They are bidirectional, widely supported, and every real-time tutorial on the internet uses them. But after evaluating our requirements, we chose Server-Sent Events instead -- and the simplicity gains have been significant.

Why not WebSockets

WebSockets are designed for bidirectional communication. Chat applications, collaborative editors, and multiplayer games all need the client to send data back to the server over the same connection. A monitoring dashboard does not. The client subscribes to updates and renders them. The data flows in one direction: server to client.

WebSockets also require more infrastructure. You need sticky sessions or a Redis adapter for horizontal scaling. Load balancers need WebSocket-aware configuration. Connection state management adds complexity to both server and client code. For a unidirectional data flow, this is overhead without benefit.

The SSE architecture

Our implementation uses three components: a Redis pub/sub layer for cross-process event distribution, an SSE endpoint that authenticates the user and streams events, and a client-side hook that manages the EventSource connection with automatic reconnection.

On the server side, the worker publishes events to Redis channels whenever a probe completes or a monitor changes status. The SSE endpoint at /api/events/monitors subscribes to these channels and filters events by the authenticated user's organization before writing them to the response stream.

Server implementation

The SSE endpoint is a standard Next.js route handler that returns a ReadableStream. The key details: we set the correct headers (Content-Type: text/event-stream, Cache-Control: no-cache, Connection: keep-alive), authenticate the request using the session token, subscribe to Redis pub/sub, and pipe filtered events to the stream. A heartbeat comment is sent every 30 seconds to keep the connection alive through proxies that might close idle connections.

Client implementation

On the client, a custom useRealtime hook creates an EventSource connection and calls a callback function whenever an event arrives. The hook handles reconnection with exponential backoff, cleans up on unmount, and pauses when the tab is not visible (using the Page Visibility API) to reduce unnecessary resource consumption.

Each dashboard hook -- useMonitors, useMonitor, useDashboardStats, useIncidents -- integrates with the real-time hook to trigger a data refresh when relevant events arrive. The polling interval is set to 60 seconds as a fallback, but in practice, the SSE connection delivers updates within milliseconds of each probe completion.

Scaling considerations

SSE connections are long-lived HTTP connections, so they consume a file descriptor on the server for each connected client. For our use case -- dashboard users who are actively viewing their monitors -- the connection count is bounded by concurrent dashboard sessions, not total users. A typical deployment handles thousands of SSE connections without issue.

If we needed to scale beyond that, the architecture supports it naturally. Additional API server instances each subscribe to the same Redis pub/sub channels. There is no shared connection state between servers, so horizontal scaling is straightforward.

Results

The SSE approach gives us sub-second dashboard updates with minimal infrastructure complexity. Monitor status changes, new check results, and incident transitions all appear on the dashboard within milliseconds of occurring. The implementation is roughly 200 lines of server code and 80 lines of client code -- considerably simpler than an equivalent WebSocket setup with connection state management and room-based broadcasting.

If your use case is server-to-client event streaming, SSE deserves serious consideration before reaching for WebSockets. The protocol is simpler, the browser API is cleaner, and the infrastructure requirements are lower. Sometimes the less glamorous technology is the right one.

Start monitoring with Traxo

Free plan includes 5 monitors. No credit card required.