# Real-time: WebSockets vs SSE vs Polling > Match the right real-time transport to your data-flow direction. Frontend architecture guidance from fearchitect, written by Abas Turabli and last reviewed 2026-06-21. Source: https://fearchitect.com/topics/realtime-websockets-sse-polling Use it as reference for the task at hand. Before changing code, check this guidance against the codebase: where the code already makes a different, deliberate choice, flag the conflict instead of rewriting it. Library APIs move faster than this guide, so confirm exact signatures in the official docs linked at the end. ## Summary Polling, Server-Sent Events, and WebSockets each trade connection state, directionality, and infrastructure cost differently. Short polling is the simplest fallback; SSE is HTTP-native one-way push; WebSockets give full-duplex at the cost of stateful servers and trickier load-balancing. ## Transport comparison | Transport | Direction | Reconnect | Protocol | Best fit | | --- | --- | --- | --- | --- | | **Short polling** | Client → Server (request-driven) | None — new request each tick | Plain HTTP GET on timer | Any proxy, any CDN | | **Long polling** | Client → Server (request-driven) | New request on response | Plain HTTP, server blocks | Low-latency fallback when SSE blocked | | **SSE (EventSource)** | Server → Client only | Automatic + Last-Event-ID replay | HTTP/1.1 text/event-stream | Notifications, feeds, live dashboards | | **WebSocket** | Full-duplex (both directions) | Manual — you write backoff logic | HTTP 101 upgrade → WS frames | Chat, games, collaborative editing | ## SSE with controlled backoff Close EventSource on error and schedule reconnect manually so backoff delay stays predictable — the browser's built-in retry uses a fixed 3 s. **SSE with EventSource and exponential-backoff reconnect** ```ts // Works in any browser; replace URL with your endpoint. function connectSSE(url: string, onMessage: (data: string) => void) { let es: EventSource; let retryDelay = 1_000; // ms function connect() { es = new EventSource(url, { withCredentials: true }); es.addEventListener("message", (e) => { retryDelay = 1_000; // reset on success onMessage(e.data); }); es.addEventListener("error", () => { es.close(); // EventSource would auto-reconnect, but we want backoff control setTimeout(connect, retryDelay); retryDelay = Math.min(retryDelay * 2, 30_000); }); } connect(); return () => es.close(); // cleanup } // Server side (Node / Edge): // res.setHeader("Content-Type", "text/event-stream"); // res.setHeader("Cache-Control", "no-cache"); // res.write(`data: ${JSON.stringify(payload)}\n\n`); ``` Closing EventSource on error bypasses the browser's fixed 3 s retry; backoff doubles up to 30 s, preventing server floods. ## Diagram ```mermaid sequenceDiagram participant C as Client participant S as Server Note over C,S: Short polling C->>S: GET /events (timer) S-->>C: 200 (data or empty) Note over C,S: SSE C->>S: GET /stream (text/event-stream) S-->>C: data: event1\n\n S-->>C: data: event2\n\n Note over C,S: WebSocket C->>S: GET /ws (Upgrade: websocket) S-->>C: 101 Switching Protocols C-->>S: frame (client→server) ``` Polling opens a new connection per tick; SSE streams over one long HTTP response; WebSocket upgrades to a full-duplex channel. ## Decision: Pick by directionality first One-way server push → SSE. Both sides send → WebSocket. No persistent connection acceptable, or behind proxies that block upgrades → polling. ## Key terms - **EventSource**: Browser API for SSE: opens a persistent GET, fires `message` events, auto-reconnects with `Last-Event-ID`. - **WebSocket upgrade**: HTTP 101 handshake that switches a TCP connection to the WebSocket frame protocol. - **Long polling**: Server holds a request open until data is ready, reducing empty responses vs. fixed-interval polling. - **Sticky session**: Load-balancer rule routing one client to the same server node, needed for stateful WebSocket connections. - **Heartbeat / ping-pong**: Periodic frames sent to keep a TCP connection alive through idle-timeout proxies. ## Related topics - [Realtime Dashboard](https://fearchitect.com/topics/realtime-dashboard.md): Design a live data dashboard without overwhelming the main thread. - [Server State & Data Fetching](https://fearchitect.com/topics/server-state-data-fetching.md): Async, shared, remote-owned data that requires a dedicated cache layer. - [Optimistic UI & Mutations](https://fearchitect.com/topics/optimistic-ui-mutations.md): Update the UI before the server replies; roll back on error. - [CDN & Edge Caching](https://fearchitect.com/topics/cdn-edge-caching.md): Serve cached responses from PoPs near users, sparing the origin. - [Load Balancing (for Frontends)](https://fearchitect.com/topics/load-balancing.md): Spread traffic across servers; keep WebSocket apps sticky. - [File Upload](https://fearchitect.com/topics/file-upload.md): Presigned URLs, chunked uploads, progress UI, and retry. ## Further reading - [MDN — EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) - [MDN — WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) - [MDN — Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) - [RFC 6455 — The WebSocket Protocol](https://www.rfc-editor.org/rfc/rfc6455)