Skip to main content

Real-Time Updates

Real-time updates let an interface reflect new server data without requiring the user to refresh the page manually.

Data changes on the server
↓
Update reaches the client
↓
Client validates and applies it
↓
Affected UI renders again

Common examples include chat messages, delivery tracking, live scores, market data, notifications, collaborative editing, and operational dashboards.

Core idea: Choose the simplest communication model that meets the required update direction, latency, reliability, and scale.


1. “Real Time” Is a Product Requirement​

Real time is not one specific technology. It describes how fresh the interface must be.

ExperiencePossible freshness target
Support dashboardSeveral seconds
Delivery statusA few seconds
ChatSub-second to a few seconds
Collaborative editingOften hundreds of milliseconds
Multiplayer controlsMuch lower latency and specialized networking

Before choosing a transport, ask:

  • How quickly must updates appear?
  • Is communication server-to-client or bidirectional?
  • How many users and subscriptions exist?
  • Can an update be lost, duplicated, or delayed?
  • Must clients recover events missed while offline?
  • How large and frequent are messages?

Polling every five seconds may be correct for one dashboard and unacceptable for one collaborative editor.


2. Main Communication Options​

ApproachConnection modelDirectionBest fit
Short pollingRepeated HTTP requestsClient requests, server respondsSimple and relaxed freshness
Long pollingServer holds each HTTP requestMostly server-to-client deliveryCompatibility with near-real-time behavior
Server-Sent Events (SSE)Long-lived HTTP responseServer to clientNotifications, feeds, progress, dashboards
WebSocketPersistent upgraded connectionBidirectionalChat, presence, collaboration, interactive systems
WebhookServer sends HTTP request to another serverServer to serverExternal system integration

Webhooks do not directly update a browser. A receiving backend can process the webhook and then notify its connected clients through polling, SSE, or WebSockets.


3. Short Polling​

The client requests the latest state at a fixed or adaptive interval.

Client → “Any updates?” → Server response
wait
Client → “Any updates?” → Server response
wait

Advantages​

  • simple HTTP infrastructure;
  • easy authentication, caching, and debugging;
  • works through most proxies and networks;
  • failure is isolated to one request.

Limitations​

  • updates wait until the next interval;
  • empty requests waste bandwidth and server work;
  • short intervals can create heavy traffic;
  • overlapping requests can return out of order.

Safe React Polling Pattern​

Use recursive setTimeout rather than a blind setInterval so a new request begins only after the previous attempt finishes.

import { useEffect, useState } from "react";

function useOrderStatus(orderId, intervalMs = 5000) {
const [order, setOrder] = useState(null);
const [error, setError] = useState(null);

useEffect(() => {
let isActive = true;
let timerId;
let currentController;

async function poll() {
if (!isActive) return;

if (document.hidden) {
timerId = window.setTimeout(poll, intervalMs);
return;
}

currentController = new AbortController();

try {
const response = await fetch(`/api/orders/${orderId}`, {
signal: currentController.signal,
});

if (!response.ok) throw new Error("Could not load order");

const nextOrder = await response.json();
if (isActive) {
setOrder(nextOrder);
setError(null);
}
} catch (requestError) {
if (requestError.name !== "AbortError" && isActive) {
setError(requestError);
}
} finally {
if (isActive) {
timerId = window.setTimeout(poll, intervalMs);
}
}
}

poll();

return () => {
isActive = false;
window.clearTimeout(timerId);
currentController?.abort();
};
}, [orderId, intervalMs]);

return { order, error };
}

The server can reduce payload size using conditional requests, a since cursor, version, or timestamp instead of returning the entire resource every time.


4. Adaptive Polling​

The polling interval does not have to remain constant.

Active operation   → poll every 2 seconds
No changes found → gradually slow to 10 seconds
Page hidden → pause or use a much slower interval
User returns → refresh immediately
Repeated failures → exponential backoff with jitter

Adaptive polling lowers cost while keeping important periods responsive.

