Real-time frontend architecture enables instant data updates without page refreshes — choosing between WebSockets, Server-Sent Events, and long polling depends on whether your communication is bidirectional, and building features like presence indicators, notifications, and collaborative editing requires understanding event-driven patterns and conflict resolution.
WebSockets for bidirectional communication (chat, collaboration), Server-Sent Events for server-to-client streams (notifications, feeds), long polling as a universal fallback.
Presence indicators require periodic client heartbeats and server-side timeouts — efficient broadcasting sends updates only to users who can see the presence change.
Operational Transformation resolves concurrent edits via a central server (Google Docs approach); CRDTs merge without conflicts by mathematical design and work peer-to-peer (Yjs, Automerge).
Real-time notifications need priority levels, batching/grouping for rapid-fire events, offline persistence with sync-on-reconnect, and deduplication across reconnections.
Decouple transport (WebSocket/SSE) from business logic using event emitters and pub/sub patterns — components subscribe to events they care about, not to raw connection messages.
Real-time features transform static web pages into living, interactive applications. Chat messages appear instantly, cursors move in collaborative documents, stock prices update continuously, and notifications pop up without refreshing. Implementing these features requires understanding the transport mechanisms, architectural patterns, and conflict resolution strategies that make real-time work reliably.
WebSockets provide full-duplex, persistent connections between client and server. After an HTTP upgrade handshake, both sides can send messages at any time over a single TCP connection.
const ws = new WebSocket('wss://api.example.com/ws');
ws.onopen = () => ws.send(JSON.stringify({ type: 'subscribe', channel: 'chat' }));
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
// Handle incoming message
};
ws.onclose = () => { /* Reconnect logic */ };When to use: Bidirectional communication — chat, multiplayer games, collaborative editing, live trading. The client needs to both send and receive messages frequently.
Trade-offs: Requires WebSocket server infrastructure (not all load balancers/proxies handle WebSocket well), more complex connection management (reconnection, heartbeats), doesn't benefit from HTTP caching.
SSE provides a unidirectional stream from server to client over standard HTTP. The browser's EventSource API handles reconnection automatically.
const source = new EventSource('/api/notifications');
source.onmessage = (event) => {
const data = JSON.parse(event.data);
// Handle notification
};
source.onerror = () => { /* Auto-reconnects by default */ };When to use: Server-to-client updates only — notifications, live feeds, stock tickers, build status, real-time dashboards. The client doesn't need to send messages back through the same channel (it can use regular HTTP requests for that).
Trade-offs: Simpler than WebSockets (regular HTTP, works with all proxies, auto-reconnect), but limited to text data, unidirectional, and limited concurrent connections per domain (6 in HTTP/1.1, unlimited in HTTP/2).
Long polling simulates real-time by keeping an HTTP request open until the server has new data:
When to use: Fallback when WebSocket and SSE aren't available (restrictive corporate proxies, legacy infrastructure). Also acceptable for low-frequency updates where the complexity of persistent connections isn't justified.
Trade-offs: Higher latency and overhead (new HTTP connection per update), but works everywhere and requires no special server infrastructure.
A notification system requires several architectural components:
Showing who's online (green dot, "typing..." indicator, live cursors) requires:
Collaborative editing (Google Docs, Figma, Notion) is one of the hardest real-time problems because multiple users can edit the same content simultaneously, creating conflicts.
OT transforms concurrent operations so they can be applied in any order and produce the same result. If user A inserts "x" at position 5 and user B deletes character at position 3, OT adjusts A's operation to insert at position 4 (since B's deletion shifted the text). Google Docs uses OT.
Pros: Battle-tested, works well for text editing. Cons: Complex algorithm, requires a central server to order operations, hard to implement correctly.
CRDTs are data structures that can be merged without conflicts by mathematical design. Each user edits their local copy, and all copies converge to the same state when synced — no central server needed for conflict resolution.
Pros: Works peer-to-peer, handles offline editing naturally, mathematically guaranteed convergence. Cons: Higher memory overhead (metadata for each character/operation), newer technology with fewer production-proven implementations.
Popular CRDT libraries: Yjs, Automerge, Liveblocks.
Real-time systems are inherently event-driven. On the frontend, this means:
Choose WebSockets for bidirectional real-time (chat, collaboration), SSE for server-to-client streams (notifications, feeds), and long polling as a fallback. Presence requires heartbeats with timeouts. Collaborative editing needs conflict resolution — OT for server-centric text editing, CRDTs for peer-to-peer or offline-first scenarios. Real-time systems are event-driven: decouple message transport from business logic using pub/sub patterns.
Fun Fact
Google Docs originally used Operational Transformation (OT) for collaborative editing, but when Google built their next-generation collaboration system, they explored CRDTs. The Yjs CRDT library can handle documents with over 100,000 concurrent characters while maintaining sub-millisecond merge times.