Back to Course|Frontend Engineer Interviews: React, System Design & Web Performance Mastery
Lab

Design a Real-Time Notification System

25 min
Intermediate
3 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.onLine and the online/offline events
  • Queue "mark as read" actions while offline
  • Flush the queue when the connection is restored
  • Persist the queue in localStorage so it survives page refreshes

6. Accessibility (ARIA Live Regions)

  • The bell button must have aria-label with the unread count (e.g., "Notifications, 3 unread")
  • The dropdown must have role="dialog" and aria-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 useReducer for notification state — it handles complex state transitions (add, mark read, bulk read, revert) cleanly
  • Create a custom useWebSocket hook that encapsulates connection, reconnection, and message handling
  • Use useRef to track the current reconnect timeout for cleanup
  • Use Intl.RelativeTimeFormat for human-readable timestamps
  • Group notifications with Array.prototype.reduce or Object.groupBy
  • For the offline queue, create a small class with enqueue, flush, and restore methods
  • 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