Lab
Design a Real-Time Notification System
25 min
Intermediate3 Free Attempts
Instructions
Objective
Build a <NotificationSystem> React component that receives real-time notifications via WebSocket, displays them in a grouped dropdown panel, and handles offline scenarios gracefully. This is a common frontend system design interview challenge that tests real-time communication, state management, and accessibility.
Requirements
1. Notification Bell with Unread Count
- Display a bell icon button in the header
- Show an unread count badge (e.g., "3") on the bell
- Badge should hide when count is 0
- Animate the badge when a new notification arrives (CSS pulse or scale)
- Bell icon should be a
<button>with proper focus styles
2. Dropdown Notification Panel
- Opens when clicking the bell icon
- Closes when clicking outside, pressing Escape, or clicking the bell again
- Groups notifications by type: "Messages", "System", "Activity"
- Each notification shows: icon, title, description, and relative timestamp (e.g., "2 min ago")
- "Mark all as read" button at the top
- "View all notifications" link at the bottom
- Maximum height with scrollable overflow
3. WebSocket Connection Management
- Connect to a WebSocket endpoint on component mount
- Implement exponential backoff reconnection (1s, 2s, 4s, 8s... up to 30s max)
- Show a connection status indicator (connected/reconnecting/disconnected)
- Clean up the connection on unmount
4. Optimistic Updates
- When user clicks "Mark as read", update the UI immediately
- If the server request fails, revert the notification back to unread
- Show a subtle error toast if the revert happens
5. Offline Queueing
- Detect when the browser goes offline using
navigator.onLineand theonline/offlineevents - Queue "mark as read" actions while offline
- Flush the queue when the connection is restored
- Persist the queue in
localStorageso it survives page refreshes
6. Accessibility (ARIA Live Regions)
- The bell button must have
aria-labelwith the unread count (e.g., "Notifications, 3 unread") - The dropdown must have
role="dialog"andaria-label="Notifications" - New notifications must be announced via an
aria-live="polite"region - All items must be navigable with Arrow keys, selectable with Enter
- Focus must move into the panel when opened and return to the bell when closed
Data Schema
Your component should work with this data shape:
type NotificationType = 'message' | 'system' | 'activity';
interface Notification {
id: string;
type: NotificationType;
title: string;
description: string;
timestamp: string; // ISO date string
isRead: boolean;
actionUrl?: string; // optional link to navigate to
}
interface NotificationGroup {
type: NotificationType;
label: string;
notifications: Notification[];
}
interface WebSocketMessage {
event: 'new_notification' | 'notification_read' | 'bulk_read';
payload: Notification | { ids: string[] };
}
Example Usage
<NotificationSystem
wsUrl="wss://api.example.com/notifications"
userId="user-123"
onNotificationClick={(notification) => router.push(notification.actionUrl)}
/>
Hints
- Use
useReducerfor notification state — it handles complex state transitions (add, mark read, bulk read, revert) cleanly - Create a custom
useWebSockethook that encapsulates connection, reconnection, and message handling - Use
useRefto track the current reconnect timeout for cleanup - Use
Intl.RelativeTimeFormatfor human-readable timestamps - Group notifications with
Array.prototype.reduceorObject.groupBy - For the offline queue, create a small class with
enqueue,flush, andrestoremethods - Use
aria-live="polite"on a visually hidden element to announce new notifications
Grading Rubric
Notification Bell: Bell button with dynamic unread count badge, badge hides at zero, proper button element with focus styles15 points
Dropdown Panel: Opens/closes correctly, groups notifications by type, shows title/description/timestamp, has mark-all-read and close functionality20 points
WebSocket Management: Connects on mount, implements exponential backoff reconnection with max delay, shows connection status, cleans up on unmount20 points
Optimistic Updates: Mark-as-read updates UI immediately, reverts on server failure, shows error feedback on revert15 points
Offline Queueing: Detects offline/online status, queues actions while offline, flushes on reconnect, persists queue in localStorage15 points
Accessibility: Bell has aria-label with count, panel has role=dialog, new notifications announced via aria-live region, keyboard navigation with Arrow/Enter/Escape, focus management15 points
Your Solution
This lab requires TypeScript
TSTypeScript(required)
3 free attempts remaining