Design the frontend for a real-time collaborative dashboard
Streaming transport (WS or SSE) feeds a normalized client store; the UI subscribes to slices via selectors. Reconciliation, presence, conflict resolution, backpressure, reconnect/replay are explicit design choices.
Walk an interviewer through these layers:
1. Requirements / clarifying questions.
- How many concurrent viewers per board? Per cell?
- Read-only or read-write? Conflict semantics if write?
- Latency budget (≤200ms typical for "real-time").
- Mobile + offline?
2. Transport.
- Read-only feed → SSE (HTTP/2, auto-reconnect, simpler ops).
- Bidirectional / collab editing → WebSocket.
- Always plan reconnect with
Last-Event-ID(SSE) or sequence numbers (WS) for replay.
3. Data model. Normalize entities into a flat store keyed by id (Redux/Zustand/Apollo cache). Each component subscribes to a selector — only the affected components re-render on a delta.
4. Server → client deltas. Send patches, not full snapshots. JSON Patch / custom op log. On reconnect, request "since seq N" so the client replays missed ops.
5. Conflict resolution (if write).
- Last-write-wins — simplest. OK for non-collaborative widgets.
- Operational transforms or CRDTs — for collaborative editing (Yjs/Automerge).
6. Presence and cursors. A separate ephemeral channel for "who's viewing / typing". Don't mix with persisted state.
7. Backpressure. A high-frequency stream can flood the UI. Coalesce updates per animation frame; throttle aggregator queries.
8. UX for connection state. Show connection status, retry status, last-synced time. Users tolerate problems they can see.
9. Scaling. Server side: pub/sub (Redis, NATS) so any pod can deliver to any client. Sticky sessions for WS. Auth at upgrade time.
10. Observability. Per-message latency, dropped-message counts, reconnect frequency.
Code
Follow-up questions
- •How do you handle a slow consumer that can't keep up with the stream?
- •How would you replay missed events after a reconnect?
- •How do you design the conflict-resolution UI for a collaborative cell edit?
Common mistakes
- •Re-rendering the whole dashboard on every message — selector subscriptions are essential.
- •Sending full snapshots instead of deltas.
- •No reconnect/replay — a brief disconnect leaves the UI silently stale.
Performance considerations
- •Coalesce N updates per rAF tick. Diffing once per frame is far cheaper than per-message setState.
Edge cases
- •Tab in background — browsers throttle timers; the stream can fall behind. Mark as stale, refresh on focus.
- •Auth token expiry mid-session — refresh and reconnect transparently.
Real-world examples
- •Linear, Notion, Figma, Datadog dashboards — all use a streaming transport + normalized client store + selector subscriptions.