Design a Chat Application
The Prompt
Design the frontend architecture for a team chat application similar to Slack with channels, direct messages, threads, and real-time updates.
Data Model
interface Message {
id: string;
channelId: string;
threadId?: string;
authorId: string;
content: string;
timestamp: number;
edited?: boolean;
reactions: Record<string, string[]>;
attachments: Attachment[];
}
interface Channel {
id: string;
name: string;
type: 'public' | 'private' | 'dm';
members: string[];
unreadCount: number;
lastMessage?: Message;
}Architecture Decisions
Message Loading Strategy
- Initial load: Last 50 messages per visible channel
- Scroll up: Fetch older messages in pages (cursor-based pagination)
- New messages: WebSocket push â append to local store
- Search: Server-side full-text search with highlighted results
Real-Time Connection
Single WebSocket â Multiplexed channels
â Message events (new, edit, delete)
â Presence events (online, typing, away)
â Channel events (created, updated, member changes)One WebSocket connection multiplexed across all subscribed channels. Avoids connection overhead.
State Management
ââ Channel list (sorted by last activity)
Global Store âââââââžâ Active channel messages (virtualized)
ââ Thread messages (lazy loaded)
ââ User presence map
ââ Unread countsOffline Support
- IndexedDB for message cache
- Optimistic updates â show sent message immediately
- Queue â store unsent messages, retry on reconnect
- Conflict resolution â server timestamp is authoritative
Performance
- Virtualize the message list â only render visible messages
- Lazy-load images and file previews
- Debounce typing indicators (500ms)
- Batch unread count updates
Key Interactions
| Feature | Approach |
|---|---|
| Typing indicator | Debounced broadcast, auto-expire after 3s |
| Message editing | Optimistic update + server confirmation |
| Reactions | Toggle mutation, optimistic UI |
| Thread replies | Separate message list, lazy-loaded |
| @mentions | Client-side user search with fuzzy matching |
The real challenge isn't sending messages â it's keeping state consistent across multiple concurrent views with minimal re-renders.