Use the Page Visibility API as a resource hint, not as proof that the user has left forever. When the page becomes visible again, reconcile with the server before assuming local state is current.


5. Long Polling​

With long polling, the server holds the request until data becomes available or a timeout is reached.

Client → request
Server waits...
Server waits...
Server → event or timeout response
Client → immediately opens the next request

Advantages​

  • lower empty-request frequency than short polling;
  • uses ordinary HTTP request-response semantics;
  • useful where streaming connections are restricted.

Limitations​

  • every response requires a new request;
  • timeouts and reconnect behavior need careful handling;
  • proxies may terminate long-held requests;
  • servers must manage many pending requests;
  • duplicate delivery is possible around reconnect boundaries.

Include a cursor or last-seen event ID in every new request so the server can resume after the previous confirmed event.


6. Server-Sent Events​

SSE keeps an HTTP response open and streams UTF-8 text events from server to browser.

Client → subscribe once
Server → event
Server → event
Server → event

It is one-way for the event stream: server to client. The client can still use normal HTTP requests for commands.

Browser Client​

useEffect(() => {
const source = new EventSource(`/api/orders/${orderId}/events`);

source.addEventListener("status-changed", event => {
const update = JSON.parse(event.data);
applyOrderEvent(update);
});

source.onerror = () => {
setConnectionState("reconnecting");
};

source.onopen = () => {
setConnectionState("connected");
};

return () => source.close();
}, [orderId]);

Event Stream​

event: status-changed
id: 8421
retry: 5000
data: {"orderId":"o-17","status":"shipped","version":6}

Each event ends with a blank line. Important fields include:

FieldPurpose
eventNamed event type
dataEvent payload
idResume identifier
retrySuggested reconnect delay in milliseconds

Browsers reconnect EventSource automatically. When the stream provides IDs, the browser can send Last-Event-ID during reconnection so the server can resume if it retains an event history.

Good SSE Use Cases​

  • progress updates;
  • notification streams;
  • live dashboards;
  • content feeds;
  • server-side job status;
  • read-heavy monitoring.

SSE is not “free.” Long-lived connections, proxy timeouts, connection limits, buffering, authentication, and resume retention still require design.


7. WebSockets​

WebSockets create a persistent, bidirectional connection. Either side can send a message at any time.

Client ⇄ Server

Browser Client​

useEffect(() => {
const socket = new WebSocket("wss://example.com/realtime");

socket.addEventListener("open", () => {
socket.send(JSON.stringify({
type: "subscribe",
channel: `conversation:${conversationId}`,
}));
});

socket.addEventListener("message", event => {
const message = JSON.parse(event.data);
handleRealtimeMessage(message);
});

socket.addEventListener("close", closeEvent => {
setConnectionState(
closeEvent.wasClean ? "closed" : "disconnected"
);
});

socket.addEventListener("error", () => {
setConnectionState("error");
});

return () => socket.close(1000, "Component unmounted");
}, [conversationId]);

Good WebSocket Use Cases​

  • chat and presence;
  • collaborative editing;
  • multiplayer interactions;
  • bidirectional device control;
  • rapidly changing trading or operations interfaces.

Tradeoffs​

  • the application owns reconnection and resubscription;
  • authentication and authorization must be maintained after connection;
  • servers keep connection state and heartbeats;
  • deployments need WebSocket-capable proxies and load balancers;
  • the classic browser WebSocket API has no automatic backpressure;
  • message schemas and compatibility require governance.

A persistent connection can reduce repeated request overhead, but it does not automatically make a system cheaper or more scalable. Connection count, fan-out, message rate, and processing cost determine capacity.


8. Webhooks Are Server-to-Server​

A webhook notifies another backend by sending an HTTP request to a registered endpoint.

Payment provider → your webhook endpoint
↓
verify and process
↓
persist authoritative state
↓
notify browser through SSE/WS

