← Blog
React

Real-Time Dashboards with Socket.IO and React: Patterns That Survive Production

By Muzamal Ali8 min readSocket.IO · Real-time · React

The problem with naive real-time

Adding Socket.IO to a React app looks trivial in a demo: connect, listen, set state. In production — I built an aircraft operations dashboard that lived on this stack for ten months — the naive version falls apart on reconnection storms, duplicate listeners after hot navigation, and state that drifts from the server after a missed event. The patterns below are what survived.

One connection, owned outside React

The socket connection does not belong inside a component. Components mount and unmount with navigation; your connection should not. I keep a singleton socket module — created once, imported anywhere — and expose subscription hooks on top of it:

// socket.ts
import { io } from "socket.io-client";
export const socket = io(WS_URL, {
  autoConnect: false,
  reconnectionDelay: 1000,
  reconnectionDelayMax: 10000,
});

React then consumes it through a hook that subscribes on mount, unsubscribes on unmount, and never owns the connection lifecycle. That separation eliminated an entire class of duplicate-listener bugs.

Reconciliation beats accumulation

The tempting pattern — append every incoming event to local state — drifts the moment a packet is missed. For the flight dashboard, every reconnect triggered a full snapshot fetch over REST, and socket events only patched state between snapshots. Server state stayed authoritative:

  • On connect or reconnect: fetch snapshot, replace state
  • On event: apply patch optimistically
  • On visibility change after sleep: re-snapshot, because laptops close lids

This "snapshot plus patches" model cut our data-latency complaints to zero and reduced processing latency by half compared to the previous accumulate-and-hope approach.

Backpressure and batching

Real-time geospatial data arrives faster than the UI needs to render it. Updating React state on every message at 20Hz melts low-end machines. We batched socket events into a buffer flushed on requestAnimationFrame, and memoized map overlays so only changed aircraft re-rendered. The map stayed smooth while the wire stayed busy.

Failure states are UI states

A real-time dashboard that silently stops being real-time is dangerous — users trust stale data. Connection state was rendered explicitly: a status indicator, a "last updated" timestamp, and a degraded-mode banner when reconnecting. Operations staff told us this single banner was the most trusted feature in the app.

Testing the unhappy paths

The bugs live in reconnection, not connection. We tested by killing the server mid-session, throttling the network, and sleeping the laptop — manually at first, then scripted. If your real-time feature has only been tested on a stable localhost connection, it has not been tested.

Where this fits

Real-time UI is one of those areas where senior experience pays for itself quickly, because the failure modes are invisible in demos and expensive in production. The aviation project this article draws from is documented in the case studies, and the architecture principles behind it are in how I structure React components for teams of 10+.

Working on something similar?

I help European tech teams ship better frontends.

Let's Work Together