The Question
FE DesignDesign a Web-based Email Client
Design a scalable, secure, and performant web-based email client similar to Gmail. Focus on handling large volumes of data in the inbox list, ensuring secure rendering of untrusted HTML message content, implementing robust state management for real-time updates, and providing offline-first capabilities. Discuss architectural trade-offs between rendering strategies, security isolation techniques, and client-side storage mechanisms for a smooth, desktop-like user experience.
React
TanStack Query
Zustand
Tailwind CSS
IndexedDB
Service Workers
DOMPurify
WebSockets
Virtualization
Questions & Insights
Clarifying Questions
Scale and Volume: How many emails should a user be able to manage in their inbox?
Assumption: Support for 100k+ emails per user, with an MVP focus on 50-100 emails per page/scroll.
Real-time Requirements: Is real-time notification of new emails required?
Assumption: Yes, using Server-Sent Events (SSE) or WebSockets for instant updates.
Offline Support: Should users be able to read or draft emails offline?
Assumption: MVP will support basic offline reading of cached emails via Service Workers and IndexedDB, but focus on online-first operations.
Rich Text Editing: What level of complexity is needed for the composer?
Assumption: A standard Rich Text Editor (RTE) supporting basic formatting (bold, links, lists) and attachments.
Crash Strategy
Performance Bottleneck: Rendering thousands of email rows and complex HTML content in the message view.
Core Loop: List (Inbox) -> View (Thread) -> Action (Compose/Reply).
Strategy Questions:
How do we ensure the list remains performant regardless of size? (Virtualization).
How do we prevent 3rd party email HTML from breaking our app or compromising security? (Sanitization & Shadow DOM/iFrames).
How do we handle state synchronization across multiple tabs? (BroadcastChannel + Normalized Cache).
How do we ensure the UI feels "snappy" during actions like deleting or moving emails? (Optimistic Updates).
Elite Bonus Points
Web Worker Offloading: Use Web Workers to parse large MIME messages or search through the local IndexedDB cache to keep the main thread free.
Security (XSS Mitigation): Implement a multi-layered sanitization strategy using DOMPurify and Content Security Policy (CSP) headers specifically for the message preview pane.
Micro-Interactions: Implement "Gmail-style" keyboard shortcuts (Command Palette) and swipe-to-action for mobile responsiveness.
Streaming Rendering: For very long email threads, stream the rendering of individual messages to improve Time to Interactive (TTI).
Design Breakdown
Requirements
Functional Requirements:
View inbox/sent/drafts/folders.
Read email threads with attachments.
Compose new emails with rich text formatting.
Search emails by keyword/sender.
Folder/Label management (Move, Delete, Archive).
Non-Functional Requirements:
Performance: < 2s Initial Load; < 100ms for interaction response.
Scalability: Handle large volumes of messages via virtualization.
Security: Strict XSS prevention for rendering untrusted email HTML.
Accessibility: Full ARIA support for screen readers and keyboard navigation.
Responsiveness: Adaptive layout for Desktop, Tablet, and Mobile.
Design Summary
Concise Summary: A high-performance Single Page Application (SPA) utilizing list virtualization for the inbox and a normalized client-side cache to manage email state. It prioritizes security through isolated rendering of email bodies and high-speed navigation via optimistic UI updates.
Major Components:
App Shell: The persistent navigation and global layout container.
Thread List: A virtualized list component for efficient rendering of thousands of emails.
Message Viewer: A secure, isolated container for rendering email content.
Email Composer: A Rich Text Editor component for drafting messages.
Data Sync Manager: Handles real-time updates and local persistence.
CUJ Walkthrough:
Reading: User clicks a Thread Row; the
ThreadDetail fetches the full message and renders it in a sanitized MessageBody component.Organizing: User selects multiple emails and clicks "Archive";
DataSyncManager triggers an optimistic update to remove items locally while the API call completes in the background.Composing: User opens the
Composer; state is saved to a "Drafts" local cache every 5 seconds to prevent data loss.Simplicity Audit: This architecture uses standard REST/WS patterns and virtualization, avoiding complex micro-frontends or distributed databases on the client side, which is sufficient for an MVP.
Architecture Decision Rationale:
Why this architecture?: Virtualization is the industry standard for large lists. Sanitization is non-negotiable for email. Normalized state management (via TanStack Query) ensures that if an email is marked "read" in the list, it is also "read" in the detail view immediately.
Requirement Satisfaction: Meets performance goals via virtualization, security via DOMPurify/iFrames, and UX goals via optimistic updates.
System Diagram
Architecture Deep Dive
Presentation Layer
Component Hierarchy: The App Shell contains the sidebar and search. The Layout Split View manages the 3-column layout (Folders, List, Detail). Thread List Container uses
react-window or virtuoso to render only visible Thread Row Leaf components.Interaction Layer: Keyboard shortcuts (e.g., 'j', 'k' for navigation) are handled at the Shell level. Input validation in the Composer ensures valid email formats and attachment sizes.
Rendering Layer: CSR is used for the main application to provide a desktop-like feel. Message Body Leaf uses a hidden
iframe or Shadow DOM to encapsulate styles and prevent CSS leakage from the email to the app.UI Frameworks / Tools: React for UI, Tailwind CSS for styling, Headless UI for accessible modals/dropdowns.
Application Layer
Data Fetching Layer: TanStack Query manages the server state, handling pagination and caching. It fetches message summaries for the list and full details only when a thread is selected.
State Management Layer: Global UI state (selected folder, search query) is stored in Zustand. Email data is normalized in the Query Cache using
messageId as the key.Routing & Navigation: Nested routes (e.g.,
/inbox/:threadId) allow for direct linking to specific messages and proper browser history handling.Domain Layer
Business Rules / Use Cases: Logic for "Mark as Read", "Flagging", and "Spam Filtering" is encapsulated in the Email Service. This layer handles the transformation of raw API DTOs into structured Thread Aggregates.
Entities / Models:
Email, Thread, Attachment, and Folder are the primary domain models. They are immutable to prevent side effects.Inter-layer Contracts: The Presentation layer interacts with the Domain via hooks (e.g.,
useEmailActions) which wrap the service logic.Infrastructure Layer
API / Network: RESTful endpoints for CRUD. WebSockets provide a "New Email" signal, triggering a cache invalidation in the Application layer.
Storage: IndexedDB caches the last 500 emails for offline access and instant loading. Service Worker intercepts network requests to serve these cached assets.
Wrap Up
Wrap-up
Evaluation: The design balances performance (virtualization) with security (sanitization).
Trade-offs: Using an
iframe for message rendering adds complexity to "Auto-height" calculations but is significantly safer than simple div injection.Optimization: Implement "Prefetching" where the app fetches the content of the next email in the list when the user hovers over a row or finishes reading the current one.