Webhook receivers should verify signatures, reject replays, respond within the provider's timeout, process idempotently, and queue slow work.

Do not expose a browser as a webhook endpoint or trust an unsigned payload.


9. How to Choose​

Can updates arrive several seconds late?
└── Yes → start with polling

Does only the server need to push updates?
└── Yes → consider SSE

Do client and server both send frequent messages?
└── Yes → consider WebSockets

Is one backend notifying another backend?
└── Yes → webhook

Are streaming connections unavailable?
└── Yes → polling or long polling
RequirementPollingLong pollingSSEWebSocket
Simple HTTP infrastructureExcellentGoodGoodMore specialized
Server pushDelayedNear-real-timeYesYes
Client sends frequent stream messagesNoNoSeparate HTTPYes
Built-in browser reconnectApplication logicApplication logicYesApplication logic
Binary messagesNormal HTTPNormal HTTPNoYes
Easy cacheable snapshotsYesPossibleSeparate endpointSeparate endpoint

Start with requirements rather than assuming the most sophisticated transport is best.


10. Separate Commands, Events, and State​

These messages have different meanings.

Command: “Send this chat message”
Event: “Message 921 was created”
State: “Here is the current conversation snapshot”

Commands express intent and may fail validation. Events describe something that already happened. A snapshot represents current authoritative state.

Even with WebSockets, commands may still use HTTP because request-response semantics, idempotency keys, validation errors, and tracing can be simpler there. The live channel then delivers resulting events.


11. Design an Event Envelope​

Do not send unrelated anonymous JSON shapes without shared metadata.

{
"eventId": "evt_8421",
"type": "order.status_changed",
"occurredAt": "2026-09-16T12:34:56Z",
"entityId": "order_17",
"entityVersion": 6,
"schemaVersion": 1,
"payload": {
"status": "shipped"
}
}
FieldWhy it matters
eventIdDeduplication and resume position
typeRoutes the event to the correct handler
occurredAtObservability and display, not always ordering
entityIdIdentifies affected data
entityVersionDetects stale or missing updates
schemaVersionSupports compatible evolution
payloadContains event-specific data

Clients should ignore unknown event types safely and validate data before applying it.


12. Snapshot Plus Event Stream​

A live stream usually contains changes, not the complete current state. New clients need both.

1. Fetch a snapshot with resume cursor 8400
2. Connect to the event stream after cursor 8400
3. Apply events 8401, 8402, 8403...

The snapshot and cursor must represent one consistent point. Otherwise an event can occur between fetching the snapshot and subscribing, creating a gap.

Common solutions include:

  • snapshot response includes a high-water cursor;
  • subscribe first and buffer events while loading the snapshot;
  • stream endpoint sends an initial snapshot followed by changes;
  • reconnect falls back to a fresh snapshot when retention is exceeded.

The server, not the client clock, should define the resume boundary.


13. Delivery Is Usually At Least Once​

Networks can disconnect after the server sends an event but before the client confirms or records it. Reconnection may deliver that event again.

Design handlers to be idempotent:

function applyEvent(event) {
if (processedEventIds.has(event.eventId)) return;

updateEntity(event);
processedEventIds.add(event.eventId);
}

Do not implement a financial or inventory change as “increment by one” if a duplicate event would apply it twice. Prefer setting an authoritative value or versioned transition when possible.

Deduplication state must live long enough for the delivery and replay window.


14. Ordering and Versioning​

Global ordering across a distributed system is expensive and often unnecessary. Many products need ordering only within one entity or channel.

order_17 version 5
order_17 version 7 arrives
↓
version 6 is missing → buffer briefly or refetch order_17

Do not rely only on client receipt time or wall-clock timestamps. Network delays can reorder messages, and clocks can differ.

Use sequence numbers, entity versions, partition offsets, or another server-defined ordering rule.


15. Reconnection and Missed Updates​

Every persistent connection eventually disconnects because of network changes, sleep, proxy timeouts, deployments, or server failure.

