Front-end Engineering Lab
RADIO FrameworkComplete Systems

Real-Time Chat System

System design for chat with typing indicators, read receipts, and presence

Design a real-time chat application (Slack, WhatsApp Web) with typing indicators, read receipts, and presence.

R - Requirements

Key Questions:

  • Message types? (text, images, files, emoji)
  • Typing indicators? (yes, show when typing)
  • Read receipts? (yes, show read status)
  • Presence? (online, offline, away)
  • Group chats? (yes, multiple participants)
  • Message history? (scroll to load older)

Common Answers:

  • Text, images, files, emoji
  • Typing indicators
  • Read receipts (single/double check)
  • Presence status
  • Group and 1-on-1 chats

Talking Points:

  • Real-time chat - WebSocket connection, message ordering, conflict resolution
  • Message syncing - Offline queue, background sync, message deduplication
  • Messages list - Virtualized reverse scroll, message grouping, infinite scroll
  • Chat list - Unread counts, last message preview, presence indicators

A - Architecture

Message Flow:

System Architecture:

Architecture Decisions:

  • WebSocket for real-time (bidirectional, low latency)
  • Message ordering (timestamp + sequence numbers for conflict resolution)
  • Conflict resolution (last-write-wins or CRDT for collaborative editing)
  • Offline support (queue messages in IndexedDB, sync when online)
  • Normalized store (messages by chat ID + message ID for O(1) lookup)

Relevant Content:

D - Data Model

Talking Points:

  • Message syncing - Offline queue (IndexedDB), background sync, message deduplication
  • Messages list - Virtualized reverse scroll, message grouping by date/user

State Management:

  • Messages (normalized by chat ID, message ID)
  • Typing indicators (per chat, per user)
  • Read receipts (per message, per user)
  • Presence (per user)
  • Message queue (offline messages)

Implementation Example - Message Deduplication:

// Normalized message store
const messages: Record<string, Record<string, Message>> = {};
// messages[chatId][messageId] = message

function addMessage(chatId: string, message: Message) {
  // Check if message already exists (deduplication)
  if (messages[chatId]?.[message.id]) {
    return; // Already have this message
  }
  
  // Add to normalized store
  if (!messages[chatId]) {
    messages[chatId] = {};
  }
  messages[chatId][message.id] = message;
  
  // Update UI
  updateChatUI(chatId, Object.values(messages[chatId]));
}

// Typing indicator throttling
let typingTimeout: NodeJS.Timeout;
function onTyping(chatId: string) {
  // Clear previous timeout
  clearTimeout(typingTimeout);
  
  // Send typing indicator
  sendTypingIndicator(chatId);
  
  // Stop typing after 2 seconds of no input
  typingTimeout = setTimeout(() => {
    stopTypingIndicator(chatId);
  }, 2000);
}

Caching:

  • Message cache (recent messages, paginated)
  • Offline queue (IndexedDB for unsent messages)
  • Presence cache (last seen, status)

Relevant Content:

I - Interface

Talking Points:

  • Messages list - Virtualized reverse scroll, message grouping, infinite scroll to load older
  • Chat list - Unread counts, last message preview, presence indicators, sorting

Features:

  • Message list (virtualized, reverse scroll, grouped by date/user)
  • Chat list (unread counts, last message, presence)
  • Input field (emoji picker, file upload)
  • Typing indicator ("User is typing...")
  • Read receipts (single/double check, timestamps)
  • Presence indicators (online, offline, away)
  • Message status (sending, sent, delivered, read)
  • Scroll to bottom button

Accessibility:

  • Keyboard navigation
  • Screen reader support (announce new messages)
  • Focus management (focus input after send)
  • ARIA live regions (typing, presence)

Relevant Content:

O - Optimizations

Performance:

  • Virtual scrolling (for long message history)
  • Message deduplication (avoid duplicate messages)
  • Image optimization (thumbnails, lazy load)
  • Code splitting (emoji picker, file upload lazy load)

Real-Time:

  • WebSocket connection pooling (reuse connections)
  • Message batching (batch multiple messages)
  • Connection resilience (reconnect, queue messages)
  • Throttle typing indicators (don't send on every keystroke)

Offline:

  • Message queue (store unsent messages)
  • Background sync (send when online)

Relevant Content:

Implementation Checklist

  • WebSocket connection
  • Message list (virtualized, reverse scroll)
  • Typing indicators (throttled)
  • Read receipts (single/double check)
  • Presence system (online, offline, away)
  • Message ordering (timestamp, sequence)
  • Offline queue (IndexedDB)
  • Background sync (send when online)
  • Image optimization (thumbnails)
  • Accessibility (keyboard, screen readers)

Common Pitfalls

No message ordering → Messages appear out of order
Use timestamps + sequence numbers, handle conflicts

Typing on every keystroke → Too many messages
Throttle typing indicators (1-2s)

No offline support → Lost messages
Queue messages in IndexedDB, sync when online

Poor connection handling → Lost messages, disconnects
Connection resilience, reconnect logic, message queue

On this page