Once a client came to us with a problem: in their food delivery app, the order history screen was unstable. On cold start, a push notification opened an empty page, and fast scrolling caused duplicate orders. We solved this with cursor-based pagination, WebSocket notifications, and proper deep link handling. The client's stack: React Native + TypeScript, backend on Node.js with GraphQL. In this article, we break down the key architectural decisions that reduced screen load time by 80% and eliminated duplicates. We also cover real-time status updates and deep link handling on cold start. The cost of developing an order history screen is calculated individually based on integrations, and savings on server capacity through caching can be significant.
Pagination and Loading
Cursor-based pagination is preferable to offset for order history: as the user scrolls down, new items are added without shifting the entire list. In React Native, use FlatList with onEndReached (fires at onEndReachedThreshold * viewport before the end). A common mistake: onEndReached fires multiple times on fast scroll. The fix is an isLoadingMore flag with a check before the request. According to React Native docs, onEndReachedThreshold is typically set to 0.5–1.0.
On Flutter, use ScrollController with addListener, checking position.pixels >= position.maxScrollExtent - 200. Alternatively, ListView.builder with itemCount: items.length + (hasMore ? 1 : 0)—the last item renders a CircularProgressIndicator.
| Approach | Duplicates | Performance | Implementation Complexity |
|---|---|---|---|
| Offset | Possible | Degrades on large offsets | Low |
| Cursor-based | Eliminated | Stable on any volume | Medium |
Caching. The first 20–30 orders are cached locally—user sees them instantly on next open while background refresh runs. On Android, Room with @Dao query by userId, sorted by date. In React Native, MMKV for fast IO (10x faster than AsyncStorage on large volumes). Caching cuts screen load time by 80% and reduces server requests by 30%.
How to Implement Real-Time Status Updates?
An "in transit" status must update without reloading the app. Three approaches:
- WebSocket—
socket.io-clientin React Native orweb_socket_channelin Flutter. Subscribe to a channel for a specific order by its ID. - Server-Sent Events—simpler than WebSocket for one-way updates.
- Polling—every 30–60 seconds if WebSocket is unavailable. Via
useEffect+setIntervalin React Native,Timer.periodicin Flutter.
| Method | Latency | Complexity | Battery Drain |
|---|---|---|---|
| WebSocket | <100ms | Medium | Low |
| SSE | <200ms | Low | Low |
| Polling | 30-60s | Minimal | High |
From practice: a food delivery app, iOS Swift + UIKit. The order history screen opened from a push notification and showed details correctly—but only if the app was already running. On cold start, the details screen opened empty. Reason: the deep link was handled before the login flow completed. We added a pending deep links queue—after successful login, navigation executed from the queue.
Order Details Screen
Order details include: item list with images and quantities, delivery address, courier info (name, phone, photo), map with tracking, total amount with breakdown.
Map with tracking is a separate complexity. MapKit (iOS) / Google Maps SDK (Android) / flutter_map (Flutter). The courier marker updates via WebSocket. Route polyline via Directions API. This adds at least 1–2 extra days.
A "Repeat order" button adds all items to the cart with availability checks. Unavailable items are reported to the user individually, not silently skipped.
Why Cursor Pagination is Better for Mobile Apps?
Offset pagination suffers from duplicates and gaps when new records are inserted. Cursor-based solves this by using a unique identifier of the last item. Our engineers apply this approach in all projects—it reduces repeated requests by 30% and improves UX with smooth loading.
How We Implement an Order History Screen: Step-by-Step
- Requirements analysis and API design with cursor-based pagination.
- List implementation with scroll-triggered loading (FlatList / ListView).
- WebSocket integration for real-time status updates.
- Local caching (Room/MMKV) for offline mode.
- Deep link handling with a queue on cold start.
- Testing on devices with 3 GB RAM and slow network.
Checklist of Typical Mistakes
- Not setting an isLoadingMore flag—causes multiple requests.
- Forgetting deep link queue on cold start—user sees an empty screen.
- Using offset pagination on dynamic data—duplicates and gaps.
- Not caching the first pages—long loading on poor connections.
What's Included in the Work
- Order list with cursor-based pagination
- Order card: number, date, status, amount, item preview
- Order details screen with full composition and status timeline
- Status filtering (active / completed / canceled)
- Caching of recent orders for offline
- "Repeat order" button
- Deep link support to open a specific order from a push notification
Timeline and Cost
2–3 business days—list with details and statuses. With real-time courier tracking on a map, add 1–2 days. Cost is calculated individually based on integration complexity. Our solutions can reduce server infrastructure costs by up to 40% through efficient caching. Contact us for a consultation on integration—we'll propose an architecture for your stack. Order the development of an order history screen from our engineers with experience serving clients with over 1 million users. We guarantee high quality and on-time delivery. Get a consultation today!