A robust reconnect flow is:

Connection closes unexpectedly
↓
Wait with exponential backoff + jitter
↓
Reconnect and authenticate
↓
Resubscribe to channels
↓
Resume from last confirmed event ID
↓
If cursor expired, fetch a fresh snapshot

Example delays:

1s, 2s, 4s, 8s, 16s ... capped, with random jitter

Jitter prevents thousands of clients from reconnecting at exactly the same time after an outage.

Reset the backoff only after the connection remains healthy long enough, not immediately after a socket opens and closes again.


16. Heartbeats and Liveness​

A connection may appear open even when one network path has silently failed.

Use protocol or application heartbeats:

Server → ping or heartbeat
Client → pong when required

No heartbeat within timeout
↓
Mark unhealthy and reconnect

For SSE, comment lines can keep intermediaries from treating an idle stream as abandoned. For WebSockets, servers and infrastructure may provide ping/pong frames, while an application heartbeat can verify end-to-end behavior.

Choose intervals carefully: frequent heartbeats consume bandwidth and battery; slow heartbeats delay failure detection.


17. Backpressure and Burst Handling​

If updates arrive faster than the client can process or render them, memory and CPU usage grow.

Possible policies include:

  • batch multiple updates into one render;
  • keep only the latest value for replaceable metrics;
  • sample high-frequency telemetry;
  • pause or reduce subscriptions;
  • apply bounded queues;
  • disconnect and resynchronize if the client falls too far behind.
Chat messages       → preserve every accepted message
Live cursor movement → newest position can replace older positions
CPU metric → aggregate or sample
Inventory event → preserve or refetch authoritative state

Backpressure policy depends on event meaning. Dropping arbitrary messages is not a universal solution.


18. Client State Integration​

Avoid maintaining a second disconnected version of every entity for the live channel.

Initial HTTP query → shared entity cache
Realtime event → update or invalidate the same cache
UI → reads one source of truth

An event can:

  • update a cached entity directly;
  • insert or remove an entity from a list;
  • invalidate a query and trigger a refetch;
  • display a “New updates available” indicator;
  • update temporary presence state.

Direct updates are fast but need accurate event schemas. Invalidation is simpler and safer but causes another request. Many applications combine both.


19. Optimistic Updates and Reconciliation​

For user actions such as sending a message:

Create temporary local item
↓
Send command with idempotency/client ID
↓
Server accepts and emits authoritative event
↓
Replace temporary item with confirmed record

The client-generated ID helps match its optimistic item to the server event. Without reconciliation, both the optimistic copy and live event may appear as duplicates.

On failure, keep enough context to retry or recover the user's work.


20. Authentication and Authorization​

Authenticate the connection and authorize every subscription and command.

Authenticated user requests channel order:17
↓
Server checks access to order:17
↓
Only then attach the subscription

Important controls include:

  • use https:// and wss:// in production;
  • validate origins where applicable;
  • use secure cookies or short-lived connection credentials;
  • re-check permissions for sensitive operations;
  • handle token expiry and account changes;
  • validate message type, size, and schema;
  • rate-limit subscriptions and commands;
  • prevent users from guessing unauthorized channel names.

The native EventSource constructor does not provide a general custom-header API. Prefer secure same-origin cookies, an authenticated streaming endpoint, or carefully designed short-lived credentials rather than long-lived secrets in URLs.


21. Scaling Persistent Connections​

One application server is not enough for a large fan-out system.

Domain services
↓ publish events
Message broker / pub-sub system
↓
Realtime gateway instances
↓ filter and fan out
Connected clients

The gateway needs to know which connections subscribe to which channels. A distributed broker lets an event produced on one node reach clients connected to other nodes.

Design for:

  • total concurrent connections;
  • connections created and closed per second;
  • messages per second and average size;
  • fan-out per event;
  • slow consumers;
  • regional latency;
  • rolling deployments and graceful draining;
  • event retention for reconnect recovery.

