Learn the concept
Real-Time System Architecture
A food delivery tracking page uses WebSocket connections for real-time driver location updates on a live map, a state machine to model order lifecycle (placed through delivered), and Server-Sent Events or polling as fallbacks, with careful attention to mobile battery optimization, offline resilience, and animated map transitions.
This is a classic frontend system design question frequently asked at companies like Zomato, Uber Eats, DoorDash, and Swiggy. The challenge combines real-time data handling, map rendering, state management, and mobile optimization into a single cohesive feature.
Before diving into architecture, clarify requirements:
Functional:
Non-functional:
WebSocket is the right choice for driver location updates because the data flow is bidirectional (the client may send viewport bounds to optimize which updates it receives) and high-frequency (every 3-5 seconds).
Server-Sent Events (SSE) works well for order status changes because they are unidirectional and infrequent. SSE is simpler, auto-reconnects, and works through HTTP proxies without special configuration.
A hybrid approach is optimal:
The page is composed of these key components:
Order status should be modeled as a finite state machine to enforce valid transitions and prevent impossible states. Using useReducer with a state machine pattern gives you predictable, testable state transitions:
placed -> confirmed (restaurant accepts)confirmed -> preparing (kitchen starts)preparing -> picked_up (driver collects)picked_up -> on_the_way (driver en route)on_the_way -> delivered (driver arrives)cancelled (cancellation)picked_up -> driver_reassigned -> picked_up (driver swap)Each state transition triggers UI updates: the timeline advances, ETA recalculates, and notifications fire.
Use Mapbox GL JS or Google Maps JavaScript API for the interactive map:
requestAnimationFrame over 3 seconds (matching the update interval) for smooth visual movement.Mobile users may track their order for 20-40 minutes. Aggressive real-time updates drain battery:
document.hidden === true), reduce WebSocket update frequency to every 30 seconds or pause entirely. Resume full frequency when the user returns.requestAnimationFrame callback rather than triggering re-renders for each message.navigator.onLine and the online/offline events.// Types for the order state machine
type OrderStatus =
| 'placed'
| 'confirmed'
| 'preparing'
| 'picked_up'
| 'on_the_way'
| 'delivered'
| 'cancelled';
interface OrderState {
status: OrderStatus;
eta: number | null; // minutes
driverLocation: { lat: number; lng: number } | null;
driver: { name: string; photo: string; phone: string } | null;
statusHistory: Array<{ status: OrderStatus; timestamp: number }>;
error: string | null;
}
type OrderAction =
| { type: 'STATUS_UPDATE'; status: OrderStatus; eta?: number }
| { type: 'LOCATION_UPDATE'; lat: number; lng: number }
| { type: 'DRIVER_ASSIGNED'; driver: OrderState['driver'] }
| { type: 'DRIVER_REASSIGNED'; driver: OrderState['driver']; reason: string }
| { type: 'ETA_UPDATE'; eta: number }
| { type: 'CANCELLED'; reason: string }
| { type: 'ERROR'; message: string };
// Valid transitions — enforced at the reducer level
const VALID_TRANSITIONS: Record<OrderStatus, OrderStatus[]> = {
placed: ['confirmed', 'cancelled'],
confirmed: ['preparing', 'cancelled'],
preparing: ['picked_up', 'cancelled'],
picked_up: ['on_the_way', 'cancelled'],
on_the_way: ['delivered', 'cancelled'],
delivered: [],
cancelled: [],
};
function orderReducer(state: OrderState, action: OrderAction): OrderState {
switch (action.type) {
case 'STATUS_UPDATE': {
const allowed = VALID_TRANSITIONS[state.status];
if (!allowed.includes(action.status)) {
console.warn(`Invalid transition: ${state.status} → ${action.status}`);
return state;
}
return {
...state,
status: action.status,
eta: action.eta ?? state.eta,
statusHistory: [
...state.statusHistory,
{ status: action.status, timestamp: Date.now() },
],
error: null,
};
}
case 'LOCATION_UPDATE':
return {
...state,
driverLocation: { lat: action.lat, lng: action.lng },
};
case 'DRIVER_ASSIGNED':
return { ...state, driver: action.driver };
case 'DRIVER_REASSIGNED':
return {
...state,
driver: action.driver,
error: `Driver reassigned: ${action.reason}`,
};
case 'ETA_UPDATE':
return { ...state, eta: action.eta };
case 'CANCELLED':
return {
...state,
status: 'cancelled',
error: action.reason,
statusHistory: [
...state.statusHistory,
{ status: 'cancelled', timestamp: Date.now() },
],
};
case 'ERROR':
return { ...state, error: action.message };
default:
return state;
}
}
// Usage in component
function useOrderTracking(orderId: string) {
const [state, dispatch] = useReducer(orderReducer, {
status: 'placed',
eta: null,
driverLocation: null,
driver: null,
statusHistory: [{ status: 'placed', timestamp: Date.now() }],
error: null,
});
return { state, dispatch };
}Showing customers a real-time map of their driver's location with animated movement, order status timeline, and dynamic ETA as the driver navigates through traffic
Displaying the assigned driver's approach on a live map with ETA countdown after booking a ride, with smooth marker animation between GPS updates
Showing last-mile delivery progress on a map when the package is out for delivery, with status updates at each checkpoint
Build a food delivery tracking page with a mock WebSocket server that simulates a driver moving along a predefined route, with order status transitions and ETA updates
Create a map component that receives GPS coordinates via WebSocket and smoothly animates a marker between updates using requestAnimationFrame interpolation
Real-time order tracking with live driver location, animated map, and granular order status updates from restaurant to doorstep
Live order tracking with ETA predictions powered by ML models that account for traffic, restaurant prep time, and driver behavior
Real-time delivery tracking with driver location, animated route progress, and status timeline showing each stage of the delivery