Sticky sessions can simplify some stateful designs, but shared subscription and event infrastructure is still needed for resilience and multi-node fan-out.


22. Accessibility and UX​

Live updates should help without constantly interrupting the user.

  • Use a polite live region for important, infrequent status changes.
  • Do not announce rapidly changing dashboards on every update.
  • Preserve focus when content is inserted or reordered.
  • Avoid moving a user's reading position unexpectedly.
  • Offer a “New messages” or “Updates available” control when automatic insertion would be disruptive.
  • Show connection state when freshness matters.
  • Provide retry or manual refresh when disconnected.
  • Do not rely on color alone to show changed values.

For fast-moving values, a visual update can occur without a screen-reader announcement. Provide summaries or on-demand detail instead.


23. Observability​

Measure the full path from event creation to UI application.

Useful metrics include:

  • active connections and subscriptions;
  • connection success and failure rate;
  • reconnect attempts and duration;
  • events produced, delivered, dropped, and duplicated;
  • event delivery latency;
  • client processing and render latency;
  • queue depth and consumer lag;
  • heartbeat timeouts;
  • snapshot fallbacks after resume failure;
  • authorization rejection rate.

Include event IDs, connection IDs, user or tenant context, and trace correlation where policy permits. Never log secrets or complete sensitive payloads unnecessarily.


24. Common Mistakes​

MistakeBetter approach
Choosing WebSockets because the feature is called real timeStart from direction, latency, volume, and reliability requirements
Polling with setInterval while requests overlapSchedule the next request after completion
Polling hidden tabs at full frequencyPause or slow work and reconcile on return
Assuming SSE or WebSockets never disconnectImplement recovery and resubscription
Reconnecting every client immediatelyUse capped exponential backoff with jitter
Treating delivery as exactly onceMake consumers idempotent and deduplicate
Applying events without ordering metadataUse versions, sequences, or refetch on gaps
Opening a stream without an initial snapshotCombine snapshot and resume cursor consistently
Updating a separate live-only storeReconcile with the same authoritative client cache
Rendering every high-frequency messageBatch, sample, coalesce, or bound the queue by event semantics
Authorizing only the initial connectionAuthorize subscriptions and commands too
Broadcasting every update to assistive technologyAnnounce only useful changes without moving focus

25. Testing Checklist​

Connection Lifecycle​

  • Initial connection succeeds and authenticates.
  • Cleanup closes the connection or cancels polling.
  • Network loss triggers bounded reconnect behavior.
  • Reconnection restores subscriptions.
  • Expired resume cursors fall back to a snapshot.
  • Hidden and visible page transitions use the intended resource policy.

Data Correctness​

  • Duplicate events do not apply twice.
  • Out-of-order events are reordered, rejected, or repaired.
  • Missing sequence numbers trigger recovery.
  • Optimistic records reconcile with server events.
  • Unknown event and schema versions fail safely.
  • Filtered subscriptions cannot receive unauthorized data.

Load and Failure​

  • Slow consumers cannot exhaust unbounded memory.
  • Event bursts follow the correct batching or preservation policy.
  • Gateway restarts and deployments drain connections safely.
  • Broker delay and partial service failure are visible in metrics.
  • A reconnect storm does not overwhelm the service.

User Experience​

  • Connection loss is communicated when freshness matters.
  • Manual refresh or retry is available.
  • Live insertion does not steal focus or lose user input.
  • Screen-reader announcements are useful and restrained.

Official References​


Summary​

Real-time updates are a reliability pipeline built on polling, SSE, or WebSockets—not merely an open connection.


Think of the feature as five layers:

Authoritative domain change
↓
Versioned event with stable identity
↓
Delivery transport and fan-out
↓
Reconnect, replay, ordering, and backpressure
↓
Client cache update and considerate UI feedback

The transport determines how bytes move. Correct state, recovery, authorization, scaling, and user experience determine whether the feature is truly dependable.